2 Copyright (C) 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.text.html;
41 import gnu.javax.swing.text.html.css.BorderWidth;
42 import gnu.javax.swing.text.html.css.CSSColor;
43 import gnu.javax.swing.text.html.css.CSSParser;
44 import gnu.javax.swing.text.html.css.CSSParserCallback;
45 import gnu.javax.swing.text.html.css.FontSize;
46 import gnu.javax.swing.text.html.css.FontStyle;
47 import gnu.javax.swing.text.html.css.FontWeight;
48 import gnu.javax.swing.text.html.css.Length;
49 import gnu.javax.swing.text.html.css.Selector;
51 import java.awt.Color;
53 import java.awt.Graphics;
54 import java.awt.Rectangle;
55 import java.awt.Shape;
56 import java.awt.font.FontRenderContext;
57 import java.awt.geom.Rectangle2D;
58 import java.io.BufferedReader;
59 import java.io.IOException;
60 import java.io.InputStream;
61 import java.io.InputStreamReader;
62 import java.io.Reader;
63 import java.io.Serializable;
64 import java.io.StringReader;
66 import java.util.ArrayList;
67 import java.util.Collections;
68 import java.util.Enumeration;
69 import java.util.HashMap;
70 import java.util.Iterator;
71 import java.util.List;
74 import javax.swing.border.Border;
75 import javax.swing.event.ChangeListener;
76 import javax.swing.text.AttributeSet;
77 import javax.swing.text.Element;
78 import javax.swing.text.MutableAttributeSet;
79 import javax.swing.text.SimpleAttributeSet;
80 import javax.swing.text.Style;
81 import javax.swing.text.StyleConstants;
82 import javax.swing.text.StyleContext;
83 import javax.swing.text.View;
87 * This class adds support for defining the visual characteristics of HTML views
88 * being rendered. This enables views to be customized by a look-and-feel, mulitple
89 * views over the same model can be rendered differently. Each EditorPane has its
90 * own StyleSheet, but by default one sheet will be shared by all of the HTMLEditorKit
91 * instances. An HTMLDocument can also have a StyleSheet, which holds specific CSS
94 * In order for Views to store less state and therefore be more lightweight,
95 * the StyleSheet can act as a factory for painters that handle some of the
96 * rendering tasks. Since the StyleSheet may be used by views over multiple
97 * documents the HTML attributes don't effect the selector being used.
99 * The rules are stored as named styles, and other information is stored to
100 * translate the context of an element to a rule.
102 * @author Lillian Angel (langel@redhat.com)
104 public class StyleSheet extends StyleContext
108 * Parses CSS stylesheets using the parser in gnu/javax/swing/html/css.
110 * This is package private to avoid accessor methods.
112 class CSSStyleSheetParserCallback
113 implements CSSParserCallback
116 * The current styles.
118 private CSSStyle[] styles;
121 * The precedence of the stylesheet to be parsed.
123 private int precedence;
126 * Creates a new CSS parser. This parser parses a CSS stylesheet with
127 * the specified precedence.
129 * @param prec the precedence, according to the constants defined in
132 CSSStyleSheetParserCallback(int prec)
138 * Called at the beginning of a statement.
140 * @param sel the selector
142 public void startStatement(Selector[] sel)
144 styles = new CSSStyle[sel.length];
145 for (int i = 0; i < sel.length; i++)
146 styles[i] = new CSSStyle(precedence, sel[i]);
150 * Called at the end of a statement.
152 public void endStatement()
154 for (int i = 0; i < styles.length; i++)
160 * Called when a declaration is parsed.
162 * @param property the property
163 * @param value the value
165 public void declaration(String property, String value)
167 CSS.Attribute cssAtt = CSS.getAttribute(property);
168 Object val = CSS.getValue(cssAtt, value);
169 for (int i = 0; i < styles.length; i++)
171 CSSStyle style = styles[i];
172 CSS.addInternal(style, cssAtt, value);
174 style.addAttribute(cssAtt, val);
181 * Represents a style that is defined by a CSS rule.
183 private class CSSStyle
184 extends SimpleAttributeSet
185 implements Style, Comparable<CSSStyle>
188 static final int PREC_UA = 0;
189 static final int PREC_NORM = 100000;
190 static final int PREC_AUTHOR_NORMAL = 200000;
191 static final int PREC_AUTHOR_IMPORTANT = 300000;
192 static final int PREC_USER_IMPORTANT = 400000;
195 * The priority of this style when matching CSS selectors.
197 private int precedence;
200 * The selector for this rule.
202 * This is package private to avoid accessor methods.
206 CSSStyle(int prec, Selector sel)
212 public String getName()
214 // TODO: Implement this for correctness.
218 public void addChangeListener(ChangeListener listener)
220 // TODO: Implement this for correctness.
223 public void removeChangeListener(ChangeListener listener)
225 // TODO: Implement this for correctness.
229 * Sorts the rule according to the style's precedence and the
230 * selectors specificity.
232 public int compareTo(CSSStyle other)
234 return other.precedence + other.selector.getSpecificity()
235 - precedence - selector.getSpecificity();
243 /** Base font size (int) */
247 * The linked style sheets stored.
249 private ArrayList<StyleSheet> linked;
252 * Maps element names (selectors) to AttributSet (the corresponding style
255 ArrayList<CSSStyle> css = new ArrayList<CSSStyle>();
258 * Maps selectors to their resolved styles.
260 private HashMap<String,Style> resolvedStyles;
263 * Constructs a StyleSheet.
268 baseFontSize = 4; // Default font size from CSS
269 resolvedStyles = new HashMap<String,Style>();
273 * Gets the style used to render the given tag. The element represents the tag
274 * and can be used to determine the nesting, where the attributes will differ
275 * if there is nesting inside of elements.
277 * @param t - the tag to translate to visual attributes
278 * @param e - the element representing the tag
279 * @return the set of CSS attributes to use to render the tag.
281 public Style getRule(HTML.Tag t, Element e)
283 // Create list of the element and all of its parents, starting
284 // with the bottommost element.
285 ArrayList<Element> path = new ArrayList<Element>();
288 for (el = e; el != null; el = el.getParentElement())
291 // Create fully qualified selector.
292 StringBuilder selector = new StringBuilder();
293 int count = path.size();
294 // We append the actual element after this loop.
295 for (int i = count - 1; i > 0; i--)
298 atts = el.getAttributes();
299 Object name = atts.getAttribute(StyleConstants.NameAttribute);
300 selector.append(name.toString());
301 if (atts.isDefined(HTML.Attribute.ID))
303 selector.append('#');
304 selector.append(atts.getAttribute(HTML.Attribute.ID));
306 if (atts.isDefined(HTML.Attribute.CLASS))
308 selector.append('.');
309 selector.append(atts.getAttribute(HTML.Attribute.CLASS));
311 if (atts.isDefined(HTML.Attribute.DYNAMIC_CLASS))
313 selector.append(':');
314 selector.append(atts.getAttribute(HTML.Attribute.DYNAMIC_CLASS));
316 if (atts.isDefined(HTML.Attribute.PSEUDO_CLASS))
318 selector.append(':');
319 selector.append(atts.getAttribute(HTML.Attribute.PSEUDO_CLASS));
321 selector.append(' ');
323 selector.append(t.toString());
325 atts = el.getAttributes();
326 // For leaf elements, we have to fetch the tag specific attributes.
329 Object o = atts.getAttribute(t);
330 if (o instanceof AttributeSet)
331 atts = (AttributeSet) o;
337 if (atts.isDefined(HTML.Attribute.ID))
339 selector.append('#');
340 selector.append(atts.getAttribute(HTML.Attribute.ID));
342 if (atts.isDefined(HTML.Attribute.CLASS))
344 selector.append('.');
345 selector.append(atts.getAttribute(HTML.Attribute.CLASS));
347 if (atts.isDefined(HTML.Attribute.DYNAMIC_CLASS))
349 selector.append(':');
350 selector.append(atts.getAttribute(HTML.Attribute.DYNAMIC_CLASS));
352 if (atts.isDefined(HTML.Attribute.PSEUDO_CLASS))
354 selector.append(':');
355 selector.append(atts.getAttribute(HTML.Attribute.PSEUDO_CLASS));
358 return getResolvedStyle(selector.toString(), path, t);
362 * Fetches a resolved style. If there is no resolved style for the
363 * specified selector, the resolve the style using
364 * {@link #resolveStyle(String, List, HTML.Tag)}.
366 * @param selector the selector for which to resolve the style
367 * @param path the Element path, used in the resolving algorithm
368 * @param tag the tag for which to resolve
370 * @return the resolved style
372 private Style getResolvedStyle(String selector, List path, HTML.Tag tag)
374 Style style = resolvedStyles.get(selector);
376 style = resolveStyle(selector, path, tag);
381 * Resolves a style. This creates arrays that hold the tag names,
382 * class and id attributes and delegates the work to
383 * {@link #resolveStyle(String, String[], Map[])}.
385 * @param selector the selector
386 * @param path the Element path
389 * @return the resolved style
391 private Style resolveStyle(String selector, List path, HTML.Tag tag)
393 int count = path.size();
394 String[] tags = new String[count];
395 Map[] attributes = new Map[count];
396 for (int i = 0; i < count; i++)
398 Element el = (Element) path.get(i);
399 AttributeSet atts = el.getAttributes();
400 if (i == 0 && el.isLeaf())
402 Object o = atts.getAttribute(tag);
403 if (o instanceof AttributeSet)
404 atts = (AttributeSet) o;
411 (HTML.Tag) atts.getAttribute(StyleConstants.NameAttribute);
413 tags[i] = t.toString();
416 attributes[i] = attributeSetToMap(atts);
421 attributes[i] = null;
424 tags[0] = tag.toString();
425 return resolveStyle(selector, tags, attributes);
429 * Performs style resolving.
431 * @param selector the selector
432 * @param tags the tags
433 * @param attributes the attributes of the tags
435 * @return the resolved style
437 private Style resolveStyle(String selector, String[] tags, Map[] attributes)
439 // FIXME: This style resolver is not correct. But it works good enough for
441 ArrayList<CSSStyle> styles = new ArrayList<CSSStyle>();
442 for (CSSStyle style : css)
444 if (style.selector.matches(tags, attributes))
448 // Add styles from linked stylesheets.
451 for (int i = linked.size() - 1; i >= 0; i--)
453 StyleSheet ss = linked.get(i);
454 for (int j = ss.css.size() - 1; j >= 0; j--)
456 CSSStyle style = ss.css.get(j);
457 if (style.selector.matches(tags, attributes))
464 Collections.sort(styles);
465 Style[] styleArray = new Style[styles.size()];
466 styleArray = (Style[]) styles.toArray(styleArray);
467 Style resolved = new MultiStyle(selector,
468 (Style[]) styles.toArray(styleArray));
469 resolvedStyles.put(selector, resolved);
474 * Gets the rule that best matches the selector. selector is a space
475 * separated String of element names. The attributes of the returned
476 * Style will change as rules are added and removed.
478 * @param selector - the element names separated by spaces
479 * @return the set of CSS attributes to use to render
481 public Style getRule(String selector)
483 CSSStyle best = null;
484 for (Iterator i = css.iterator(); i.hasNext();)
486 CSSStyle style = (CSSStyle) i.next();
487 if (style.compareTo(best) < 0)
494 * Adds a set of rules to the sheet. The rules are expected to be in valid
495 * CSS format. This is called as a result of parsing a <style> tag
497 * @param rule - the rule to add to the sheet
499 public void addRule(String rule)
501 CSSStyleSheetParserCallback cb =
502 new CSSStyleSheetParserCallback(CSSStyle.PREC_AUTHOR_NORMAL);
503 // FIXME: Handle ref.
504 StringReader in = new StringReader(rule);
505 CSSParser parser = new CSSParser(in, cb);
510 catch (IOException ex)
512 // Shouldn't happen. And if, then don't let it bork the outside code.
514 // Clean up resolved styles cache so that the new styles are recognized
515 // on next stylesheet request.
516 resolvedStyles.clear();
520 * Translates a CSS declaration into an AttributeSet. This is called
521 * as a result of encountering an HTML style attribute.
523 * @param decl - the declaration to get
524 * @return the AttributeSet representing the declaration
526 public AttributeSet getDeclaration(String decl)
529 return SimpleAttributeSet.EMPTY;
530 // FIXME: Not implemented.
535 * Loads a set of rules that have been specified in terms of CSS grammar.
536 * If there are any conflicts with existing rules, the new rule is added.
538 * @param in - the stream to read the CSS grammar from.
539 * @param ref - the reference URL. It is the location of the stream, it may
540 * be null. All relative URLs specified in the stream will be based upon this
542 * @throws IOException - For any IO error while reading
544 public void loadRules(Reader in, URL ref)
547 CSSStyleSheetParserCallback cb =
548 new CSSStyleSheetParserCallback(CSSStyle.PREC_UA);
549 // FIXME: Handle ref.
550 CSSParser parser = new CSSParser(in, cb);
555 * Gets a set of attributes to use in the view. This is a set of
556 * attributes that can be used for View.getAttributes
558 * @param v - the view to get the set for
559 * @return the AttributeSet to use in the view.
561 public AttributeSet getViewAttributes(View v)
563 return new ViewAttributeSet(v, this);
567 * Removes a style previously added.
569 * @param nm - the name of the style to remove
571 public void removeStyle(String nm)
573 // FIXME: Not implemented.
574 super.removeStyle(nm);
578 * Adds the rules from ss to those of the receiver. ss's rules will
579 * override the old rules. An added StyleSheet will never override the rules
580 * of the receiving style sheet.
582 * @param ss - the new StyleSheet.
584 public void addStyleSheet(StyleSheet ss)
587 linked = new ArrayList();
592 * Removes ss from those of the receiver
594 * @param ss - the StyleSheet to remove.
596 public void removeStyleSheet(StyleSheet ss)
605 * Returns an array of the linked StyleSheets. May return null.
607 * @return - An array of the linked StyleSheets.
609 public StyleSheet[] getStyleSheets()
611 StyleSheet[] linkedSS;
614 linkedSS = new StyleSheet[linked.size()];
615 linkedSS = linked.toArray(linkedSS);
625 * Imports a style sheet from the url. The rules are directly added to the
626 * receiver. This is usually called when a <link> tag is resolved in an
629 * @param url the URL to import the StyleSheet from
631 public void importStyleSheet(URL url)
635 InputStream in = url.openStream();
636 Reader r = new BufferedReader(new InputStreamReader(in));
637 CSSStyleSheetParserCallback cb =
638 new CSSStyleSheetParserCallback(CSSStyle.PREC_AUTHOR_NORMAL);
639 CSSParser parser = new CSSParser(r, cb);
642 catch (IOException ex)
644 // We can't do anything about it I guess.
649 * Sets the base url. All import statements that are relative, will be
655 public void setBase(URL base)
671 * Adds a CSS attribute to the given set.
673 * @param attr - the attribute set
674 * @param key - the attribute to add
675 * @param value - the value of the key
677 public void addCSSAttribute(MutableAttributeSet attr, CSS.Attribute key,
680 Object val = CSS.getValue(key, value);
681 CSS.addInternal(attr, key, value);
682 attr.addAttribute(key, val);
686 * Adds a CSS attribute to the given set.
687 * This method parses the value argument from HTML based on key.
688 * Returns true if it finds a valid value for the given key,
689 * and false otherwise.
691 * @param attr - the attribute set
692 * @param key - the attribute to add
693 * @param value - the value of the key
694 * @return true if a valid value was found.
696 public boolean addCSSAttributeFromHTML(MutableAttributeSet attr, CSS.Attribute key,
699 // FIXME: Need to parse value from HTML based on key.
700 attr.addAttribute(key, value);
701 return attr.containsAttribute(key, value);
705 * Converts a set of HTML attributes to an equivalent set of CSS attributes.
707 * @param htmlAttrSet - the set containing the HTML attributes.
708 * @return the set of CSS attributes
710 public AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet)
712 AttributeSet cssAttr = htmlAttrSet.copyAttributes();
714 // The HTML align attribute maps directly to the CSS text-align attribute.
715 Object o = htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
717 cssAttr = addAttribute(cssAttr, CSS.Attribute.TEXT_ALIGN, o);
719 // The HTML width attribute maps directly to CSS width.
720 o = htmlAttrSet.getAttribute(HTML.Attribute.WIDTH);
722 cssAttr = addAttribute(cssAttr, CSS.Attribute.WIDTH,
723 new Length(o.toString()));
725 // The HTML height attribute maps directly to CSS height.
726 o = htmlAttrSet.getAttribute(HTML.Attribute.HEIGHT);
728 cssAttr = addAttribute(cssAttr, CSS.Attribute.HEIGHT,
729 new Length(o.toString()));
731 o = htmlAttrSet.getAttribute(HTML.Attribute.NOWRAP);
733 cssAttr = addAttribute(cssAttr, CSS.Attribute.WHITE_SPACE, "nowrap");
735 // Map cellspacing attr of tables to CSS border-spacing.
736 o = htmlAttrSet.getAttribute(HTML.Attribute.CELLSPACING);
738 cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_SPACING,
739 new Length(o.toString()));
741 // For table cells and headers, fetch the cellpadding value from the
742 // parent table and set it as CSS padding attribute.
743 HTML.Tag tag = (HTML.Tag)
744 htmlAttrSet.getAttribute(StyleConstants.NameAttribute);
745 if ((tag == HTML.Tag.TD || tag == HTML.Tag.TH)
746 && htmlAttrSet instanceof Element)
748 Element el = (Element) htmlAttrSet;
749 AttributeSet tableAttrs = el.getParentElement().getParentElement()
751 o = tableAttrs.getAttribute(HTML.Attribute.CELLPADDING);
754 Length l = new Length(o.toString());
755 cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_BOTTOM, l);
756 cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_LEFT, l);
757 cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_RIGHT, l);
758 cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_TOP, l);
760 o = tableAttrs.getAttribute(HTML.Attribute.BORDER);
761 cssAttr = translateBorder(cssAttr, o);
764 // Translate border attribute.
765 o = cssAttr.getAttribute(HTML.Attribute.BORDER);
766 cssAttr = translateBorder(cssAttr, o);
768 // TODO: Add more mappings.
773 * Translates a HTML border attribute to a corresponding set of CSS
776 * @param cssAttr the original set of CSS attributes to add to
777 * @param o the value of the border attribute
779 * @return the new set of CSS attributes
781 private AttributeSet translateBorder(AttributeSet cssAttr, Object o)
785 BorderWidth l = new BorderWidth(o.toString());
786 if (l.getValue() > 0)
788 cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_WIDTH, l);
789 cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_STYLE,
791 cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_COLOR,
792 new CSSColor("black"));
799 * Adds an attribute to the given set and returns a new set. This is implemented
800 * to convert StyleConstants attributes to CSS before forwarding them to the superclass.
801 * The StyleConstants attribute do not have corresponding CSS entry, the attribute
802 * is stored (but will likely not be used).
804 * @param old - the old set
805 * @param key - the non-null attribute key
806 * @param value - the attribute value
807 * @return the updated set
809 public AttributeSet addAttribute(AttributeSet old, Object key,
812 // FIXME: Not implemented.
813 return super.addAttribute(old, key, value);
817 * Adds a set of attributes to the element. If any of these attributes are
818 * StyleConstants, they will be converted to CSS before forwarding to the
821 * @param old - the old set
822 * @param attr - the attributes to add
823 * @return the updated attribute set
825 public AttributeSet addAttributes(AttributeSet old, AttributeSet attr)
827 // FIXME: Not implemented.
828 return super.addAttributes(old, attr);
832 * Removes an attribute from the set. If the attribute is a
833 * StyleConstants, it will be converted to CSS before forwarding to the
836 * @param old - the old set
837 * @param key - the non-null attribute key
838 * @return the updated set
840 public AttributeSet removeAttribute(AttributeSet old, Object key)
842 // FIXME: Not implemented.
843 return super.removeAttribute(old, key);
847 * Removes an attribute from the set. If any of the attributes are
848 * StyleConstants, they will be converted to CSS before forwarding to the
851 * @param old - the old set
852 * @param attrs - the attributes to remove
853 * @return the updated set
855 public AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs)
857 // FIXME: Not implemented.
858 return super.removeAttributes(old, attrs);
862 * Removes a set of attributes for the element. If any of the attributes is a
863 * StyleConstants, they will be converted to CSS before forwarding to the
866 * @param old - the old attribute set
867 * @param names - the attribute names
868 * @return the update attribute set
870 public AttributeSet removeAttributes(AttributeSet old, Enumeration<?> names)
872 // FIXME: Not implemented.
873 return super.removeAttributes(old, names);
877 * Creates a compact set of attributes that might be shared. This is a hook
878 * for subclasses that want to change the behaviour of SmallAttributeSet.
880 * @param a - the set of attributes to be represented in the compact form.
881 * @return the set of attributes created
883 protected StyleContext.SmallAttributeSet createSmallAttributeSet(AttributeSet a)
885 return super.createSmallAttributeSet(a);
889 * Creates a large set of attributes. This set is not shared. This is a hook
890 * for subclasses that want to change the behaviour of the larger attribute
893 * @param a - the set of attributes to be represented in the larger form.
894 * @return the large set of attributes.
896 protected MutableAttributeSet createLargeAttributeSet(AttributeSet a)
898 return super.createLargeAttributeSet(a);
902 * Gets the font to use for the given set.
904 * @param a - the set to get the font for.
905 * @return the font for the set
907 public Font getFont(AttributeSet a)
909 int realSize = getFontSize(a);
911 // Decrement size for subscript and superscript.
912 Object valign = a.getAttribute(CSS.Attribute.VERTICAL_ALIGN);
915 String v = valign.toString();
916 if (v.contains("sup") || v.contains("sub"))
920 // TODO: Convert font family.
921 String family = "SansSerif";
923 int style = Font.PLAIN;
924 FontWeight weight = (FontWeight) a.getAttribute(CSS.Attribute.FONT_WEIGHT);
926 style |= weight.getValue();
927 FontStyle fStyle = (FontStyle) a.getAttribute(CSS.Attribute.FONT_STYLE);
929 style |= fStyle.getValue();
930 return new Font(family, style, realSize);
934 * Determines the EM base value based on the specified attributes.
936 * @param atts the attibutes
938 * @return the EM base value
940 float getEMBase(AttributeSet atts)
942 Font font = getFont(atts);
943 FontRenderContext ctx = new FontRenderContext(null, false, false);
944 Rectangle2D bounds = font.getStringBounds("M", ctx);
945 return (float) bounds.getWidth();
949 * Determines the EX base value based on the specified attributes.
951 * @param atts the attibutes
953 * @return the EX base value
955 float getEXBase(AttributeSet atts)
957 Font font = getFont(atts);
958 FontRenderContext ctx = new FontRenderContext(null, false, false);
959 Rectangle2D bounds = font.getStringBounds("x", ctx);
960 return (float) bounds.getHeight();
964 * Resolves the fontsize for a given set of attributes.
966 * @param atts the attributes
968 * @return the resolved font size
970 private int getFontSize(AttributeSet atts)
973 if (atts.isDefined(CSS.Attribute.FONT_SIZE))
975 FontSize fs = (FontSize) atts.getAttribute(CSS.Attribute.FONT_SIZE);
979 AttributeSet resolver = atts.getResolveParent();
980 if (resolver != null)
981 parSize = getFontSize(resolver);
982 size = fs.getValue(parSize);
986 size = fs.getValue();
991 AttributeSet resolver = atts.getResolveParent();
992 if (resolver != null)
993 size = getFontSize(resolver);
999 * Takes a set of attributes and turns it into a foreground
1000 * color specification. This is used to specify things like, brigher, more hue
1003 * @param a - the set to get the foreground color for
1004 * @return the foreground color for the set
1006 public Color getForeground(AttributeSet a)
1008 CSSColor c = (CSSColor) a.getAttribute(CSS.Attribute.COLOR);
1011 color = c.getValue();
1016 * Takes a set of attributes and turns it into a background
1017 * color specification. This is used to specify things like, brigher, more hue
1020 * @param a - the set to get the background color for
1021 * @return the background color for the set
1023 public Color getBackground(AttributeSet a)
1025 CSSColor c = (CSSColor) a.getAttribute(CSS.Attribute.BACKGROUND_COLOR);
1028 color = c.getValue();
1033 * Gets the box formatter to use for the given set of CSS attributes.
1035 * @param a - the given set
1036 * @return the box formatter
1038 public BoxPainter getBoxPainter(AttributeSet a)
1040 return new BoxPainter(a, this);
1044 * Gets the list formatter to use for the given set of CSS attributes.
1046 * @param a - the given set
1047 * @return the list formatter
1049 public ListPainter getListPainter(AttributeSet a)
1051 return new ListPainter(a, this);
1055 * Sets the base font size between 1 and 7.
1057 * @param sz - the new font size for the base.
1059 public void setBaseFontSize(int sz)
1061 if (sz <= 7 && sz >= 1)
1066 * Sets the base font size from the String. It can either identify
1067 * a specific font size (between 1 and 7) or identify a relative
1068 * font size such as +1 or -2.
1070 * @param size - the new font size as a String.
1072 public void setBaseFontSize(String size)
1078 if (size.length() == 2)
1080 int i = new Integer(size.substring(1)).intValue();
1081 if (size.startsWith("+"))
1082 temp = baseFontSize + i;
1083 else if (size.startsWith("-"))
1084 temp = baseFontSize - i;
1086 else if (size.length() == 1)
1087 temp = new Integer(size.substring(0)).intValue();
1089 if (temp <= 7 && temp >= 1)
1090 baseFontSize = temp;
1092 catch (NumberFormatException nfe)
1104 public static int getIndexOfSize(float pt)
1106 // FIXME: Not implemented.
1111 * Gets the point size, given a size index.
1113 * @param index - the size index
1114 * @return the point size.
1116 public float getPointSize(int index)
1118 // FIXME: Not implemented.
1123 * Given the string of the size, returns the point size value.
1125 * @param size - the string representation of the size.
1126 * @return - the point size value.
1128 public float getPointSize(String size)
1130 // FIXME: Not implemented.
1135 * Convert the color string represenation into java.awt.Color. The valid
1136 * values are like "aqua" , "#00FFFF" or "rgb(1,6,44)".
1138 * @param colorName the color to convert.
1139 * @return the matching java.awt.color
1141 public Color stringToColor(String colorName)
1143 return CSSColor.convertValue(colorName);
1147 * This class carries out some of the duties of CSS formatting. This enables views
1148 * to present the CSS formatting while not knowing how the CSS values are cached.
1150 * This object is reponsible for the insets of a View and making sure
1151 * the background is maintained according to the CSS attributes.
1153 * @author Lillian Angel (langel@redhat.com)
1155 public static class BoxPainter extends Object implements Serializable
1161 private float leftInset;
1166 private float rightInset;
1171 private float topInset;
1176 private float bottomInset;
1179 * The border of the box.
1181 private Border border;
1183 private float leftPadding;
1184 private float rightPadding;
1185 private float topPadding;
1186 private float bottomPadding;
1189 * The background color.
1191 private Color background;
1194 * Package-private constructor.
1196 * @param as - AttributeSet for painter
1198 BoxPainter(AttributeSet as, StyleSheet ss)
1200 float emBase = ss.getEMBase(as);
1201 float exBase = ss.getEXBase(as);
1203 Length l = (Length) as.getAttribute(CSS.Attribute.MARGIN_LEFT);
1206 l.setFontBases(emBase, exBase);
1207 leftInset = l.getValue();
1209 l = (Length) as.getAttribute(CSS.Attribute.MARGIN_RIGHT);
1212 l.setFontBases(emBase, exBase);
1213 rightInset = l.getValue();
1215 l = (Length) as.getAttribute(CSS.Attribute.MARGIN_TOP);
1218 l.setFontBases(emBase, exBase);
1219 topInset = l.getValue();
1221 l = (Length) as.getAttribute(CSS.Attribute.MARGIN_BOTTOM);
1224 l.setFontBases(emBase, exBase);
1225 bottomInset = l.getValue();
1229 l = (Length) as.getAttribute(CSS.Attribute.PADDING_LEFT);
1232 l.setFontBases(emBase, exBase);
1233 leftPadding = l.getValue();
1235 l = (Length) as.getAttribute(CSS.Attribute.PADDING_RIGHT);
1238 l.setFontBases(emBase, exBase);
1239 rightPadding = l.getValue();
1241 l = (Length) as.getAttribute(CSS.Attribute.PADDING_TOP);
1244 l.setFontBases(emBase, exBase);
1245 topPadding = l.getValue();
1247 l = (Length) as.getAttribute(CSS.Attribute.PADDING_BOTTOM);
1250 l.setFontBases(emBase, exBase);
1251 bottomPadding = l.getValue();
1254 // Determine border.
1255 border = new CSSBorder(as, ss);
1257 // Determine background.
1258 background = ss.getBackground(as);
1264 * Gets the inset needed on a given side to account for the margin, border
1267 * @param size - the size of the box to get the inset for. View.TOP, View.LEFT,
1268 * View.BOTTOM or View.RIGHT.
1269 * @param v - the view making the request. This is used to get the AttributeSet,
1270 * amd may be used to resolve percentage arguments.
1272 * @throws IllegalArgumentException - for an invalid direction.
1274 public float getInset(int size, View v)
1282 inset += border.getBorderInsets(null).top;
1283 inset += topPadding;
1286 inset = bottomInset;
1288 inset += border.getBorderInsets(null).bottom;
1289 inset += bottomPadding;
1294 inset += border.getBorderInsets(null).left;
1295 inset += leftPadding;
1300 inset += border.getBorderInsets(null).right;
1301 inset += rightPadding;
1310 * Paints the CSS box according to the attributes given. This should
1311 * paint the border, padding and background.
1313 * @param g - the graphics configuration
1314 * @param x - the x coordinate
1315 * @param y - the y coordinate
1316 * @param w - the width of the allocated area
1317 * @param h - the height of the allocated area
1318 * @param v - the view making the request
1320 public void paint(Graphics g, float x, float y, float w, float h, View v)
1322 int inX = (int) (x + leftInset);
1323 int inY = (int) (y + topInset);
1324 int inW = (int) (w - leftInset - rightInset);
1325 int inH = (int) (h - topInset - bottomInset);
1326 if (background != null)
1328 g.setColor(background);
1329 g.fillRect(inX, inY, inW, inH);
1333 border.paintBorder(null, g, inX, inY, inW, inH);
1339 * This class carries out some of the CSS list formatting duties. Implementations
1340 * of this class enable views to present the CSS formatting while not knowing anything
1341 * about how the CSS values are being cached.
1343 * @author Lillian Angel (langel@redhat.com)
1345 public static class ListPainter implements Serializable
1349 * Attribute set for painter
1351 private AttributeSet attributes;
1354 * The associated style sheet.
1356 private StyleSheet styleSheet;
1361 private String type;
1364 * Package-private constructor.
1366 * @param as - AttributeSet for painter
1368 ListPainter(AttributeSet as, StyleSheet ss)
1372 type = (String) as.getAttribute(CSS.Attribute.LIST_STYLE_TYPE);
1376 * Cached rectangle re-used in the paint method below.
1378 private final Rectangle tmpRect = new Rectangle();
1381 * Paints the CSS list decoration according to the attributes given.
1383 * @param g - the graphics configuration
1384 * @param x - the x coordinate
1385 * @param y - the y coordinate
1386 * @param w - the width of the allocated area
1387 * @param h - the height of the allocated area
1388 * @param v - the view making the request
1389 * @param item - the list item to be painted >=0.
1391 public void paint(Graphics g, float x, float y, float w, float h, View v,
1394 // FIXME: This is a very simplistic list rendering. We still need
1395 // to implement different bullet types (see type field) and custom
1396 // bullets via images.
1397 View itemView = v.getView(item);
1398 AttributeSet viewAtts = itemView.getAttributes();
1399 Object tag = viewAtts.getAttribute(StyleConstants.NameAttribute);
1400 // Only paint something here when the child view is an LI tag
1401 // and the calling view is some of the list tags then).
1402 if (tag != null && tag == HTML.Tag.LI)
1404 g.setColor(Color.BLACK);
1405 int centerX = (int) (x - 12);
1407 // For paragraphs (almost all cases) center bullet vertically
1408 // in the middle of the first line.
1409 tmpRect.setBounds((int) x, (int) y, (int) w, (int) h);
1410 if (itemView.getViewCount() > 0)
1412 View v1 = itemView.getView(0);
1413 if (v1 instanceof ParagraphView && v1.getViewCount() > 0)
1415 Shape a1 = itemView.getChildAllocation(0, tmpRect);
1416 Rectangle r1 = a1 instanceof Rectangle ? (Rectangle) a1
1418 ParagraphView par = (ParagraphView) v1;
1419 Shape a = par.getChildAllocation(0, r1);
1422 Rectangle r = a instanceof Rectangle ? (Rectangle) a
1424 centerY = (int) (r.height / 2 + r.y);
1430 centerY =(int) (h / 2 + y);
1432 g.fillOval(centerX - 3, centerY - 3, 6, 6);
1438 * Converts an AttributeSet to a Map. This is used for CSS resolving.
1440 * @param atts the attributes to convert
1442 * @return the converted map
1444 private Map attributeSetToMap(AttributeSet atts)
1446 HashMap<String,String> map = new HashMap<String,String>();
1447 Enumeration<?> keys = atts.getAttributeNames();
1448 while (keys.hasMoreElements())
1450 Object key = keys.nextElement();
1451 Object value = atts.getAttribute(key);
1452 map.put(key.toString(), value.toString());