0001 /*
0002 * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025 package javax.swing.text.html;
0026
0027 import java.awt.Color;
0028 import java.awt.Font;
0029 import java.awt.GraphicsEnvironment;
0030 import java.awt.Toolkit;
0031 import java.awt.HeadlessException;
0032 import java.awt.Image;
0033 import java.io.*;
0034 import java.lang.reflect.Method;
0035 import java.net.URL;
0036 import java.net.MalformedURLException;
0037 import java.util.Enumeration;
0038 import java.util.Hashtable;
0039 import java.util.Vector;
0040 import java.util.Locale;
0041 import javax.swing.ImageIcon;
0042 import javax.swing.SizeRequirements;
0043 import javax.swing.text.*;
0044
0045 /**
0046 * Defines a set of
0047 * <a href="http://www.w3.org/TR/REC-CSS1">CSS attributes</a>
0048 * as a typesafe enumeration. The HTML View implementations use
0049 * CSS attributes to determine how they will render. This also defines
0050 * methods to map between CSS/HTML/StyleConstants. Any shorthand
0051 * properties, such as font, are mapped to the intrinsic properties.
0052 * <p>The following describes the CSS properties that are suppored by the
0053 * rendering engine:
0054 * <ul><li>font-family
0055 * <li>font-style
0056 * <li>font-size (supports relative units)
0057 * <li>font-weight
0058 * <li>font
0059 * <li>color
0060 * <li>background-color (with the exception of transparent)
0061 * <li>background-image
0062 * <li>background-repeat
0063 * <li>background-position
0064 * <li>background
0065 * <li>background-repeat
0066 * <li>text-decoration (with the exception of blink and overline)
0067 * <li>vertical-align (only sup and super)
0068 * <li>text-align (justify is treated as center)
0069 * <li>margin-top
0070 * <li>margin-right
0071 * <li>margin-bottom
0072 * <li>margin-left
0073 * <li>margin
0074 * <li>padding-top
0075 * <li>padding-right
0076 * <li>padding-bottom
0077 * <li>padding-left
0078 * <li>border-style (only supports inset, outset and none)
0079 * <li>list-style-type
0080 * <li>list-style-position
0081 * </ul>
0082 * The following are modeled, but currently not rendered.
0083 * <ul><li>font-variant
0084 * <li>background-attachment (background always treated as scroll)
0085 * <li>word-spacing
0086 * <li>letter-spacing
0087 * <li>text-indent
0088 * <li>text-transform
0089 * <li>line-height
0090 * <li>border-top-width (this is used to indicate if a border should be used)
0091 * <li>border-right-width
0092 * <li>border-bottom-width
0093 * <li>border-left-width
0094 * <li>border-width
0095 * <li>border-top
0096 * <li>border-right
0097 * <li>border-bottom
0098 * <li>border-left
0099 * <li>border
0100 * <li>width
0101 * <li>height
0102 * <li>float
0103 * <li>clear
0104 * <li>display
0105 * <li>white-space
0106 * <li>list-style
0107 * </ul>
0108 * <p><b>Note: for the time being we do not fully support relative units,
0109 * unless noted, so that
0110 * p { margin-top: 10% } will be treated as if no margin-top was specified.
0111 *
0112 * @author Timothy Prinzing
0113 * @author Scott Violet
0114 * @version 1.74 05/05/07
0115 * @see StyleSheet
0116 */
0117 public class CSS implements Serializable {
0118
0119 /**
0120 * Definitions to be used as a key on AttributeSet's
0121 * that might hold CSS attributes. Since this is a
0122 * closed set (i.e. defined exactly by the specification),
0123 * it is final and cannot be extended.
0124 */
0125 public static final class Attribute {
0126
0127 private Attribute(String name, String defaultValue,
0128 boolean inherited) {
0129 this .name = name;
0130 this .defaultValue = defaultValue;
0131 this .inherited = inherited;
0132 }
0133
0134 /**
0135 * The string representation of the attribute. This
0136 * should exactly match the string specified in the
0137 * CSS specification.
0138 */
0139 public String toString() {
0140 return name;
0141 }
0142
0143 /**
0144 * Fetch the default value for the attribute.
0145 * If there is no default value (such as for
0146 * composite attributes), null will be returned.
0147 */
0148 public String getDefaultValue() {
0149 return defaultValue;
0150 }
0151
0152 /**
0153 * Indicates if the attribute should be inherited
0154 * from the parent or not.
0155 */
0156 public boolean isInherited() {
0157 return inherited;
0158 }
0159
0160 private String name;
0161 private String defaultValue;
0162 private boolean inherited;
0163
0164 public static final Attribute BACKGROUND = new Attribute(
0165 "background", null, false);
0166
0167 public static final Attribute BACKGROUND_ATTACHMENT = new Attribute(
0168 "background-attachment", "scroll", false);
0169
0170 public static final Attribute BACKGROUND_COLOR = new Attribute(
0171 "background-color", "transparent", false);
0172
0173 public static final Attribute BACKGROUND_IMAGE = new Attribute(
0174 "background-image", "none", false);
0175
0176 public static final Attribute BACKGROUND_POSITION = new Attribute(
0177 "background-position", null, false);
0178
0179 public static final Attribute BACKGROUND_REPEAT = new Attribute(
0180 "background-repeat", "repeat", false);
0181
0182 public static final Attribute BORDER = new Attribute("border",
0183 null, false);
0184
0185 public static final Attribute BORDER_BOTTOM = new Attribute(
0186 "border-bottom", null, false);
0187
0188 public static final Attribute BORDER_BOTTOM_WIDTH = new Attribute(
0189 "border-bottom-width", "medium", false);
0190
0191 public static final Attribute BORDER_COLOR = new Attribute(
0192 "border-color", "black", false);
0193
0194 public static final Attribute BORDER_LEFT = new Attribute(
0195 "border-left", null, false);
0196
0197 public static final Attribute BORDER_LEFT_WIDTH = new Attribute(
0198 "border-left-width", "medium", false);
0199
0200 public static final Attribute BORDER_RIGHT = new Attribute(
0201 "border-right", null, false);
0202
0203 public static final Attribute BORDER_RIGHT_WIDTH = new Attribute(
0204 "border-right-width", "medium", false);
0205
0206 public static final Attribute BORDER_STYLE = new Attribute(
0207 "border-style", "none", false);
0208
0209 public static final Attribute BORDER_TOP = new Attribute(
0210 "border-top", null, false);
0211
0212 public static final Attribute BORDER_TOP_WIDTH = new Attribute(
0213 "border-top-width", "medium", false);
0214
0215 public static final Attribute BORDER_WIDTH = new Attribute(
0216 "border-width", "medium", false);
0217
0218 public static final Attribute CLEAR = new Attribute("clear",
0219 "none", false);
0220
0221 public static final Attribute COLOR = new Attribute("color",
0222 "black", true);
0223
0224 public static final Attribute DISPLAY = new Attribute(
0225 "display", "block", false);
0226
0227 public static final Attribute FLOAT = new Attribute("float",
0228 "none", false);
0229
0230 public static final Attribute FONT = new Attribute("font",
0231 null, true);
0232
0233 public static final Attribute FONT_FAMILY = new Attribute(
0234 "font-family", null, true);
0235
0236 public static final Attribute FONT_SIZE = new Attribute(
0237 "font-size", "medium", true);
0238
0239 public static final Attribute FONT_STYLE = new Attribute(
0240 "font-style", "normal", true);
0241
0242 public static final Attribute FONT_VARIANT = new Attribute(
0243 "font-variant", "normal", true);
0244
0245 public static final Attribute FONT_WEIGHT = new Attribute(
0246 "font-weight", "normal", true);
0247
0248 public static final Attribute HEIGHT = new Attribute("height",
0249 "auto", false);
0250
0251 public static final Attribute LETTER_SPACING = new Attribute(
0252 "letter-spacing", "normal", true);
0253
0254 public static final Attribute LINE_HEIGHT = new Attribute(
0255 "line-height", "normal", true);
0256
0257 public static final Attribute LIST_STYLE = new Attribute(
0258 "list-style", null, true);
0259
0260 public static final Attribute LIST_STYLE_IMAGE = new Attribute(
0261 "list-style-image", "none", true);
0262
0263 public static final Attribute LIST_STYLE_POSITION = new Attribute(
0264 "list-style-position", "outside", true);
0265
0266 public static final Attribute LIST_STYLE_TYPE = new Attribute(
0267 "list-style-type", "disc", true);
0268
0269 public static final Attribute MARGIN = new Attribute("margin",
0270 null, false);
0271
0272 public static final Attribute MARGIN_BOTTOM = new Attribute(
0273 "margin-bottom", "0", false);
0274
0275 public static final Attribute MARGIN_LEFT = new Attribute(
0276 "margin-left", "0", false);
0277
0278 public static final Attribute MARGIN_RIGHT = new Attribute(
0279 "margin-right", "0", false);
0280
0281 /*
0282 * made up css attributes to describe orientation depended
0283 * margins. used for <dir>, <menu>, <ul> etc. see
0284 * 5088268 for more details
0285 */
0286 static final Attribute MARGIN_LEFT_LTR = new Attribute(
0287 "margin-left-ltr", Integer.toString(Integer.MIN_VALUE),
0288 false);
0289
0290 static final Attribute MARGIN_LEFT_RTL = new Attribute(
0291 "margin-left-rtl", Integer.toString(Integer.MIN_VALUE),
0292 false);
0293
0294 static final Attribute MARGIN_RIGHT_LTR = new Attribute(
0295 "margin-right-ltr",
0296 Integer.toString(Integer.MIN_VALUE), false);
0297
0298 static final Attribute MARGIN_RIGHT_RTL = new Attribute(
0299 "margin-right-rtl",
0300 Integer.toString(Integer.MIN_VALUE), false);
0301
0302 public static final Attribute MARGIN_TOP = new Attribute(
0303 "margin-top", "0", false);
0304
0305 public static final Attribute PADDING = new Attribute(
0306 "padding", null, false);
0307
0308 public static final Attribute PADDING_BOTTOM = new Attribute(
0309 "padding-bottom", "0", false);
0310
0311 public static final Attribute PADDING_LEFT = new Attribute(
0312 "padding-left", "0", false);
0313
0314 public static final Attribute PADDING_RIGHT = new Attribute(
0315 "padding-right", "0", false);
0316
0317 public static final Attribute PADDING_TOP = new Attribute(
0318 "padding-top", "0", false);
0319
0320 public static final Attribute TEXT_ALIGN = new Attribute(
0321 "text-align", null, true);
0322
0323 public static final Attribute TEXT_DECORATION = new Attribute(
0324 "text-decoration", "none", true);
0325
0326 public static final Attribute TEXT_INDENT = new Attribute(
0327 "text-indent", "0", true);
0328
0329 public static final Attribute TEXT_TRANSFORM = new Attribute(
0330 "text-transform", "none", true);
0331
0332 public static final Attribute VERTICAL_ALIGN = new Attribute(
0333 "vertical-align", "baseline", false);
0334
0335 public static final Attribute WORD_SPACING = new Attribute(
0336 "word-spacing", "normal", true);
0337
0338 public static final Attribute WHITE_SPACE = new Attribute(
0339 "white-space", "normal", true);
0340
0341 public static final Attribute WIDTH = new Attribute("width",
0342 "auto", false);
0343
0344 /*public*/static final Attribute BORDER_SPACING = new Attribute(
0345 "border-spacing", "0", true);
0346
0347 /*public*/static final Attribute CAPTION_SIDE = new Attribute(
0348 "caption-side", "left", true);
0349
0350 // All possible CSS attribute keys.
0351 static final Attribute[] allAttributes = { BACKGROUND,
0352 BACKGROUND_ATTACHMENT, BACKGROUND_COLOR,
0353 BACKGROUND_IMAGE, BACKGROUND_POSITION,
0354 BACKGROUND_REPEAT, BORDER, BORDER_BOTTOM,
0355 BORDER_BOTTOM_WIDTH, BORDER_COLOR, BORDER_LEFT,
0356 BORDER_LEFT_WIDTH, BORDER_RIGHT, BORDER_RIGHT_WIDTH,
0357 BORDER_STYLE, BORDER_TOP, BORDER_TOP_WIDTH,
0358 BORDER_WIDTH, CLEAR, COLOR, DISPLAY, FLOAT, FONT,
0359 FONT_FAMILY, FONT_SIZE, FONT_STYLE, FONT_VARIANT,
0360 FONT_WEIGHT, HEIGHT, LETTER_SPACING, LINE_HEIGHT,
0361 LIST_STYLE, LIST_STYLE_IMAGE, LIST_STYLE_POSITION,
0362 LIST_STYLE_TYPE, MARGIN, MARGIN_BOTTOM, MARGIN_LEFT,
0363 MARGIN_RIGHT, MARGIN_TOP, PADDING, PADDING_BOTTOM,
0364 PADDING_LEFT, PADDING_RIGHT, PADDING_TOP, TEXT_ALIGN,
0365 TEXT_DECORATION, TEXT_INDENT, TEXT_TRANSFORM,
0366 VERTICAL_ALIGN, WORD_SPACING, WHITE_SPACE, WIDTH,
0367 BORDER_SPACING, CAPTION_SIDE, MARGIN_LEFT_LTR,
0368 MARGIN_LEFT_RTL, MARGIN_RIGHT_LTR, MARGIN_RIGHT_RTL };
0369
0370 private static final Attribute[] ALL_MARGINS = { MARGIN_TOP,
0371 MARGIN_RIGHT, MARGIN_BOTTOM, MARGIN_LEFT };
0372 private static final Attribute[] ALL_PADDING = { PADDING_TOP,
0373 PADDING_RIGHT, PADDING_BOTTOM, PADDING_LEFT };
0374 private static final Attribute[] ALL_BORDER_WIDTHS = {
0375 BORDER_TOP_WIDTH, BORDER_RIGHT_WIDTH,
0376 BORDER_BOTTOM_WIDTH, BORDER_LEFT_WIDTH };
0377
0378 }
0379
0380 static final class Value {
0381
0382 private Value(String name) {
0383 this .name = name;
0384 }
0385
0386 /**
0387 * The string representation of the attribute. This
0388 * should exactly match the string specified in the
0389 * CSS specification.
0390 */
0391 public String toString() {
0392 return name;
0393 }
0394
0395 static final Value INHERITED = new Value("inherited");
0396 static final Value NONE = new Value("none");
0397 static final Value DOTTED = new Value("dotted");
0398 static final Value DASHED = new Value("dashed");
0399 static final Value SOLID = new Value("solid");
0400 static final Value DOUBLE = new Value("double");
0401 static final Value GROOVE = new Value("groove");
0402 static final Value RIDGE = new Value("ridge");
0403 static final Value INSET = new Value("inset");
0404 static final Value OUTSET = new Value("outset");
0405 // Lists.
0406 static final Value BLANK_LIST_ITEM = new Value("none");
0407 static final Value DISC = new Value("disc");
0408 static final Value CIRCLE = new Value("circle");
0409 static final Value SQUARE = new Value("square");
0410 static final Value DECIMAL = new Value("decimal");
0411 static final Value LOWER_ROMAN = new Value("lower-roman");
0412 static final Value UPPER_ROMAN = new Value("upper-roman");
0413 static final Value LOWER_ALPHA = new Value("lower-alpha");
0414 static final Value UPPER_ALPHA = new Value("upper-alpha");
0415 // background-repeat
0416 static final Value BACKGROUND_NO_REPEAT = new Value("no-repeat");
0417 static final Value BACKGROUND_REPEAT = new Value("repeat");
0418 static final Value BACKGROUND_REPEAT_X = new Value("repeat-x");
0419 static final Value BACKGROUND_REPEAT_Y = new Value("repeat-y");
0420 // background-attachment
0421 static final Value BACKGROUND_SCROLL = new Value("scroll");
0422 static final Value BACKGROUND_FIXED = new Value("fixed");
0423
0424 private String name;
0425
0426 static final Value[] allValues = { INHERITED, NONE, DOTTED,
0427 DASHED, SOLID, DOUBLE, GROOVE, RIDGE, INSET, OUTSET,
0428 DISC, CIRCLE, SQUARE, DECIMAL, LOWER_ROMAN,
0429 UPPER_ROMAN, LOWER_ALPHA, UPPER_ALPHA, BLANK_LIST_ITEM,
0430 BACKGROUND_NO_REPEAT, BACKGROUND_REPEAT,
0431 BACKGROUND_REPEAT_X, BACKGROUND_REPEAT_Y,
0432 BACKGROUND_FIXED, BACKGROUND_FIXED };
0433 }
0434
0435 public CSS() {
0436 baseFontSize = baseFontSizeIndex + 1;
0437 // setup the css conversion table
0438 valueConvertor = new Hashtable();
0439 valueConvertor.put(CSS.Attribute.FONT_SIZE, new FontSize());
0440 valueConvertor.put(CSS.Attribute.FONT_FAMILY, new FontFamily());
0441 valueConvertor.put(CSS.Attribute.FONT_WEIGHT, new FontWeight());
0442 valueConvertor.put(CSS.Attribute.BORDER_STYLE,
0443 new BorderStyle());
0444 Object cv = new ColorValue();
0445 valueConvertor.put(CSS.Attribute.COLOR, cv);
0446 valueConvertor.put(CSS.Attribute.BACKGROUND_COLOR, cv);
0447 valueConvertor.put(CSS.Attribute.BORDER_COLOR, cv);
0448 Object lv = new LengthValue();
0449 valueConvertor.put(CSS.Attribute.MARGIN_TOP, lv);
0450 valueConvertor.put(CSS.Attribute.MARGIN_BOTTOM, lv);
0451 valueConvertor.put(CSS.Attribute.MARGIN_LEFT, lv);
0452 valueConvertor.put(CSS.Attribute.MARGIN_LEFT_LTR, lv);
0453 valueConvertor.put(CSS.Attribute.MARGIN_LEFT_RTL, lv);
0454 valueConvertor.put(CSS.Attribute.MARGIN_RIGHT, lv);
0455 valueConvertor.put(CSS.Attribute.MARGIN_RIGHT_LTR, lv);
0456 valueConvertor.put(CSS.Attribute.MARGIN_RIGHT_RTL, lv);
0457 valueConvertor.put(CSS.Attribute.PADDING_TOP, lv);
0458 valueConvertor.put(CSS.Attribute.PADDING_BOTTOM, lv);
0459 valueConvertor.put(CSS.Attribute.PADDING_LEFT, lv);
0460 valueConvertor.put(CSS.Attribute.PADDING_RIGHT, lv);
0461 Object bv = new BorderWidthValue(null, 0);
0462 valueConvertor.put(CSS.Attribute.BORDER_WIDTH, lv);
0463 valueConvertor.put(CSS.Attribute.BORDER_TOP_WIDTH, bv);
0464 valueConvertor.put(CSS.Attribute.BORDER_BOTTOM_WIDTH, bv);
0465 valueConvertor.put(CSS.Attribute.BORDER_LEFT_WIDTH, bv);
0466 valueConvertor.put(CSS.Attribute.BORDER_RIGHT_WIDTH, bv);
0467 Object nlv = new LengthValue(true);
0468 valueConvertor.put(CSS.Attribute.TEXT_INDENT, nlv);
0469 valueConvertor.put(CSS.Attribute.WIDTH, lv);
0470 valueConvertor.put(CSS.Attribute.HEIGHT, lv);
0471 valueConvertor.put(CSS.Attribute.BORDER_SPACING, lv);
0472 Object sv = new StringValue();
0473 valueConvertor.put(CSS.Attribute.FONT_STYLE, sv);
0474 valueConvertor.put(CSS.Attribute.TEXT_DECORATION, sv);
0475 valueConvertor.put(CSS.Attribute.TEXT_ALIGN, sv);
0476 valueConvertor.put(CSS.Attribute.VERTICAL_ALIGN, sv);
0477 Object valueMapper = new CssValueMapper();
0478 valueConvertor.put(CSS.Attribute.LIST_STYLE_TYPE, valueMapper);
0479 valueConvertor.put(CSS.Attribute.BACKGROUND_IMAGE,
0480 new BackgroundImage());
0481 valueConvertor.put(CSS.Attribute.BACKGROUND_POSITION,
0482 new BackgroundPosition());
0483 valueConvertor
0484 .put(CSS.Attribute.BACKGROUND_REPEAT, valueMapper);
0485 valueConvertor.put(CSS.Attribute.BACKGROUND_ATTACHMENT,
0486 valueMapper);
0487 Object generic = new CssValue();
0488 int n = CSS.Attribute.allAttributes.length;
0489 for (int i = 0; i < n; i++) {
0490 CSS.Attribute key = CSS.Attribute.allAttributes[i];
0491 if (valueConvertor.get(key) == null) {
0492 valueConvertor.put(key, generic);
0493 }
0494 }
0495 }
0496
0497 /**
0498 * Sets the base font size. <code>sz</code> is a CSS value, and is
0499 * not necessarily the point size. Use getPointSize to determine the
0500 * point size corresponding to <code>sz</code>.
0501 */
0502 void setBaseFontSize(int sz) {
0503 if (sz < 1)
0504 baseFontSize = 0;
0505 else if (sz > 7)
0506 baseFontSize = 7;
0507 else
0508 baseFontSize = sz;
0509 }
0510
0511 /**
0512 * Sets the base font size from the passed in string.
0513 */
0514 void setBaseFontSize(String size) {
0515 int relSize, absSize, diff;
0516
0517 if (size != null) {
0518 if (size.startsWith("+")) {
0519 relSize = Integer.valueOf(size.substring(1)).intValue();
0520 setBaseFontSize(baseFontSize + relSize);
0521 } else if (size.startsWith("-")) {
0522 relSize = -Integer.valueOf(size.substring(1))
0523 .intValue();
0524 setBaseFontSize(baseFontSize + relSize);
0525 } else {
0526 setBaseFontSize(Integer.valueOf(size).intValue());
0527 }
0528 }
0529 }
0530
0531 /**
0532 * Returns the base font size.
0533 */
0534 int getBaseFontSize() {
0535 return baseFontSize;
0536 }
0537
0538 /**
0539 * Parses the CSS property <code>key</code> with value
0540 * <code>value</code> placing the result in <code>att</code>.
0541 */
0542 void addInternalCSSValue(MutableAttributeSet attr,
0543 CSS.Attribute key, String value) {
0544 if (key == CSS.Attribute.FONT) {
0545 ShorthandFontParser.parseShorthandFont(this , value, attr);
0546 } else if (key == CSS.Attribute.BACKGROUND) {
0547 ShorthandBackgroundParser.parseShorthandBackground(this ,
0548 value, attr);
0549 } else if (key == CSS.Attribute.MARGIN) {
0550 ShorthandMarginParser.parseShorthandMargin(this , value,
0551 attr, CSS.Attribute.ALL_MARGINS);
0552 } else if (key == CSS.Attribute.PADDING) {
0553 ShorthandMarginParser.parseShorthandMargin(this , value,
0554 attr, CSS.Attribute.ALL_PADDING);
0555 } else if (key == CSS.Attribute.BORDER_WIDTH) {
0556 ShorthandMarginParser.parseShorthandMargin(this , value,
0557 attr, CSS.Attribute.ALL_BORDER_WIDTHS);
0558 } else {
0559 Object iValue = getInternalCSSValue(key, value);
0560 if (iValue != null) {
0561 attr.addAttribute(key, iValue);
0562 }
0563 }
0564 }
0565
0566 /**
0567 * Gets the internal CSS representation of <code>value</code> which is
0568 * a CSS value of the CSS attribute named <code>key</code>. The receiver
0569 * should not modify <code>value</code>, and the first <code>count</code>
0570 * strings are valid.
0571 */
0572 Object getInternalCSSValue(CSS.Attribute key, String value) {
0573 CssValue conv = (CssValue) valueConvertor.get(key);
0574 Object r = conv.parseCssValue(value);
0575 return r != null ? r : conv
0576 .parseCssValue(key.getDefaultValue());
0577 }
0578
0579 /**
0580 * Maps from a StyleConstants to a CSS Attribute.
0581 */
0582 Attribute styleConstantsKeyToCSSKey(StyleConstants sc) {
0583 return (Attribute) styleConstantToCssMap.get(sc);
0584 }
0585
0586 /**
0587 * Maps from a StyleConstants value to a CSS value.
0588 */
0589 Object styleConstantsValueToCSSValue(StyleConstants sc,
0590 Object styleValue) {
0591 Object cssKey = styleConstantsKeyToCSSKey(sc);
0592 if (cssKey != null) {
0593 CssValue conv = (CssValue) valueConvertor.get(cssKey);
0594 return conv.fromStyleConstants(sc, styleValue);
0595 }
0596 return null;
0597 }
0598
0599 /**
0600 * Converts the passed in CSS value to a StyleConstants value.
0601 * <code>key</code> identifies the CSS attribute being mapped.
0602 */
0603 Object cssValueToStyleConstantsValue(StyleConstants key,
0604 Object value) {
0605 if (value instanceof CssValue) {
0606 return ((CssValue) value).toStyleConstants(
0607 (StyleConstants) key, null);
0608 }
0609 return null;
0610 }
0611
0612 /**
0613 * Returns the font for the values in the passed in AttributeSet.
0614 * It is assumed the keys will be CSS.Attribute keys.
0615 * <code>sc</code> is the StyleContext that will be messaged to get
0616 * the font once the size, name and style have been determined.
0617 */
0618 Font getFont(StyleContext sc, AttributeSet a, int defaultSize,
0619 StyleSheet ss) {
0620 ss = getStyleSheet(ss);
0621 int size = getFontSize(a, defaultSize, ss);
0622
0623 /*
0624 * If the vertical alignment is set to either superscirpt or
0625 * subscript we reduce the font size by 2 points.
0626 */
0627 StringValue vAlignV = (StringValue) a
0628 .getAttribute(CSS.Attribute.VERTICAL_ALIGN);
0629 if ((vAlignV != null)) {
0630 String vAlign = vAlignV.toString();
0631 if ((vAlign.indexOf("sup") >= 0)
0632 || (vAlign.indexOf("sub") >= 0)) {
0633 size -= 2;
0634 }
0635 }
0636
0637 FontFamily familyValue = (FontFamily) a
0638 .getAttribute(CSS.Attribute.FONT_FAMILY);
0639 String family = (familyValue != null) ? familyValue.getValue()
0640 : Font.SANS_SERIF;
0641 int style = Font.PLAIN;
0642 FontWeight weightValue = (FontWeight) a
0643 .getAttribute(CSS.Attribute.FONT_WEIGHT);
0644 if ((weightValue != null) && (weightValue.getValue() > 400)) {
0645 style |= Font.BOLD;
0646 }
0647 Object fs = a.getAttribute(CSS.Attribute.FONT_STYLE);
0648 if ((fs != null) && (fs.toString().indexOf("italic") >= 0)) {
0649 style |= Font.ITALIC;
0650 }
0651 if (family.equalsIgnoreCase("monospace")) {
0652 family = Font.MONOSPACED;
0653 }
0654 Font f = sc.getFont(family, style, size);
0655 if (f == null
0656 || (f.getFamily().equals(Font.DIALOG) && !family
0657 .equalsIgnoreCase(Font.DIALOG))) {
0658 family = Font.SANS_SERIF;
0659 f = sc.getFont(family, style, size);
0660 }
0661 return f;
0662 }
0663
0664 static int getFontSize(AttributeSet attr, int defaultSize,
0665 StyleSheet ss) {
0666 // PENDING(prinz) this is a 1.1 based implementation, need to also
0667 // have a 1.2 version.
0668 FontSize sizeValue = (FontSize) attr
0669 .getAttribute(CSS.Attribute.FONT_SIZE);
0670
0671 return (sizeValue != null) ? sizeValue.getValue(attr, ss)
0672 : defaultSize;
0673 }
0674
0675 /**
0676 * Takes a set of attributes and turn it into a color
0677 * specification. This might be used to specify things
0678 * like brighter, more hue, etc.
0679 * This will return null if there is no value for <code>key</code>.
0680 *
0681 * @param key CSS.Attribute identifying where color is stored.
0682 * @param a the set of attributes
0683 * @return the color
0684 */
0685 Color getColor(AttributeSet a, CSS.Attribute key) {
0686 ColorValue cv = (ColorValue) a.getAttribute(key);
0687 if (cv != null) {
0688 return cv.getValue();
0689 }
0690 return null;
0691 }
0692
0693 /**
0694 * Returns the size of a font from the passed in string.
0695 *
0696 * @param size CSS string describing font size
0697 * @param baseFontSize size to use for relative units.
0698 */
0699 float getPointSize(String size, StyleSheet ss) {
0700 int relSize, absSize, diff, index;
0701 ss = getStyleSheet(ss);
0702 if (size != null) {
0703 if (size.startsWith("+")) {
0704 relSize = Integer.valueOf(size.substring(1)).intValue();
0705 return getPointSize(baseFontSize + relSize, ss);
0706 } else if (size.startsWith("-")) {
0707 relSize = -Integer.valueOf(size.substring(1))
0708 .intValue();
0709 return getPointSize(baseFontSize + relSize, ss);
0710 } else {
0711 absSize = Integer.valueOf(size).intValue();
0712 return getPointSize(absSize, ss);
0713 }
0714 }
0715 return 0;
0716 }
0717
0718 /**
0719 * Returns the length of the attribute in <code>a</code> with
0720 * key <code>key</code>.
0721 */
0722 float getLength(AttributeSet a, CSS.Attribute key, StyleSheet ss) {
0723 ss = getStyleSheet(ss);
0724 LengthValue lv = (LengthValue) a.getAttribute(key);
0725 boolean isW3CLengthUnits = (ss == null) ? false : ss
0726 .isW3CLengthUnits();
0727 float len = (lv != null) ? lv.getValue(isW3CLengthUnits) : 0;
0728 return len;
0729 }
0730
0731 /**
0732 * Convert a set of HTML attributes to an equivalent
0733 * set of CSS attributes.
0734 *
0735 * @param AttributeSet containing the HTML attributes.
0736 * @return AttributeSet containing the corresponding CSS attributes.
0737 * The AttributeSet will be empty if there are no mapping
0738 * CSS attributes.
0739 */
0740 AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet) {
0741 MutableAttributeSet cssAttrSet = new SimpleAttributeSet();
0742 Element elem = (Element) htmlAttrSet;
0743 HTML.Tag tag = getHTMLTag(htmlAttrSet);
0744 if ((tag == HTML.Tag.TD) || (tag == HTML.Tag.TH)) {
0745 // translate border width into the cells
0746 AttributeSet tableAttr = elem.getParentElement()
0747 .getParentElement().getAttributes();
0748 translateAttribute(HTML.Attribute.BORDER, tableAttr,
0749 cssAttrSet);
0750 String pad = (String) tableAttr
0751 .getAttribute(HTML.Attribute.CELLPADDING);
0752 if (pad != null) {
0753 LengthValue v = (LengthValue) getInternalCSSValue(
0754 CSS.Attribute.PADDING_TOP, pad);
0755 v.span = (v.span < 0) ? 0 : v.span;
0756 cssAttrSet.addAttribute(CSS.Attribute.PADDING_TOP, v);
0757 cssAttrSet
0758 .addAttribute(CSS.Attribute.PADDING_BOTTOM, v);
0759 cssAttrSet.addAttribute(CSS.Attribute.PADDING_LEFT, v);
0760 cssAttrSet.addAttribute(CSS.Attribute.PADDING_RIGHT, v);
0761 }
0762 }
0763 if (elem.isLeaf()) {
0764 translateEmbeddedAttributes(htmlAttrSet, cssAttrSet);
0765 } else {
0766 translateAttributes(tag, htmlAttrSet, cssAttrSet);
0767 }
0768 if (tag == HTML.Tag.CAPTION) {
0769 /*
0770 * Navigator uses ALIGN for caption placement and IE uses VALIGN.
0771 */
0772 Object v = htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
0773 if ((v != null) && (v.equals("top") || v.equals("bottom"))) {
0774 cssAttrSet.addAttribute(CSS.Attribute.CAPTION_SIDE, v);
0775 cssAttrSet.removeAttribute(CSS.Attribute.TEXT_ALIGN);
0776 } else {
0777 v = htmlAttrSet.getAttribute(HTML.Attribute.VALIGN);
0778 if (v != null) {
0779 cssAttrSet.addAttribute(CSS.Attribute.CAPTION_SIDE,
0780 v);
0781 }
0782 }
0783 }
0784 return cssAttrSet;
0785 }
0786
0787 private static final Hashtable attributeMap = new Hashtable();
0788 private static final Hashtable valueMap = new Hashtable();
0789
0790 /**
0791 * The hashtable and the static initalization block below,
0792 * set up a mapping from well-known HTML attributes to
0793 * CSS attributes. For the most part, there is a 1-1 mapping
0794 * between the two. However in the case of certain HTML
0795 * attributes for example HTML.Attribute.VSPACE or
0796 * HTML.Attribute.HSPACE, end up mapping to two CSS.Attribute's.
0797 * Therefore, the value associated with each HTML.Attribute.
0798 * key ends up being an array of CSS.Attribute.* objects.
0799 */
0800 private static final Hashtable htmlAttrToCssAttrMap = new Hashtable(
0801 20);
0802
0803 /**
0804 * The hashtable and static initialization that follows sets
0805 * up a translation from StyleConstants (i.e. the <em>well known</em>
0806 * attributes) to the associated CSS attributes.
0807 */
0808 private static final Hashtable styleConstantToCssMap = new Hashtable(
0809 17);
0810 /** Maps from HTML value to a CSS value. Used in internal mapping. */
0811 private static final Hashtable htmlValueToCssValueMap = new Hashtable(
0812 8);
0813 /** Maps from CSS value (string) to internal value. */
0814 private static final Hashtable cssValueToInternalValueMap = new Hashtable(
0815 13);
0816
0817 static {
0818 // load the attribute map
0819 for (int i = 0; i < Attribute.allAttributes.length; i++) {
0820 attributeMap.put(Attribute.allAttributes[i].toString(),
0821 Attribute.allAttributes[i]);
0822 }
0823 // load the value map
0824 for (int i = 0; i < Value.allValues.length; i++) {
0825 valueMap.put(Value.allValues[i].toString(),
0826 Value.allValues[i]);
0827 }
0828
0829 htmlAttrToCssAttrMap.put(HTML.Attribute.COLOR,
0830 new CSS.Attribute[] { CSS.Attribute.COLOR });
0831 htmlAttrToCssAttrMap.put(HTML.Attribute.TEXT,
0832 new CSS.Attribute[] { CSS.Attribute.COLOR });
0833 htmlAttrToCssAttrMap.put(HTML.Attribute.CLEAR,
0834 new CSS.Attribute[] { CSS.Attribute.CLEAR });
0835 htmlAttrToCssAttrMap.put(HTML.Attribute.BACKGROUND,
0836 new CSS.Attribute[] { CSS.Attribute.BACKGROUND_IMAGE });
0837 htmlAttrToCssAttrMap.put(HTML.Attribute.BGCOLOR,
0838 new CSS.Attribute[] { CSS.Attribute.BACKGROUND_COLOR });
0839 htmlAttrToCssAttrMap.put(HTML.Attribute.WIDTH,
0840 new CSS.Attribute[] { CSS.Attribute.WIDTH });
0841 htmlAttrToCssAttrMap.put(HTML.Attribute.HEIGHT,
0842 new CSS.Attribute[] { CSS.Attribute.HEIGHT });
0843 htmlAttrToCssAttrMap.put(HTML.Attribute.BORDER,
0844 new CSS.Attribute[] { CSS.Attribute.BORDER_TOP_WIDTH,
0845 CSS.Attribute.BORDER_RIGHT_WIDTH,
0846 CSS.Attribute.BORDER_BOTTOM_WIDTH,
0847 CSS.Attribute.BORDER_LEFT_WIDTH });
0848 htmlAttrToCssAttrMap.put(HTML.Attribute.CELLPADDING,
0849 new CSS.Attribute[] { CSS.Attribute.PADDING });
0850 htmlAttrToCssAttrMap.put(HTML.Attribute.CELLSPACING,
0851 new CSS.Attribute[] { CSS.Attribute.BORDER_SPACING });
0852 htmlAttrToCssAttrMap.put(HTML.Attribute.MARGINWIDTH,
0853 new CSS.Attribute[] { CSS.Attribute.MARGIN_LEFT,
0854 CSS.Attribute.MARGIN_RIGHT });
0855 htmlAttrToCssAttrMap.put(HTML.Attribute.MARGINHEIGHT,
0856 new CSS.Attribute[] { CSS.Attribute.MARGIN_TOP,
0857 CSS.Attribute.MARGIN_BOTTOM });
0858 htmlAttrToCssAttrMap.put(HTML.Attribute.HSPACE,
0859 new CSS.Attribute[] { CSS.Attribute.PADDING_LEFT,
0860 CSS.Attribute.PADDING_RIGHT });
0861 htmlAttrToCssAttrMap.put(HTML.Attribute.VSPACE,
0862 new CSS.Attribute[] { CSS.Attribute.PADDING_BOTTOM,
0863 CSS.Attribute.PADDING_TOP });
0864 htmlAttrToCssAttrMap.put(HTML.Attribute.FACE,
0865 new CSS.Attribute[] { CSS.Attribute.FONT_FAMILY });
0866 htmlAttrToCssAttrMap.put(HTML.Attribute.SIZE,
0867 new CSS.Attribute[] { CSS.Attribute.FONT_SIZE });
0868 htmlAttrToCssAttrMap.put(HTML.Attribute.VALIGN,
0869 new CSS.Attribute[] { CSS.Attribute.VERTICAL_ALIGN });
0870 htmlAttrToCssAttrMap
0871 .put(HTML.Attribute.ALIGN, new CSS.Attribute[] {
0872 CSS.Attribute.VERTICAL_ALIGN,
0873 CSS.Attribute.TEXT_ALIGN, CSS.Attribute.FLOAT });
0874 htmlAttrToCssAttrMap.put(HTML.Attribute.TYPE,
0875 new CSS.Attribute[] { CSS.Attribute.LIST_STYLE_TYPE });
0876 htmlAttrToCssAttrMap.put(HTML.Attribute.NOWRAP,
0877 new CSS.Attribute[] { CSS.Attribute.WHITE_SPACE });
0878
0879 // initialize StyleConstants mapping
0880 styleConstantToCssMap.put(StyleConstants.FontFamily,
0881 CSS.Attribute.FONT_FAMILY);
0882 styleConstantToCssMap.put(StyleConstants.FontSize,
0883 CSS.Attribute.FONT_SIZE);
0884 styleConstantToCssMap.put(StyleConstants.Bold,
0885 CSS.Attribute.FONT_WEIGHT);
0886 styleConstantToCssMap.put(StyleConstants.Italic,
0887 CSS.Attribute.FONT_STYLE);
0888 styleConstantToCssMap.put(StyleConstants.Underline,
0889 CSS.Attribute.TEXT_DECORATION);
0890 styleConstantToCssMap.put(StyleConstants.StrikeThrough,
0891 CSS.Attribute.TEXT_DECORATION);
0892 styleConstantToCssMap.put(StyleConstants.Superscript,
0893 CSS.Attribute.VERTICAL_ALIGN);
0894 styleConstantToCssMap.put(StyleConstants.Subscript,
0895 CSS.Attribute.VERTICAL_ALIGN);
0896 styleConstantToCssMap.put(StyleConstants.Foreground,
0897 CSS.Attribute.COLOR);
0898 styleConstantToCssMap.put(StyleConstants.Background,
0899 CSS.Attribute.BACKGROUND_COLOR);
0900 styleConstantToCssMap.put(StyleConstants.FirstLineIndent,
0901 CSS.Attribute.TEXT_INDENT);
0902 styleConstantToCssMap.put(StyleConstants.LeftIndent,
0903 CSS.Attribute.MARGIN_LEFT);
0904 styleConstantToCssMap.put(StyleConstants.RightIndent,
0905 CSS.Attribute.MARGIN_RIGHT);
0906 styleConstantToCssMap.put(StyleConstants.SpaceAbove,
0907 CSS.Attribute.MARGIN_TOP);
0908 styleConstantToCssMap.put(StyleConstants.SpaceBelow,
0909 CSS.Attribute.MARGIN_BOTTOM);
0910 styleConstantToCssMap.put(StyleConstants.Alignment,
0911 CSS.Attribute.TEXT_ALIGN);
0912
0913 // HTML->CSS
0914 htmlValueToCssValueMap.put("disc", CSS.Value.DISC);
0915 htmlValueToCssValueMap.put("square", CSS.Value.SQUARE);
0916 htmlValueToCssValueMap.put("circle", CSS.Value.CIRCLE);
0917 htmlValueToCssValueMap.put("1", CSS.Value.DECIMAL);
0918 htmlValueToCssValueMap.put("a", CSS.Value.LOWER_ALPHA);
0919 htmlValueToCssValueMap.put("A", CSS.Value.UPPER_ALPHA);
0920 htmlValueToCssValueMap.put("i", CSS.Value.LOWER_ROMAN);
0921 htmlValueToCssValueMap.put("I", CSS.Value.UPPER_ROMAN);
0922
0923 // CSS-> internal CSS
0924 cssValueToInternalValueMap.put("none", CSS.Value.NONE);
0925 cssValueToInternalValueMap.put("disc", CSS.Value.DISC);
0926 cssValueToInternalValueMap.put("square", CSS.Value.SQUARE);
0927 cssValueToInternalValueMap.put("circle", CSS.Value.CIRCLE);
0928 cssValueToInternalValueMap.put("decimal", CSS.Value.DECIMAL);
0929 cssValueToInternalValueMap.put("lower-roman",
0930 CSS.Value.LOWER_ROMAN);
0931 cssValueToInternalValueMap.put("upper-roman",
0932 CSS.Value.UPPER_ROMAN);
0933 cssValueToInternalValueMap.put("lower-alpha",
0934 CSS.Value.LOWER_ALPHA);
0935 cssValueToInternalValueMap.put("upper-alpha",
0936 CSS.Value.UPPER_ALPHA);
0937 cssValueToInternalValueMap.put("repeat",
0938 CSS.Value.BACKGROUND_REPEAT);
0939 cssValueToInternalValueMap.put("no-repeat",
0940 CSS.Value.BACKGROUND_NO_REPEAT);
0941 cssValueToInternalValueMap.put("repeat-x",
0942 CSS.Value.BACKGROUND_REPEAT_X);
0943 cssValueToInternalValueMap.put("repeat-y",
0944 CSS.Value.BACKGROUND_REPEAT_Y);
0945 cssValueToInternalValueMap.put("scroll",
0946 CSS.Value.BACKGROUND_SCROLL);
0947 cssValueToInternalValueMap.put("fixed",
0948 CSS.Value.BACKGROUND_FIXED);
0949
0950 // Register all the CSS attribute keys for archival/unarchival
0951 Object[] keys = CSS.Attribute.allAttributes;
0952 try {
0953 for (int i = 0; i < keys.length; i++) {
0954 StyleContext.registerStaticAttributeKey(keys[i]);
0955 }
0956 } catch (Throwable e) {
0957 e.printStackTrace();
0958 }
0959
0960 // Register all the CSS Values for archival/unarchival
0961 keys = CSS.Value.allValues;
0962 try {
0963 for (int i = 0; i < keys.length; i++) {
0964 StyleContext.registerStaticAttributeKey(keys[i]);
0965 }
0966 } catch (Throwable e) {
0967 e.printStackTrace();
0968 }
0969 }
0970
0971 /**
0972 * Return the set of all possible CSS attribute keys.
0973 */
0974 public static Attribute[] getAllAttributeKeys() {
0975 Attribute[] keys = new Attribute[Attribute.allAttributes.length];
0976 System.arraycopy(Attribute.allAttributes, 0, keys, 0,
0977 Attribute.allAttributes.length);
0978 return keys;
0979 }
0980
0981 /**
0982 * Translates a string to a <code>CSS.Attribute</code> object.
0983 * This will return <code>null</code> if there is no attribute
0984 * by the given name.
0985 *
0986 * @param name the name of the CSS attribute to fetch the
0987 * typesafe enumeration for
0988 * @return the <code>CSS.Attribute</code> object,
0989 * or <code>null</code> if the string
0990 * doesn't represent a valid attribute key
0991 */
0992 public static final Attribute getAttribute(String name) {
0993 return (Attribute) attributeMap.get(name);
0994 }
0995
0996 /**
0997 * Translates a string to a <code>CSS.Value</code> object.
0998 * This will return <code>null</code> if there is no value
0999 * by the given name.
1000 *
1001 * @param name the name of the CSS value to fetch the
1002 * typesafe enumeration for
1003 * @return the <code>CSS.Value</code> object,
1004 * or <code>null</code> if the string
1005 * doesn't represent a valid CSS value name; this does
1006 * not mean that it doesn't represent a valid CSS value
1007 */
1008 static final Value getValue(String name) {
1009 return (Value) valueMap.get(name);
1010 }
1011
1012 //
1013 // Conversion related methods/classes
1014 //
1015
1016 /**
1017 * Returns a URL for the given CSS url string. If relative,
1018 * <code>base</code> is used as the parent. If a valid URL can not
1019 * be found, this will not throw a MalformedURLException, instead
1020 * null will be returned.
1021 */
1022 static URL getURL(URL base, String cssString) {
1023 if (cssString == null) {
1024 return null;
1025 }
1026 if (cssString.startsWith("url(") && cssString.endsWith(")")) {
1027 cssString = cssString.substring(4, cssString.length() - 1);
1028 }
1029 // Absolute first
1030 try {
1031 URL url = new URL(cssString);
1032 if (url != null) {
1033 return url;
1034 }
1035 } catch (MalformedURLException mue) {
1036 }
1037 // Then relative
1038 if (base != null) {
1039 // Relative URL, try from base
1040 try {
1041 URL url = new URL(base, cssString);
1042 return url;
1043 } catch (MalformedURLException muee) {
1044 }
1045 }
1046 return null;
1047 }
1048
1049 /**
1050 * Converts a type Color to a hex string
1051 * in the format "#RRGGBB"
1052 */
1053 static String colorToHex(Color color) {
1054
1055 String colorstr = new String("#");
1056
1057 // Red
1058 String str = Integer.toHexString(color.getRed());
1059 if (str.length() > 2)
1060 str = str.substring(0, 2);
1061 else if (str.length() < 2)
1062 colorstr += "0" + str;
1063 else
1064 colorstr += str;
1065
1066 // Green
1067 str = Integer.toHexString(color.getGreen());
1068 if (str.length() > 2)
1069 str = str.substring(0, 2);
1070 else if (str.length() < 2)
1071 colorstr += "0" + str;
1072 else
1073 colorstr += str;
1074
1075 // Blue
1076 str = Integer.toHexString(color.getBlue());
1077 if (str.length() > 2)
1078 str = str.substring(0, 2);
1079 else if (str.length() < 2)
1080 colorstr += "0" + str;
1081 else
1082 colorstr += str;
1083
1084 return colorstr;
1085 }
1086
1087 /**
1088 * Convert a "#FFFFFF" hex string to a Color.
1089 * If the color specification is bad, an attempt
1090 * will be made to fix it up.
1091 */
1092 static final Color hexToColor(String value) {
1093 String digits;
1094 int n = value.length();
1095 if (value.startsWith("#")) {
1096 digits = value.substring(1, Math.min(value.length(), 7));
1097 } else {
1098 digits = value;
1099 }
1100 String hstr = "0x" + digits;
1101 Color c;
1102 try {
1103 c = Color.decode(hstr);
1104 } catch (NumberFormatException nfe) {
1105 c = null;
1106 }
1107 return c;
1108 }
1109
1110 /**
1111 * Convert a color string such as "RED" or "#NNNNNN" or "rgb(r, g, b)"
1112 * to a Color.
1113 */
1114 static Color stringToColor(String str) {
1115 Color color = null;
1116
1117 if (str.length() == 0)
1118 color = Color.black;
1119 else if (str.startsWith("rgb(")) {
1120 color = parseRGB(str);
1121 } else if (str.charAt(0) == '#')
1122 color = hexToColor(str);
1123 else if (str.equalsIgnoreCase("Black"))
1124 color = hexToColor("#000000");
1125 else if (str.equalsIgnoreCase("Silver"))
1126 color = hexToColor("#C0C0C0");
1127 else if (str.equalsIgnoreCase("Gray"))
1128 color = hexToColor("#808080");
1129 else if (str.equalsIgnoreCase("White"))
1130 color = hexToColor("#FFFFFF");
1131 else if (str.equalsIgnoreCase("Maroon"))
1132 color = hexToColor("#800000");
1133 else if (str.equalsIgnoreCase("Red"))
1134 color = hexToColor("#FF0000");
1135 else if (str.equalsIgnoreCase("Purple"))
1136 color = hexToColor("#800080");
1137 else if (str.equalsIgnoreCase("Fuchsia"))
1138 color = hexToColor("#FF00FF");
1139 else if (str.equalsIgnoreCase("Green"))
1140 color = hexToColor("#008000");
1141 else if (str.equalsIgnoreCase("Lime"))
1142 color = hexToColor("#00FF00");
1143 else if (str.equalsIgnoreCase("Olive"))
1144 color = hexToColor("#808000");
1145 else if (str.equalsIgnoreCase("Yellow"))
1146 color = hexToColor("#FFFF00");
1147 else if (str.equalsIgnoreCase("Navy"))
1148 color = hexToColor("#000080");
1149 else if (str.equalsIgnoreCase("Blue"))
1150 color = hexToColor("#0000FF");
1151 else if (str.equalsIgnoreCase("Teal"))
1152 color = hexToColor("#008080");
1153 else if (str.equalsIgnoreCase("Aqua"))
1154 color = hexToColor("#00FFFF");
1155 else
1156 color = hexToColor(str); // sometimes get specified without leading #
1157 return color;
1158 }
1159
1160 /**
1161 * Parses a String in the format <code>rgb(r, g, b)</code> where
1162 * each of the Color components is either an integer, or a floating number
1163 * with a % after indicating a percentage value of 255. Values are
1164 * constrained to fit with 0-255. The resulting Color is returned.
1165 */
1166 private static Color parseRGB(String string) {
1167 // Find the next numeric char
1168 int[] index = new int[1];
1169
1170 index[0] = 4;
1171 int red = getColorComponent(string, index);
1172 int green = getColorComponent(string, index);
1173 int blue = getColorComponent(string, index);
1174
1175 return new Color(red, green, blue);
1176 }
1177
1178 /**
1179 * Returns the next integer value from <code>string</code> starting
1180 * at <code>index[0]</code>. The value can either can an integer, or
1181 * a percentage (floating number ending with %), in which case it is
1182 * multiplied by 255.
1183 */
1184 private static int getColorComponent(String string, int[] index) {
1185 int length = string.length();
1186 char aChar;
1187
1188 // Skip non-decimal chars
1189 while (index[0] < length
1190 && (aChar = string.charAt(index[0])) != '-'
1191 && !Character.isDigit(aChar) && aChar != '.') {
1192 index[0]++;
1193 }
1194
1195 int start = index[0];
1196
1197 if (start < length && string.charAt(index[0]) == '-') {
1198 index[0]++;
1199 }
1200 while (index[0] < length
1201 && Character.isDigit(string.charAt(index[0]))) {
1202 index[0]++;
1203 }
1204 if (index[0] < length && string.charAt(index[0]) == '.') {
1205 // Decimal value
1206 index[0]++;
1207 while (index[0] < length
1208 && Character.isDigit(string.charAt(index[0]))) {
1209 index[0]++;
1210 }
1211 }
1212 if (start != index[0]) {
1213 try {
1214 float value = Float.parseFloat(string.substring(start,
1215 index[0]));
1216
1217 if (index[0] < length && string.charAt(index[0]) == '%') {
1218 index[0]++;
1219 value = value * 255f / 100f;
1220 }
1221 return Math.min(255, Math.max(0, (int) value));
1222 } catch (NumberFormatException nfe) {
1223 // Treat as 0
1224 }
1225 }
1226 return 0;
1227 }
1228
1229 static int getIndexOfSize(float pt, int[] sizeMap) {
1230 for (int i = 0; i < sizeMap.length; i++)
1231 if (pt <= sizeMap[i])
1232 return i + 1;
1233 return sizeMap.length;
1234 }
1235
1236 static int getIndexOfSize(float pt, StyleSheet ss) {
1237 int[] sizeMap = (ss != null) ? ss.getSizeMap()
1238 : StyleSheet.sizeMapDefault;
1239 return getIndexOfSize(pt, sizeMap);
1240 }
1241
1242 /**
1243 * @return an array of all the strings in <code>value</code>
1244 * that are separated by whitespace.
1245 */
1246 static String[] parseStrings(String value) {
1247 int current, last;
1248 int length = (value == null) ? 0 : value.length();
1249 Vector temp = new Vector(4);
1250
1251 current = 0;
1252 while (current < length) {
1253 // Skip ws
1254 while (current < length
1255 && Character.isWhitespace(value.charAt(current))) {
1256 current++;
1257 }
1258 last = current;
1259 while (current < length
1260 && !Character.isWhitespace(value.charAt(current))) {
1261 current++;
1262 }
1263 if (last != current) {
1264 temp.addElement(value.substring(last, current));
1265 }
1266 current++;
1267 }
1268 String[] retValue = new String[temp.size()];
1269 temp.copyInto(retValue);
1270 return retValue;
1271 }
1272
1273 /**
1274 * Return the point size, given a size index. Legal HTML index sizes
1275 * are 1-7.
1276 */
1277 float getPointSize(int index, StyleSheet ss) {
1278 ss = getStyleSheet(ss);
1279 int[] sizeMap = (ss != null) ? ss.getSizeMap()
1280 : StyleSheet.sizeMapDefault;
1281 --index;
1282 if (index < 0)
1283 return sizeMap[0];
1284 else if (index > sizeMap.length - 1)
1285 return sizeMap[sizeMap.length - 1];
1286 else
1287 return sizeMap[index];
1288 }
1289
1290 private void translateEmbeddedAttributes(AttributeSet htmlAttrSet,
1291 MutableAttributeSet cssAttrSet) {
1292 Enumeration keys = htmlAttrSet.getAttributeNames();
1293 if (htmlAttrSet.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.HR) {
1294 // HR needs special handling due to us treating it as a leaf.
1295 translateAttributes(HTML.Tag.HR, htmlAttrSet, cssAttrSet);
1296 }
1297 while (keys.hasMoreElements()) {
1298 Object key = keys.nextElement();
1299 if (key instanceof HTML.Tag) {
1300 HTML.Tag tag = (HTML.Tag) key;
1301 Object o = htmlAttrSet.getAttribute(tag);
1302 if (o != null && o instanceof AttributeSet) {
1303 translateAttributes(tag, (AttributeSet) o,
1304 cssAttrSet);
1305 }
1306 } else if (key instanceof CSS.Attribute) {
1307 cssAttrSet.addAttribute(key, htmlAttrSet
1308 .getAttribute(key));
1309 }
1310 }
1311 }
1312
1313 private void translateAttributes(HTML.Tag tag,
1314 AttributeSet htmlAttrSet, MutableAttributeSet cssAttrSet) {
1315 Enumeration names = htmlAttrSet.getAttributeNames();
1316 while (names.hasMoreElements()) {
1317 Object name = names.nextElement();
1318
1319 if (name instanceof HTML.Attribute) {
1320 HTML.Attribute key = (HTML.Attribute) name;
1321
1322 /*
1323 * HTML.Attribute.ALIGN needs special processing.
1324 * It can map to to 1 of many(3) possible CSS attributes
1325 * depending on the nature of the tag the attribute is
1326 * part off and depending on the value of the attribute.
1327 */
1328 if (key == HTML.Attribute.ALIGN) {
1329 String htmlAttrValue = (String) htmlAttrSet
1330 .getAttribute(HTML.Attribute.ALIGN);
1331 if (htmlAttrValue != null) {
1332 CSS.Attribute cssAttr = getCssAlignAttribute(
1333 tag, htmlAttrSet);
1334 if (cssAttr != null) {
1335 Object o = getCssValue(cssAttr,
1336 htmlAttrValue);
1337 if (o != null) {
1338 cssAttrSet.addAttribute(cssAttr, o);
1339 }
1340 }
1341 }
1342 } else {
1343
1344 /*
1345 * The html size attribute has a mapping in the CSS world only
1346 * if it is par of a font or base font tag.
1347 */
1348
1349 if (key == HTML.Attribute.SIZE
1350 && !isHTMLFontTag(tag)) {
1351 continue;
1352 }
1353
1354 translateAttribute(key, htmlAttrSet, cssAttrSet);
1355 }
1356 } else if (name instanceof CSS.Attribute) {
1357 cssAttrSet.addAttribute(name, htmlAttrSet
1358 .getAttribute(name));
1359 }
1360 }
1361 }
1362
1363 private void translateAttribute(HTML.Attribute key,
1364 AttributeSet htmlAttrSet, MutableAttributeSet cssAttrSet) {
1365 /*
1366 * In the case of all remaining HTML.Attribute's they
1367 * map to 1 or more CCS.Attribute.
1368 */
1369 CSS.Attribute[] cssAttrList = getCssAttribute(key);
1370
1371 String htmlAttrValue = (String) htmlAttrSet.getAttribute(key);
1372
1373 if (cssAttrList == null || htmlAttrValue == null) {
1374 return;
1375 }
1376 for (int i = 0; i < cssAttrList.length; i++) {
1377 Object o = getCssValue(cssAttrList[i], htmlAttrValue);
1378 if (o != null) {
1379 cssAttrSet.addAttribute(cssAttrList[i], o);
1380 }
1381 }
1382 }
1383
1384 /**
1385 * Given a CSS.Attribute object and its corresponding HTML.Attribute's
1386 * value, this method returns a CssValue object to associate with the
1387 * CSS attribute.
1388 *
1389 * @param the CSS.Attribute
1390 * @param a String containing the value associated HTML.Attribtue.
1391 */
1392 Object getCssValue(CSS.Attribute cssAttr, String htmlAttrValue) {
1393 CssValue value = (CssValue) valueConvertor.get(cssAttr);
1394 Object o = value.parseHtmlValue(htmlAttrValue);
1395 return o;
1396 }
1397
1398 /**
1399 * Maps an HTML.Attribute object to its appropriate CSS.Attributes.
1400 *
1401 * @param HTML.Attribute
1402 * @return CSS.Attribute[]
1403 */
1404 private CSS.Attribute[] getCssAttribute(HTML.Attribute hAttr) {
1405 return (CSS.Attribute[]) htmlAttrToCssAttrMap.get(hAttr);
1406 }
1407
1408 /**
1409 * Maps HTML.Attribute.ALIGN to either:
1410 * CSS.Attribute.TEXT_ALIGN
1411 * CSS.Attribute.FLOAT
1412 * CSS.Attribute.VERTICAL_ALIGN
1413 * based on the tag associated with the attribute and the
1414 * value of the attribute.
1415 *
1416 * @param AttributeSet containing HTML attributes.
1417 * @return CSS.Attribute mapping for HTML.Attribute.ALIGN.
1418 */
1419 private CSS.Attribute getCssAlignAttribute(HTML.Tag tag,
1420 AttributeSet htmlAttrSet) {
1421 return CSS.Attribute.TEXT_ALIGN;
1422 /*
1423 String htmlAttrValue = (String)htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
1424 CSS.Attribute cssAttr = CSS.Attribute.TEXT_ALIGN;
1425 if (htmlAttrValue != null && htmlAttrSet instanceof Element) {
1426 Element elem = (Element)htmlAttrSet;
1427 if (!elem.isLeaf() && tag.isBlock() && validTextAlignValue(htmlAttrValue)) {
1428 return CSS.Attribute.TEXT_ALIGN;
1429 } else if (isFloater(htmlAttrValue)) {
1430 return CSS.Attribute.FLOAT;
1431 } else if (elem.isLeaf()) {
1432 return CSS.Attribute.VERTICAL_ALIGN;
1433 }
1434 }
1435 return null;
1436 */
1437 }
1438
1439 /**
1440 * Fetches the tag associated with the HTML AttributeSet.
1441 *
1442 * @param AttributeSet containing the HTML attributes.
1443 * @return HTML.Tag
1444 */
1445 private HTML.Tag getHTMLTag(AttributeSet htmlAttrSet) {
1446 Object o = htmlAttrSet
1447 .getAttribute(StyleConstants.NameAttribute);
1448 if (o instanceof HTML.Tag) {
1449 HTML.Tag tag = (HTML.Tag) o;
1450 return tag;
1451 }
1452 return null;
1453 }
1454
1455 private boolean isHTMLFontTag(HTML.Tag tag) {
1456 return (tag != null && ((tag == HTML.Tag.FONT) || (tag == HTML.Tag.BASEFONT)));
1457 }
1458
1459 private boolean isFloater(String alignValue) {
1460 return (alignValue.equals("left") || alignValue.equals("right"));
1461 }
1462
1463 private boolean validTextAlignValue(String alignValue) {
1464 return (isFloater(alignValue) || alignValue.equals("center"));
1465 }
1466
1467 /**
1468 * Base class to CSS values in the attribute sets. This
1469 * is intended to act as a convertor to/from other attribute
1470 * formats.
1471 * <p>
1472 * The CSS parser uses the parseCssValue method to convert
1473 * a string to whatever format is appropriate a given key
1474 * (i.e. these convertors are stored in a map using the
1475 * CSS.Attribute as a key and the CssValue as the value).
1476 * <p>
1477 * The HTML to CSS conversion process first converts the
1478 * HTML.Attribute to a CSS.Attribute, and then calls
1479 * the parseHtmlValue method on the value of the HTML
1480 * attribute to produce the corresponding CSS value.
1481 * <p>
1482 * The StyleConstants to CSS conversion process first
1483 * converts the StyleConstants attribute to a
1484 * CSS.Attribute, and then calls the fromStyleConstants
1485 * method to convert the StyleConstants value to a
1486 * CSS value.
1487 * <p>
1488 * The CSS to StyleConstants conversion process first
1489 * converts the StyleConstants attribute to a
1490 * CSS.Attribute, and then calls the toStyleConstants
1491 * method to convert the CSS value to a StyleConstants
1492 * value.
1493 */
1494 static class CssValue implements Serializable {
1495
1496 /**
1497 * Convert a CSS value string to the internal format
1498 * (for fast processing) used in the attribute sets.
1499 * The fallback storage for any value that we don't
1500 * have a special binary format for is a String.
1501 */
1502 Object parseCssValue(String value) {
1503 return value;
1504 }
1505
1506 /**
1507 * Convert an HTML attribute value to a CSS attribute
1508 * value. If there is no conversion, return null.
1509 * This is implemented to simply forward to the CSS
1510 * parsing by default (since some of the attribute
1511 * values are the same). If the attribute value
1512 * isn't recognized as a CSS value it is generally
1513 * returned as null.
1514 */
1515 Object parseHtmlValue(String value) {
1516 return parseCssValue(value);
1517 }
1518
1519 /**
1520 * Converts a <code>StyleConstants</code> attribute value to
1521 * a CSS attribute value. If there is no conversion,
1522 * returns <code>null</code>. By default, there is no conversion.
1523 *
1524 * @param key the <code>StyleConstants</code> attribute
1525 * @param value the value of a <code>StyleConstants</code>
1526 * attribute to be converted
1527 * @return the CSS value that represents the
1528 * <code>StyleConstants</code> value
1529 */
1530 Object fromStyleConstants(StyleConstants key, Object value) {
1531 return null;
1532 }
1533
1534 /**
1535 * Converts a CSS attribute value to a
1536 * <code>StyleConstants</code>
1537 * value. If there is no conversion, returns
1538 * <code>null</code>.
1539 * By default, there is no conversion.
1540 *
1541 * @param key the <code>StyleConstants</code> attribute
1542 * @param v the view containing <code>AttributeSet</code>
1543 * @return the <code>StyleConstants</code> attribute value that
1544 * represents the CSS attribute value
1545 */
1546 Object toStyleConstants(StyleConstants key, View v) {
1547 return null;
1548 }
1549
1550 /**
1551 * Return the CSS format of the value
1552 */
1553 public String toString() {
1554 return svalue;
1555 }
1556
1557 /**
1558 * The value as a string... before conversion to a
1559 * binary format.
1560 */
1561 String svalue;
1562 }
1563
1564 /**
1565 * By default CSS attributes are represented as simple
1566 * strings. They also have no conversion to/from
1567 * StyleConstants by default. This class represents the
1568 * value as a string (via the superclass), but
1569 * provides StyleConstants conversion support for the
1570 * CSS attributes that are held as strings.
1571 */
1572 static class StringValue extends CssValue {
1573
1574 /**
1575 * Convert a CSS value string to the internal format
1576 * (for fast processing) used in the attribute sets.
1577 * This produces a StringValue, so that it can be
1578 * used to convert from CSS to StyleConstants values.
1579 */
1580 Object parseCssValue(String value) {
1581 StringValue sv = new StringValue();
1582 sv.svalue = value;
1583 return sv;
1584 }
1585
1586 /**
1587 * Converts a <code>StyleConstants</code> attribute value to
1588 * a CSS attribute value. If there is no conversion
1589 * returns <code>null</code>.
1590 *
1591 * @param key the <code>StyleConstants</code> attribute
1592 * @param value the value of a <code>StyleConstants</code>
1593 * attribute to be converted
1594 * @return the CSS value that represents the
1595 * <code>StyleConstants</code> value
1596 */
1597 Object fromStyleConstants(StyleConstants key, Object value) {
1598 if (key == StyleConstants.Italic) {
1599 if (value.equals(Boolean.TRUE)) {
1600 return parseCssValue("italic");
1601 }
1602 return parseCssValue("");
1603 } else if (key == StyleConstants.Underline) {
1604 if (value.equals(Boolean.TRUE)) {
1605 return parseCssValue("underline");
1606 }
1607 return parseCssValue("");
1608 } else if (key == StyleConstants.Alignment) {
1609 int align = ((Integer) value).intValue();
1610 String ta;
1611 switch (align) {
1612 case StyleConstants.ALIGN_LEFT:
1613 ta = "left";
1614 break;
1615 case StyleConstants.ALIGN_RIGHT:
1616 ta = "right";
1617 break;
1618 case StyleConstants.ALIGN_CENTER:
1619 ta = "center";
1620 break;
1621 case StyleConstants.ALIGN_JUSTIFIED:
1622 ta = "justify";
1623 break;
1624 default:
1625 ta = "left";
1626 }
1627 return parseCssValue(ta);
1628 } else if (key == StyleConstants.StrikeThrough) {
1629 if (value.equals(Boolean.TRUE)) {
1630 return parseCssValue("line-through");
1631 }
1632 return parseCssValue("");
1633 } else if (key == StyleConstants.Superscript) {
1634 if (value.equals(Boolean.TRUE)) {
1635 return parseCssValue("super");
1636 }
1637 return parseCssValue("");
1638 } else if (key == StyleConstants.Subscript) {
1639 if (value.equals(Boolean.TRUE)) {
1640 return parseCssValue("sub");
1641 }
1642 return parseCssValue("");
1643 }
1644 return null;
1645 }
1646
1647 /**
1648 * Converts a CSS attribute value to a
1649 * <code>StyleConstants</code> value.
1650 * If there is no conversion, returns <code>null</code>.
1651 * By default, there is no conversion.
1652 *
1653 * @param key the <code>StyleConstants</code> attribute
1654 * @return the <code>StyleConstants</code> attribute value that
1655 * represents the CSS attribute value
1656 */
1657 Object toStyleConstants(StyleConstants key, View v) {
1658 if (key == StyleConstants.Italic) {
1659 if (svalue.indexOf("italic") >= 0) {
1660 return Boolean.TRUE;
1661 }
1662 return Boolean.FALSE;
1663 } else if (key == StyleConstants.Underline) {
1664 if (svalue.indexOf("underline") >= 0) {
1665 return Boolean.TRUE;
1666 }
1667 return Boolean.FALSE;
1668 } else if (key == StyleConstants.Alignment) {
1669 if (svalue.equals("right")) {
1670 return new Integer(StyleConstants.ALIGN_RIGHT);
1671 } else if (svalue.equals("center")) {
1672 return new Integer(StyleConstants.ALIGN_CENTER);
1673 } else if (svalue.equals("justify")) {
1674 return new Integer(StyleConstants.ALIGN_JUSTIFIED);
1675 }
1676 return new Integer(StyleConstants.ALIGN_LEFT);
1677 } else if (key == StyleConstants.StrikeThrough) {
1678 if (svalue.indexOf("line-through") >= 0) {
1679 return Boolean.TRUE;
1680 }
1681 return Boolean.FALSE;
1682 } else if (key == StyleConstants.Superscript) {
1683 if (svalue.indexOf("super") >= 0) {
1684 return Boolean.TRUE;
1685 }
1686 return Boolean.FALSE;
1687 } else if (key == StyleConstants.Subscript) {
1688 if (svalue.indexOf("sub") >= 0) {
1689 return Boolean.TRUE;
1690 }
1691 return Boolean.FALSE;
1692 }
1693 return null;
1694 }
1695
1696 // Used by ViewAttributeSet
1697 boolean isItalic() {
1698 return (svalue.indexOf("italic") != -1);
1699 }
1700
1701 boolean isStrike() {
1702 return (svalue.indexOf("line-through") != -1);
1703 }
1704
1705 boolean isUnderline() {
1706 return (svalue.indexOf("underline") != -1);
1707 }
1708
1709 boolean isSub() {
1710 return (svalue.indexOf("sub") != -1);
1711 }
1712
1713 boolean isSup() {
1714 return (svalue.indexOf("sup") != -1);
1715 }
1716 }
1717
1718 /**
1719 * Represents a value for the CSS.FONT_SIZE attribute.
1720 * The binary format of the value can be one of several
1721 * types. If the type is Float,
1722 * the value is specified in terms of point or
1723 * percentage, depending upon the ending of the
1724 * associated string.
1725 * If the type is Integer, the value is specified
1726 * in terms of a size index.
1727 */
1728 class FontSize extends CssValue {
1729
1730 /**
1731 * Returns the size in points. This is ultimately
1732 * what we need for the purpose of creating/fetching
1733 * a Font object.
1734 *
1735 * @param a the attribute set the value is being
1736 * requested from. We may need to walk up the
1737 * resolve hierarchy if it's relative.
1738 */
1739 int getValue(AttributeSet a, StyleSheet ss) {
1740 ss = getStyleSheet(ss);
1741 if (index) {
1742 // it's an index, translate from size table
1743 return Math.round(getPointSize((int) value, ss));
1744 } else if (lu == null) {
1745 return Math.round(value);
1746 } else {
1747 if (lu.type == 0) {
1748 boolean isW3CLengthUnits = (ss == null) ? false
1749 : ss.isW3CLengthUnits();
1750 return Math.round(lu.getValue(isW3CLengthUnits));
1751 }
1752 if (a != null) {
1753 AttributeSet resolveParent = a.getResolveParent();
1754
1755 if (resolveParent != null) {
1756 int pValue = StyleConstants
1757 .getFontSize(resolveParent);
1758
1759 float retValue;
1760 if (lu.type == 1 || lu.type == 3) {
1761 retValue = lu.value * (float) pValue;
1762 } else {
1763 retValue = lu.value + (float) pValue;
1764 }
1765 return Math.round(retValue);
1766 }
1767 }
1768 // a is null, or no resolve parent.
1769 return 12;
1770 }
1771 }
1772
1773 Object parseCssValue(String value) {
1774 FontSize fs = new FontSize();
1775 fs.svalue = value;
1776 try {
1777 if (value.equals("xx-small")) {
1778 fs.value = 1;
1779 fs.index = true;
1780 } else if (value.equals("x-small")) {
1781 fs.value = 2;
1782 fs.index = true;
1783 } else if (value.equals("small")) {
1784 fs.value = 3;
1785 fs.index = true;
1786 } else if (value.equals("medium")) {
1787 fs.value = 4;
1788 fs.index = true;
1789 } else if (value.equals("large")) {
1790 fs.value = 5;
1791 fs.index = true;
1792 } else if (value.equals("x-large")) {
1793 fs.value = 6;
1794 fs.index = true;
1795 } else if (value.equals("xx-large")) {
1796 fs.value = 7;
1797 fs.index = true;
1798 } else {
1799 fs.lu = new LengthUnit(value, (short) 1, 1f);
1800 }
1801 // relative sizes, larger | smaller (adjust from parent by
1802 // 1.5 pixels)
1803 // em, ex refer to parent sizes
1804 // lengths: pt, mm, cm, pc, in, px
1805 // em (font height 3em would be 3 times font height)
1806 // ex (height of X)
1807 // lengths are (+/-) followed by a number and two letter
1808 // unit identifier
1809 } catch (NumberFormatException nfe) {
1810 fs = null;
1811 }
1812 return fs;
1813 }
1814
1815 Object parseHtmlValue(String value) {
1816 if ((value == null) || (value.length() == 0)) {
1817 return null;
1818 }
1819 FontSize fs = new FontSize();
1820 fs.svalue = value;
1821
1822 try {
1823 /*
1824 * relative sizes in the size attribute are relative
1825 * to the <basefont>'s size.
1826 */
1827 int baseFontSize = getBaseFontSize();
1828 if (value.charAt(0) == '+') {
1829 int relSize = Integer.valueOf(value.substring(1))
1830 .intValue();
1831 fs.value = baseFontSize + relSize;
1832 fs.index = true;
1833 } else if (value.charAt(0) == '-') {
1834 int relSize = -Integer.valueOf(value.substring(1))
1835 .intValue();
1836 fs.value = baseFontSize + relSize;
1837 fs.index = true;
1838 } else {
1839 fs.value = Integer.parseInt(value);
1840 if (fs.value > 7) {
1841 fs.value = 7;
1842 } else if (fs.value < 0) {
1843 fs.value = 0;
1844 }
1845 fs.index = true;
1846 }
1847
1848 } catch (NumberFormatException nfe) {
1849 fs = null;
1850 }
1851 return fs;
1852 }
1853
1854 /**
1855 * Converts a <code>StyleConstants</code> attribute value to
1856 * a CSS attribute value. If there is no conversion
1857 * returns <code>null</code>. By default, there is no conversion.
1858 *
1859 * @param key the <code>StyleConstants</code> attribute
1860 * @param value the value of a <code>StyleConstants</code>
1861 * attribute to be converted
1862 * @return the CSS value that represents the
1863 * <code>StyleConstants</code> value
1864 */
1865 Object fromStyleConstants(StyleConstants key, Object value) {
1866 if (value instanceof Number) {
1867 FontSize fs = new FontSize();
1868
1869 fs.value = getIndexOfSize(
1870 ((Number) value).floatValue(),
1871 StyleSheet.sizeMapDefault);
1872 fs.svalue = Integer.toString((int) fs.value);
1873 fs.index = true;
1874 return fs;
1875 }
1876 return parseCssValue(value.toString());
1877 }
1878
1879 /**
1880 * Converts a CSS attribute value to a <code>StyleConstants</code>
1881 * value. If there is no conversion, returns <code>null</code>.
1882 * By default, there is no conversion.
1883 *
1884 * @param key the <code>StyleConstants</code> attribute
1885 * @return the <code>StyleConstants</code> attribute value that
1886 * represents the CSS attribute value
1887 */
1888 Object toStyleConstants(StyleConstants key, View v) {
1889 if (v != null) {
1890 return Integer
1891 .valueOf(getValue(v.getAttributes(), null));
1892 }
1893 return Integer.valueOf(getValue(null, null));
1894 }
1895
1896 float value;
1897 boolean index;
1898 LengthUnit lu;
1899 }
1900
1901 static class FontFamily extends CssValue {
1902
1903 /**
1904 * Returns the font family to use.
1905 */
1906 String getValue() {
1907 return family;
1908 }
1909
1910 Object parseCssValue(String value) {
1911 int cIndex = value.indexOf(',');
1912 FontFamily ff = new FontFamily();
1913 ff.svalue = value;
1914 ff.family = null;
1915
1916 if (cIndex == -1) {
1917 setFontName(ff, value);
1918 } else {
1919 boolean done = false;
1920 int lastIndex;
1921 int length = value.length();
1922 cIndex = 0;
1923 while (!done) {
1924 // skip ws.
1925 while (cIndex < length
1926 && Character.isWhitespace(value
1927 .charAt(cIndex)))
1928 cIndex++;
1929 // Find next ','
1930 lastIndex = cIndex;
1931 cIndex = value.indexOf(',', cIndex);
1932 if (cIndex == -1) {
1933 cIndex = length;
1934 }
1935 if (lastIndex < length) {
1936 if (lastIndex != cIndex) {
1937 int lastCharIndex = cIndex;
1938 if (cIndex > 0
1939 && value.charAt(cIndex - 1) == ' ') {
1940 lastCharIndex--;
1941 }
1942 setFontName(ff, value.substring(lastIndex,
1943 lastCharIndex));
1944 done = (ff.family != null);
1945 }
1946 cIndex++;
1947 } else {
1948 done = true;
1949 }
1950 }
1951 }
1952 if (ff.family == null) {
1953 ff.family = Font.SANS_SERIF;
1954 }
1955 return ff;
1956 }
1957
1958 private void setFontName(FontFamily ff, String fontName) {
1959 ff.family = fontName;
1960 }
1961
1962 Object parseHtmlValue(String value) {
1963 // TBD
1964 return parseCssValue(value);
1965 }
1966
1967 /**
1968 * Converts a <code>StyleConstants</code> attribute value to
1969 * a CSS attribute value. If there is no conversion
1970 * returns <code>null</code>. By default, there is no conversion.
1971 *
1972 * @param key the <code>StyleConstants</code> attribute
1973 * @param value the value of a <code>StyleConstants</code>
1974 * attribute to be converted
1975 * @return the CSS value that represents the
1976 * <code>StyleConstants</code> value
1977 */
1978 Object fromStyleConstants(StyleConstants key, Object value) {
1979 return parseCssValue(value.toString());
1980 }
1981
1982 /**
1983 * Converts a CSS attribute value to a <code>StyleConstants</code>
1984 * value. If there is no conversion, returns <code>null</code>.
1985 * By default, there is no conversion.
1986 *
1987 * @param key the <code>StyleConstants</code> attribute
1988 * @return the <code>StyleConstants</code> attribute value that
1989 * represents the CSS attribute value
1990 */
1991 Object toStyleConstants(StyleConstants key, View v) {
1992 return family;
1993 }
1994
1995 String family;
1996 }
1997
1998 static class FontWeight extends CssValue {
1999
2000 int getValue() {
2001 return weight;
2002 }
2003
2004 Object parseCssValue(String value) {
2005 FontWeight fw = new FontWeight();
2006 fw.svalue = value;
2007 if (value.equals("bold")) {
2008 fw.weight = 700;
2009 } else if (value.equals("normal")) {
2010 fw.weight = 400;
2011 } else {
2012 // PENDING(prinz) add support for relative values
2013 try {
2014 fw.weight = Integer.parseInt(value);
2015 } catch (NumberFormatException nfe) {
2016 fw = null;
2017 }
2018 }
2019 return fw;
2020 }
2021
2022 /**
2023 * Converts a <code>StyleConstants</code> attribute value to
2024 * a CSS attribute value. If there is no conversion
2025 * returns <code>null</code>. By default, there is no conversion.
2026 *
2027 * @param key the <code>StyleConstants</code> attribute
2028 * @param value the value of a <code>StyleConstants</code>
2029 * attribute to be converted
2030 * @return the CSS value that represents the
2031 * <code>StyleConstants</code> value
2032 */
2033 Object fromStyleConstants(StyleConstants key, Object value) {
2034 if (value.equals(Boolean.TRUE)) {
2035 return parseCssValue("bold");
2036 }
2037 return parseCssValue("normal");
2038 }
2039
2040 /**
2041 * Converts a CSS attribute value to a <code>StyleConstants</code>
2042 * value. If there is no conversion, returns <code>null</code>.
2043 * By default, there is no conversion.
2044 *
2045 * @param key the <code>StyleConstants</code> attribute
2046 * @return the <code>StyleConstants</code> attribute value that
2047 * represents the CSS attribute value
2048 */
2049 Object toStyleConstants(StyleConstants key, View v) {
2050 return (weight > 500) ? Boolean.TRUE : Boolean.FALSE;
2051 }
2052
2053 boolean isBold() {
2054 return (weight > 500);
2055 }
2056
2057 int weight;
2058 }
2059
2060 static class ColorValue extends CssValue {
2061
2062 /**
2063 * Returns the color to use.
2064 */
2065 Color getValue() {
2066 return c;
2067 }
2068
2069 Object parseCssValue(String value) {
2070
2071 Color c = stringToColor(value);
2072 if (c != null) {
2073 ColorValue cv = new ColorValue();
2074 cv.svalue = value;
2075 cv.c = c;
2076 return cv;
2077 }
2078 return null;
2079 }
2080
2081 Object parseHtmlValue(String value) {
2082 return parseCssValue(value);
2083 }
2084
2085 /**
2086 * Converts a <code>StyleConstants</code> attribute value to
2087 * a CSS attribute value. If there is no conversion
2088 * returns <code>null</code>. By default, there is no conversion.
2089 *
2090 * @param key the <code>StyleConstants</code> attribute
2091 * @param value the value of a <code>StyleConstants</code>
2092 * attribute to be converted
2093 * @return the CSS value that represents the
2094 * <code>StyleConstants</code> value
2095 */
2096 Object fromStyleConstants(StyleConstants key, Object value) {
2097 ColorValue colorValue = new ColorValue();
2098 colorValue.c = (Color) value;
2099 colorValue.svalue = colorToHex(colorValue.c);
2100 return colorValue;
2101 }
2102
2103 /**
2104 * Converts a CSS attribute value to a <code>StyleConstants</code>
2105 * value. If there is no conversion, returns <code>null</code>.
2106 * By default, there is no conversion.
2107 *
2108 * @param key the <code>StyleConstants</code> attribute
2109 * @return the <code>StyleConstants</code> attribute value that
2110 * represents the CSS attribute value
2111 */
2112 Object toStyleConstants(StyleConstants key, View v) {
2113 return c;
2114 }
2115
2116 Color c;
2117 }
2118
2119 static class BorderStyle extends CssValue {
2120
2121 CSS.Value getValue() {
2122 return style;
2123 }
2124
2125 Object parseCssValue(String value) {
2126 CSS.Value cssv = CSS.getValue(value);
2127 if (cssv != null) {
2128 if ((cssv == CSS.Value.INSET)
2129 || (cssv == CSS.Value.OUTSET)
2130 || (cssv == CSS.Value.NONE)
2131 || (cssv == CSS.Value.DOTTED)
2132 || (cssv == CSS.Value.DASHED)
2133 || (cssv == CSS.Value.SOLID)
2134 || (cssv == CSS.Value.DOUBLE)
2135 || (cssv == CSS.Value.GROOVE)
2136 || (cssv == CSS.Value.RIDGE)) {
2137
2138 BorderStyle bs = new BorderStyle();
2139 bs.svalue = value;
2140 bs.style = cssv;
2141 return bs;
2142 }
2143 }
2144 return null;
2145 }
2146
2147 private void writeObject(java.io.ObjectOutputStream s)
2148 throws IOException {
2149 s.defaultWriteObject();
2150 if (style == null) {
2151 s.writeObject(null);
2152 } else {
2153 s.writeObject(style.toString());
2154 }
2155 }
2156
2157 private void readObject(ObjectInputStream s)
2158 throws ClassNotFoundException, IOException {
2159 s.defaultReadObject();
2160 Object value = s.readObject();
2161 if (value != null) {
2162 style = CSS.getValue((String) value);
2163 }
2164 }
2165
2166 // CSS.Values are static, don't archive it.
2167 transient private CSS.Value style;
2168 }
2169
2170 static class LengthValue extends CssValue {
2171
2172 /**
2173 * if this length value may be negative.
2174 */
2175 boolean mayBeNegative;
2176
2177 LengthValue() {
2178 this (false);
2179 }
2180
2181 LengthValue(boolean mayBeNegative) {
2182 this .mayBeNegative = mayBeNegative;
2183 }
2184
2185 /**
2186 * Returns the length (span) to use.
2187 */
2188 float getValue() {
2189 return getValue(false);
2190 }
2191
2192 float getValue(boolean isW3CLengthUnits) {
2193 return getValue(0, isW3CLengthUnits);
2194 }
2195
2196 /**
2197 * Returns the length (span) to use. If the value represents
2198 * a percentage, it is scaled based on <code>currentValue</code>.
2199 */
2200 float getValue(float currentValue) {
2201 return getValue(currentValue, false);
2202 }
2203
2204 float getValue(float currentValue, boolean isW3CLengthUnits) {
2205 if (percentage) {
2206 return span * currentValue;
2207 }
2208 return LengthUnit.getValue(span, units, isW3CLengthUnits);
2209 }
2210
2211 /**
2212 * Returns true if the length represents a percentage of the
2213 * containing box.
2214 */
2215 boolean isPercentage() {
2216 return percentage;
2217 }
2218
2219 Object parseCssValue(String value) {
2220 LengthValue lv;
2221 try {
2222 // Assume pixels
2223 float absolute = Float.valueOf(value).floatValue();
2224 lv = new LengthValue();
2225 lv.span = absolute;
2226 } catch (NumberFormatException nfe) {
2227 // Not pixels, use LengthUnit
2228 LengthUnit lu = new LengthUnit(value,
2229 LengthUnit.UNINITALIZED_LENGTH, 0);
2230
2231 // PENDING: currently, we only support absolute values and
2232 // percentages.
2233 switch (lu.type) {
2234 case 0:
2235 // Absolute
2236 lv = new LengthValue();
2237 lv.span = (mayBeNegative) ? lu.value : Math.max(0,
2238 lu.value);
2239 lv.units = lu.units;
2240 break;
2241 case 1:
2242 // %
2243 lv = new LengthValue();
2244 lv.span = Math.max(0, Math.min(1, lu.value));
2245 lv.percentage = true;
2246 break;
2247 default:
2248 return null;
2249 }
2250 }
2251 lv.svalue = value;
2252 return lv;
2253 }
2254
2255 Object parseHtmlValue(String value) {
2256 if (value.equals(HTML.NULL_ATTRIBUTE_VALUE)) {
2257 value = "1";
2258 }
2259 return parseCssValue(value);
2260 }
2261
2262 /**
2263 * Converts a <code>StyleConstants</code> attribute value to
2264 * a CSS attribute value. If there is no conversion,
2265 * returns <code>null</code>. By default, there is no conversion.
2266 *
2267 * @param key the <code>StyleConstants</code> attribute
2268 * @param value the value of a <code>StyleConstants</code>
2269 * attribute to be converted
2270 * @return the CSS value that represents the
2271 * <code>StyleConstants</code> value
2272 */
2273 Object fromStyleConstants(StyleConstants key, Object value) {
2274 LengthValue v = new LengthValue();
2275 v.svalue = value.toString();
2276 v.span = ((Float) value).floatValue();
2277 return v;
2278 }
2279
2280 /**
2281 * Converts a CSS attribute value to a <code>StyleConstants</code>
2282 * value. If there is no conversion, returns <code>null</code>.
2283 * By default, there is no conversion.
2284 *
2285 * @param key the <code>StyleConstants</code> attribute
2286 * @return the <code>StyleConstants</code> attribute value that
2287 * represents the CSS attribute value
2288 */
2289 Object toStyleConstants(StyleConstants key, View v) {
2290 return new Float(getValue(false));
2291 }
2292
2293 /** If true, span is a percentage value, and that to determine
2294 * the length another value needs to be passed in. */
2295 boolean percentage;
2296 /** Either the absolute value (percentage == false) or
2297 * a percentage value. */
2298 float span;
2299
2300 String units = null;
2301 }
2302
2303 /**
2304 * BorderWidthValue is used to model BORDER_XXX_WIDTH and adds support
2305 * for the thin/medium/thick values.
2306 */
2307 static class BorderWidthValue extends LengthValue {
2308 BorderWidthValue(String svalue, int index) {
2309 this .svalue = svalue;
2310 span = values[index];
2311 percentage = false;
2312 }
2313
2314 Object parseCssValue(String value) {
2315 if (value != null) {
2316 if (value.equals("thick")) {
2317 return new BorderWidthValue(value, 2);
2318 } else if (value.equals("medium")) {
2319 return new BorderWidthValue(value, 1);
2320 } else if (value.equals("thin")) {
2321 return new BorderWidthValue(value, 0);
2322 }
2323 }
2324 // Assume its a length.
2325 return super .parseCssValue(value);
2326 }
2327
2328 Object parseHtmlValue(String value) {
2329 if (value == HTML.NULL_ATTRIBUTE_VALUE) {
2330 return parseCssValue("medium");
2331 }
2332 return parseCssValue(value);
2333 }
2334
2335 /** Values used to represent border width. */
2336 private static final float[] values = { 1, 2, 4 };
2337 }
2338
2339 /**
2340 * Handles uniquing of CSS values, like lists, and background image
2341 * repeating.
2342 */
2343 static class CssValueMapper extends CssValue {
2344 Object parseCssValue(String value) {
2345 Object retValue = cssValueToInternalValueMap.get(value);
2346 if (retValue == null) {
2347 retValue = cssValueToInternalValueMap.get(value
2348 .toLowerCase());
2349 }
2350 return retValue;
2351 }
2352
2353 Object parseHtmlValue(String value) {
2354 Object retValue = htmlValueToCssValueMap.get(value);
2355 if (retValue == null) {
2356 retValue = htmlValueToCssValueMap.get(value
2357 .toLowerCase());
2358 }
2359 return retValue;
2360 }
2361 }
2362
2363 /**
2364 * Used for background images, to represent the position.
2365 */
2366 static class BackgroundPosition extends CssValue {
2367 float horizontalPosition;
2368 float verticalPosition;
2369 // bitmask: bit 0, horizontal relative, bit 1 horizontal relative to
2370 // font size, 2 vertical relative to size, 3 vertical relative to
2371 // font size.
2372 //
2373 short relative;
2374
2375 Object parseCssValue(String value) {
2376 // 'top left' and 'left top' both mean the same as '0% 0%'.
2377 // 'top', 'top center' and 'center top' mean the same as '50% 0%'.
2378 // 'right top' and 'top right' mean the same as '100% 0%'.
2379 // 'left', 'left center' and 'center left' mean the same as
2380 // '0% 50%'.
2381 // 'center' and 'center center' mean the same as '50% 50%'.
2382 // 'right', 'right center' and 'center right' mean the same as
2383 // '100% 50%'.
2384 // 'bottom left' and 'left bottom' mean the same as '0% 100%'.
2385 // 'bottom', 'bottom center' and 'center bottom' mean the same as
2386 // '50% 100%'.
2387 // 'bottom right' and 'right bottom' mean the same as '100% 100%'.
2388 String[] strings = CSS.parseStrings(value);
2389 int count = strings.length;
2390 BackgroundPosition bp = new BackgroundPosition();
2391 bp.relative = 5;
2392 bp.svalue = value;
2393
2394 if (count > 0) {
2395 // bit 0 for vert, 1 hor, 2 for center
2396 short found = 0;
2397 int index = 0;
2398 while (index < count) {
2399 // First, check for keywords
2400 String string = strings[index++];
2401 if (string.equals("center")) {
2402 found |= 4;
2403 continue;
2404 } else {
2405 if ((found & 1) == 0) {
2406 if (string.equals("top")) {
2407 found |= 1;
2408 } else if (string.equals("bottom")) {
2409 found |= 1;
2410 bp.verticalPosition = 1;
2411 continue;
2412 }
2413 }
2414 if ((found & 2) == 0) {
2415 if (string.equals("left")) {
2416 found |= 2;
2417 bp.horizontalPosition = 0;
2418 } else if (string.equals("right")) {
2419 found |= 2;
2420 bp.horizontalPosition = 1;
2421 }
2422 }
2423 }
2424 }
2425 if (found != 0) {
2426 if ((found & 1) == 1) {
2427 if ((found & 2) == 0) {
2428 // vert and no horiz.
2429 bp.horizontalPosition = .5f;
2430 }
2431 } else if ((found & 2) == 2) {
2432 // horiz and no vert.
2433 bp.verticalPosition = .5f;
2434 } else {
2435 // no horiz, no vert, but center
2436 bp.horizontalPosition = bp.verticalPosition = .5f;
2437 }
2438 } else {
2439 // Assume lengths
2440 LengthUnit lu = new LengthUnit(strings[0],
2441 (short) 0, 0f);
2442
2443 if (lu.type == 0) {
2444 bp.horizontalPosition = lu.value;
2445 bp.relative = (short) (1 ^ bp.relative);
2446 } else if (lu.type == 1) {
2447 bp.horizontalPosition = lu.value;
2448 } else if (lu.type == 3) {
2449 bp.horizontalPosition = lu.value;
2450 bp.relative = (short) ((1 ^ bp.relative) | 2);
2451 }
2452 if (count > 1) {
2453 lu = new LengthUnit(strings[1], (short) 0, 0f);
2454
2455 if (lu.type == 0) {
2456 bp.verticalPosition = lu.value;
2457 bp.relative = (short) (4 ^ bp.relative);
2458 } else if (lu.type == 1) {
2459 bp.verticalPosition = lu.value;
2460 } else if (lu.type == 3) {
2461 bp.verticalPosition = lu.value;
2462 bp.relative = (short) ((4 ^ bp.relative) | 8);
2463 }
2464 } else {
2465 bp.verticalPosition = .5f;
2466 }
2467 }
2468 }
2469 return bp;
2470 }
2471
2472 boolean isHorizontalPositionRelativeToSize() {
2473 return ((relative & 1) == 1);
2474 }
2475
2476 boolean isHorizontalPositionRelativeToFontSize() {
2477 return ((relative & 2) == 2);
2478 }
2479
2480 float getHorizontalPosition() {
2481 return horizontalPosition;
2482 }
2483
2484 boolean isVerticalPositionRelativeToSize() {
2485 return ((relative & 4) == 4);
2486 }
2487
2488 boolean isVerticalPositionRelativeToFontSize() {
2489 return ((relative & 8) == 8);
2490 }
2491
2492 float getVerticalPosition() {
2493 return verticalPosition;
2494 }
2495 }
2496
2497 /**
2498 * Used for BackgroundImages.
2499 */
2500 static class BackgroundImage extends CssValue {
2501 private boolean loadedImage;
2502 private ImageIcon image;
2503
2504 Object parseCssValue(String value) {
2505 BackgroundImage retValue = new BackgroundImage();
2506 retValue.svalue = value;
2507 return retValue;
2508 }
2509
2510 Object parseHtmlValue(String value) {
2511 return parseCssValue(value);
2512 }
2513
2514 // PENDING: this base is wrong for linked style sheets.
2515 ImageIcon getImage(URL base) {
2516 if (!loadedImage) {
2517 synchronized (this ) {
2518 if (!loadedImage) {
2519 URL url = CSS.getURL(base, svalue);
2520 loadedImage = true;
2521 if (url != null) {
2522 image = new ImageIcon();
2523 Image tmpImg = Toolkit.getDefaultToolkit()
2524 .createImage(url);
2525 if (tmpImg != null) {
2526 image.setImage(tmpImg);
2527 }
2528 }
2529 }
2530 }
2531 }
2532 return image;
2533 }
2534 }
2535
2536 /**
2537 * Parses a length value, this is used internally, and never added
2538 * to an AttributeSet or returned to the developer.
2539 */
2540 static class LengthUnit implements Serializable {
2541 static Hashtable lengthMapping = new Hashtable(6);
2542 static Hashtable w3cLengthMapping = new Hashtable(6);
2543 static {
2544 lengthMapping.put("pt", new Float(1f));
2545 // Not sure about 1.3, determined by experiementation.
2546 lengthMapping.put("px", new Float(1.3f));
2547 lengthMapping.put("mm", new Float(2.83464f));
2548 lengthMapping.put("cm", new Float(28.3464f));
2549 lengthMapping.put("pc", new Float(12f));
2550 lengthMapping.put("in", new Float(72f));
2551 int res = 72;
2552 try {
2553 res = Toolkit.getDefaultToolkit().getScreenResolution();
2554 } catch (HeadlessException e) {
2555 }
2556 // mapping according to the CSS2 spec
2557 w3cLengthMapping.put("pt", new Float(res / 72f));
2558 w3cLengthMapping.put("px", new Float(1f));
2559 w3cLengthMapping.put("mm", new Float(res / 25.4f));
2560 w3cLengthMapping.put("cm", new Float(res / 2.54f));
2561 w3cLengthMapping.put("pc", new Float(res / 6f));
2562 w3cLengthMapping.put("in", new Float(res));
2563 }
2564
2565 LengthUnit(String value, short defaultType, float defaultValue) {
2566 parse(value, defaultType, defaultValue);
2567 }
2568
2569 void parse(String value, short defaultType, float defaultValue) {
2570 type = defaultType;
2571 this .value = defaultValue;
2572
2573 int length = value.length();
2574 if (length > 0 && value.charAt(length - 1) == '%') {
2575 try {
2576 this .value = Float.valueOf(
2577 value.substring(0, length - 1))
2578 .floatValue() / 100.0f;
2579 type = 1;
2580 } catch (NumberFormatException nfe) {
2581 }
2582 }
2583 if (length >= 2) {
2584 units = value.substring(length - 2, length);
2585 Float scale = (Float) lengthMapping.get(units);
2586 if (scale != null) {
2587 try {
2588 this .value = Float.valueOf(
2589 value.substring(0, length - 2))
2590 .floatValue();
2591 type = 0;
2592 } catch (NumberFormatException nfe) {
2593 }
2594 } else if (units.equals("em") || units.equals("ex")) {
2595 try {
2596 this .value = Float.valueOf(
2597 value.substring(0, length - 2))
2598 .floatValue();
2599 type = 3;
2600 } catch (NumberFormatException nfe) {
2601 }
2602 } else if (value.equals("larger")) {
2603 this .value = 2f;
2604 type = 2;
2605 } else if (value.equals("smaller")) {
2606 this .value = -2;
2607 type = 2;
2608 } else {
2609 // treat like points.
2610 try {
2611 this .value = Float.valueOf(value).floatValue();
2612 type = 0;
2613 } catch (NumberFormatException nfe) {
2614 }
2615 }
2616 } else if (length > 0) {
2617 // treat like points.
2618 try {
2619 this .value = Float.valueOf(value).floatValue();
2620 type = 0;
2621 } catch (NumberFormatException nfe) {
2622 }
2623 }
2624 }
2625
2626 float getValue(boolean w3cLengthUnits) {
2627 Hashtable mapping = (w3cLengthUnits) ? w3cLengthMapping
2628 : lengthMapping;
2629 float scale = 1;
2630 if (units != null) {
2631 Float scaleFloat = (Float) mapping.get(units);
2632 if (scaleFloat != null) {
2633 scale = scaleFloat.floatValue();
2634 }
2635 }
2636 return this .value * scale;
2637
2638 }
2639
2640 static float getValue(float value, String units,
2641 Boolean w3cLengthUnits) {
2642 Hashtable mapping = (w3cLengthUnits) ? w3cLengthMapping
2643 : lengthMapping;
2644 float scale = 1;
2645 if (units != null) {
2646 Float scaleFloat = (Float) mapping.get(units);
2647 if (scaleFloat != null) {
2648 scale = scaleFloat.floatValue();
2649 }
2650 }
2651 return value * scale;
2652 }
2653
2654 public String toString() {
2655 return type + " " + value;
2656 }
2657
2658 // 0 - value indicates real value
2659 // 1 - % value, value relative to depends upon key.
2660 // 50% will have a value = .5
2661 // 2 - add value to parent value.
2662 // 3 - em/ex relative to font size of element (except for
2663 // font-size, which is relative to parent).
2664 short type;
2665 float value;
2666 String units = null;
2667
2668 static final short UNINITALIZED_LENGTH = (short) 10;
2669 }
2670
2671 /**
2672 * Class used to parse font property. The font property is shorthand
2673 * for the other font properties. This expands the properties, placing
2674 * them in the attributeset.
2675 */
2676 static class ShorthandFontParser {
2677 /**
2678 * Parses the shorthand font string <code>value</code>, placing the
2679 * result in <code>attr</code>.
2680 */
2681 static void parseShorthandFont(CSS css, String value,
2682 MutableAttributeSet attr) {
2683 // font is of the form:
2684 // [ <font-style> || <font-variant> || <font-weight> ]? <font-size>
2685 // [ / <line-height> ]? <font-family>
2686 String[] strings = CSS.parseStrings(value);
2687 int count = strings.length;
2688 int index = 0;
2689 // bitmask, 1 for style, 2 for variant, 3 for weight
2690 short found = 0;
2691 int maxC = Math.min(3, count);
2692
2693 // Check for font-style font-variant font-weight
2694 while (index < maxC) {
2695 if ((found & 1) == 0 && isFontStyle(strings[index])) {
2696 css.addInternalCSSValue(attr,
2697 CSS.Attribute.FONT_STYLE, strings[index++]);
2698 found |= 1;
2699 } else if ((found & 2) == 0
2700 && isFontVariant(strings[index])) {
2701 css.addInternalCSSValue(attr,
2702 CSS.Attribute.FONT_VARIANT,
2703 strings[index++]);
2704 found |= 2;
2705 } else if ((found & 4) == 0
2706 && isFontWeight(strings[index])) {
2707 css
2708 .addInternalCSSValue(attr,
2709 CSS.Attribute.FONT_WEIGHT,
2710 strings[index++]);
2711 found |= 4;
2712 } else if (strings[index].equals("normal")) {
2713 index++;
2714 } else {
2715 break;
2716 }
2717 }
2718 if ((found & 1) == 0) {
2719 css.addInternalCSSValue(attr, CSS.Attribute.FONT_STYLE,
2720 "normal");
2721 }
2722 if ((found & 2) == 0) {
2723 css.addInternalCSSValue(attr,
2724 CSS.Attribute.FONT_VARIANT, "normal");
2725 }
2726 if ((found & 4) == 0) {
2727 css.addInternalCSSValue(attr,
2728 CSS.Attribute.FONT_WEIGHT, "normal");
2729 }
2730
2731 // string at index should be the font-size
2732 if (index < count) {
2733 String fontSize = strings[index];
2734 int slashIndex = fontSize.indexOf('/');
2735
2736 if (slashIndex != -1) {
2737 fontSize = fontSize.substring(0, slashIndex);
2738 strings[index] = strings[index]
2739 .substring(slashIndex);
2740 } else {
2741 index++;
2742 }
2743 css.addInternalCSSValue(attr, CSS.Attribute.FONT_SIZE,
2744 fontSize);
2745 } else {
2746 css.addInternalCSSValue(attr, CSS.Attribute.FONT_SIZE,
2747 "medium");
2748 }
2749
2750 // Check for line height
2751 if (index < count && strings[index].startsWith("/")) {
2752 String lineHeight = null;
2753 if (strings[index].equals("/")) {
2754 if (++index < count) {
2755 lineHeight = strings[index++];
2756 }
2757 } else {
2758 lineHeight = strings[index++].substring(1);
2759 }
2760 // line height
2761 if (lineHeight != null) {
2762 css.addInternalCSSValue(attr,
2763 CSS.Attribute.LINE_HEIGHT, lineHeight);
2764 } else {
2765 css.addInternalCSSValue(attr,
2766 CSS.Attribute.LINE_HEIGHT, "normal");
2767 }
2768 } else {
2769 css.addInternalCSSValue(attr,
2770 CSS.Attribute.LINE_HEIGHT, "normal");
2771 }
2772
2773 // remainder of strings are font-family
2774 if (index < count) {
2775 String family = strings[index++];
2776
2777 while (index < count) {
2778 family += " " + strings[index++];
2779 }
2780 css.addInternalCSSValue(attr,
2781 CSS.Attribute.FONT_FAMILY, family);
2782 } else {
2783 css.addInternalCSSValue(attr,
2784 CSS.Attribute.FONT_FAMILY, Font.SANS_SERIF);
2785 }
2786 }
2787
2788 private static boolean isFontStyle(String string) {
2789 return (string.equals("italic") || string.equals("oblique"));
2790 }
2791
2792 private static boolean isFontVariant(String string) {
2793 return (string.equals("small-caps"));
2794 }
2795
2796 private static boolean isFontWeight(String string) {
2797 if (string.equals("bold") || string.equals("bolder")
2798 || string.equals("italic")
2799 || string.equals("lighter")) {
2800 return true;
2801 }
2802 // test for 100-900
2803 return (string.length() == 3 && string.charAt(0) >= '1'
2804 && string.charAt(0) <= '9'
2805 && string.charAt(1) == '0' && string.charAt(2) == '0');
2806 }
2807
2808 }
2809
2810 /**
2811 * Parses the background property into its intrinsic values.
2812 */
2813 static class ShorthandBackgroundParser {
2814 /**
2815 * Parses the shorthand font string <code>value</code>, placing the
2816 * result in <code>attr</code>.
2817 */
2818 static void parseShorthandBackground(CSS css, String value,
2819 MutableAttributeSet attr) {
2820 String[] strings = parseStrings(value);
2821 int count = strings.length;
2822 int index = 0;
2823 // bitmask: 0 for image, 1 repeat, 2 attachment, 3 position,
2824 // 4 color
2825 short found = 0;
2826
2827 while (index < count) {
2828 String string = strings[index++];
2829 if ((found & 1) == 0 && isImage(string)) {
2830 css.addInternalCSSValue(attr,
2831 CSS.Attribute.BACKGROUND_IMAGE, string);
2832 found |= 1;
2833 } else if ((found & 2) == 0 && isRepeat(string)) {
2834 css.addInternalCSSValue(attr,
2835 CSS.Attribute.BACKGROUND_REPEAT, string);
2836 found |= 2;
2837 } else if ((found & 4) == 0 && isAttachment(string)) {
2838 css
2839 .addInternalCSSValue(
2840 attr,
2841 CSS.Attribute.BACKGROUND_ATTACHMENT,
2842 string);
2843 found |= 4;
2844 } else if ((found & 8) == 0 && isPosition(string)) {
2845 if (index < count && isPosition(strings[index])) {
2846 css.addInternalCSSValue(attr,
2847 CSS.Attribute.BACKGROUND_POSITION,
2848 string + " " + strings[index++]);
2849 } else {
2850 css.addInternalCSSValue(attr,
2851 CSS.Attribute.BACKGROUND_POSITION,
2852 string);
2853 }
2854 found |= 8;
2855 } else if ((found & 16) == 0 && isColor(string)) {
2856 css.addInternalCSSValue(attr,
2857 CSS.Attribute.BACKGROUND_COLOR, string);
2858 found |= 16;
2859 }
2860 }
2861 if ((found & 1) == 0) {
2862 css.addInternalCSSValue(attr,
2863 CSS.Attribute.BACKGROUND_IMAGE, null);
2864 }
2865 if ((found & 2) == 0) {
2866 css.addInternalCSSValue(attr,
2867 CSS.Attribute.BACKGROUND_REPEAT, "repeat");
2868 }
2869 if ((found & 4) == 0) {
2870 css.addInternalCSSValue(attr,
2871 CSS.Attribute.BACKGROUND_ATTACHMENT, "scroll");
2872 }
2873 if ((found & 8) == 0) {
2874 css.addInternalCSSValue(attr,
2875 CSS.Attribute.BACKGROUND_POSITION, null);
2876 }
2877 // Currently, there is no good way to express this.
2878 /*
2879 if ((found & 16) == 0) {
2880 css.addInternalCSSValue(attr, CSS.Attribute.BACKGROUND_COLOR,
2881 null);
2882 }
2883 */
2884 }
2885
2886 static boolean isImage(String string) {
2887 return (string.startsWith("url(") && string.endsWith(")"));
2888 }
2889
2890 static boolean isRepeat(String string) {
2891 return (string.equals("repeat-x")
2892 || string.equals("repeat-y")
2893 || string.equals("repeat") || string
2894 .equals("no-repeat"));
2895 }
2896
2897 static boolean isAttachment(String string) {
2898 return (string.equals("fixed") || string.equals("scroll"));
2899 }
2900
2901 static boolean isPosition(String string) {
2902 return (string.equals("top") || string.equals("bottom")
2903 || string.equals("left") || string.equals("right")
2904 || string.equals("center") || (string.length() > 0 && Character
2905 .isDigit(string.charAt(0))));
2906 }
2907
2908 static boolean isColor(String string) {
2909 return (CSS.stringToColor(string) != null);
2910 }
2911 }
2912
2913 /**
2914 * Used to parser margin and padding.
2915 */
2916 static class ShorthandMarginParser {
2917 /**
2918 * Parses the shorthand margin/padding/border string
2919 * <code>value</code>, placing the result in <code>attr</code>.
2920 * <code>names</code> give the 4 instrinsic property names.
2921 */
2922 static void parseShorthandMargin(CSS css, String value,
2923 MutableAttributeSet attr, CSS.Attribute[] names) {
2924 String[] strings = parseStrings(value);
2925 int count = strings.length;
2926 int index = 0;
2927 switch (count) {
2928 case 0:
2929 // empty string
2930 return;
2931 case 1:
2932 // Identifies all values.
2933 for (int counter = 0; counter < 4; counter++) {
2934 css.addInternalCSSValue(attr, names[counter],
2935 strings[0]);
2936 }
2937 break;
2938 case 2:
2939 // 0 & 2 = strings[0], 1 & 3 = strings[1]
2940 css.addInternalCSSValue(attr, names[0], strings[0]);
2941 css.addInternalCSSValue(attr, names[2], strings[0]);
2942 css.addInternalCSSValue(attr, names[1], strings[1]);
2943 css.addInternalCSSValue(attr, names[3], strings[1]);
2944 break;
2945 case 3:
2946 css.addInternalCSSValue(attr, names[0], strings[0]);
2947 css.addInternalCSSValue(attr, names[1], strings[1]);
2948 css.addInternalCSSValue(attr, names[2], strings[2]);
2949 css.addInternalCSSValue(attr, names[3], strings[1]);
2950 break;
2951 default:
2952 for (int counter = 0; counter < 4; counter++) {
2953 css.addInternalCSSValue(attr, names[counter],
2954 strings[counter]);
2955 }
2956 break;
2957 }
2958 }
2959 }
2960
2961 /**
2962 * Calculate the requirements needed to tile the requirements
2963 * given by the iterator that would be tiled. The calculation
2964 * takes into consideration margin and border spacing.
2965 */
2966 static SizeRequirements calculateTiledRequirements(
2967 LayoutIterator iter, SizeRequirements r) {
2968 long minimum = 0;
2969 long maximum = 0;
2970 long preferred = 0;
2971 int lastMargin = 0;
2972 int totalSpacing = 0;
2973 int n = iter.getCount();
2974 for (int i = 0; i < n; i++) {
2975 iter.setIndex(i);
2976 int margin0 = lastMargin;
2977 int margin1 = (int) iter.getLeadingCollapseSpan();
2978 totalSpacing += Math.max(margin0, margin1);
2979 ;
2980 preferred += (int) iter.getPreferredSpan(0);
2981 minimum += iter.getMinimumSpan(0);
2982 maximum += iter.getMaximumSpan(0);
2983
2984 lastMargin = (int) iter.getTrailingCollapseSpan();
2985 }
2986 totalSpacing += lastMargin;
2987 totalSpacing += 2 * iter.getBorderWidth();
2988
2989 // adjust for the spacing area
2990 minimum += totalSpacing;
2991 preferred += totalSpacing;
2992 maximum += totalSpacing;
2993
2994 // set return value
2995 if (r == null) {
2996 r = new SizeRequirements();
2997 }
2998 r.minimum = (minimum > Integer.MAX_VALUE) ? Integer.MAX_VALUE
2999 : (int) minimum;
3000 r.preferred = (preferred > Integer.MAX_VALUE) ? Integer.MAX_VALUE
3001 : (int) preferred;
3002 r.maximum = (maximum > Integer.MAX_VALUE) ? Integer.MAX_VALUE
3003 : (int) maximum;
3004 return r;
3005 }
3006
3007 /**
3008 * Calculate a tiled layout for the given iterator.
3009 * This should be done collapsing the neighboring
3010 * margins to be a total of the maximum of the two
3011 * neighboring margin areas as described in the CSS spec.
3012 */
3013 static void calculateTiledLayout(LayoutIterator iter, int targetSpan) {
3014
3015 /*
3016 * first pass, calculate the preferred sizes, adjustments needed because
3017 * of margin collapsing, and the flexibility to adjust the sizes.
3018 */
3019 long preferred = 0;
3020 long currentPreferred = 0;
3021 int lastMargin = 0;
3022 int totalSpacing = 0;
3023 int n = iter.getCount();
3024 int adjustmentWeightsCount = LayoutIterator.WorstAdjustmentWeight + 1;
3025 //max gain we can get adjusting elements with adjustmentWeight <= i
3026 long gain[] = new long[adjustmentWeightsCount];
3027 //max loss we can get adjusting elements with adjustmentWeight <= i
3028 long loss[] = new long[adjustmentWeightsCount];
3029
3030 for (int i = 0; i < adjustmentWeightsCount; i++) {
3031 gain[i] = loss[i] = 0;
3032 }
3033 for (int i = 0; i < n; i++) {
3034 iter.setIndex(i);
3035 int margin0 = lastMargin;
3036 int margin1 = (int) iter.getLeadingCollapseSpan();
3037
3038 iter.setOffset(Math.max(margin0, margin1));
3039 totalSpacing += iter.getOffset();
3040
3041 currentPreferred = (long) iter.getPreferredSpan(targetSpan);
3042 iter.setSpan((int) currentPreferred);
3043 preferred += currentPreferred;
3044 gain[iter.getAdjustmentWeight()] += (long) iter
3045 .getMaximumSpan(targetSpan)
3046 - currentPreferred;
3047 loss[iter.getAdjustmentWeight()] += currentPreferred
3048 - (long) iter.getMinimumSpan(targetSpan);
3049 lastMargin = (int) iter.getTrailingCollapseSpan();
3050 }
3051 totalSpacing += lastMargin;
3052 totalSpacing += 2 * iter.getBorderWidth();
3053
3054 for (int i = 1; i < adjustmentWeightsCount; i++) {
3055 gain[i] += gain[i - 1];
3056 loss[i] += loss[i - 1];
3057 }
3058
3059 /*
3060 * Second pass, expand or contract by as much as possible to reach
3061 * the target span. This takes the margin collapsing into account
3062 * prior to adjusting the span.
3063 */
3064
3065 // determine the adjustment to be made
3066 int allocated = targetSpan - totalSpacing;
3067 long desiredAdjustment = allocated - preferred;
3068 long adjustmentsArray[] = (desiredAdjustment > 0) ? gain : loss;
3069 desiredAdjustment = Math.abs(desiredAdjustment);
3070 int adjustmentLevel = 0;
3071 for (; adjustmentLevel <= LayoutIterator.WorstAdjustmentWeight; adjustmentLevel++) {
3072 // adjustmentsArray[] is sorted. I do not bother about
3073 // binary search though
3074 if (adjustmentsArray[adjustmentLevel] >= desiredAdjustment) {
3075 break;
3076 }
3077 }
3078 float adjustmentFactor = 0.0f;
3079 if (adjustmentLevel <= LayoutIterator.WorstAdjustmentWeight) {
3080 desiredAdjustment -= (adjustmentLevel > 0) ? adjustmentsArray[adjustmentLevel - 1]
3081 : 0;
3082 if (desiredAdjustment != 0) {
3083 float maximumAdjustment = adjustmentsArray[adjustmentLevel]
3084 - ((adjustmentLevel > 0) ? adjustmentsArray[adjustmentLevel - 1]
3085 : 0);
3086 adjustmentFactor = desiredAdjustment
3087 / maximumAdjustment;
3088 }
3089 }
3090 // make the adjustments
3091 int totalOffset = (int) iter.getBorderWidth();
3092 ;
3093 for (int i = 0; i < n; i++) {
3094 iter.setIndex(i);
3095 iter.setOffset(iter.getOffset() + totalOffset);
3096 if (iter.getAdjustmentWeight() < adjustmentLevel) {
3097 iter.setSpan((int) ((allocated > preferred) ? Math
3098 .floor(iter.getMaximumSpan(targetSpan)) : Math
3099 .ceil(iter.getMinimumSpan(targetSpan))));
3100 } else if (iter.getAdjustmentWeight() == adjustmentLevel) {
3101 int availableSpan = (allocated > preferred) ? (int) iter
3102 .getMaximumSpan(targetSpan)
3103 - iter.getSpan()
3104 : iter.getSpan()
3105 - (int) iter.getMinimumSpan(targetSpan);
3106 int adj = (int) Math.floor(adjustmentFactor
3107 * availableSpan);
3108 iter.setSpan(iter.getSpan()
3109 + ((allocated > preferred) ? adj : -adj));
3110 }
3111 totalOffset = (int) Math.min((long) iter.getOffset()
3112 + (long) iter.getSpan(), Integer.MAX_VALUE);
3113 }
3114
3115 // while rounding we could lose several pixels.
3116 int roundError = targetSpan - totalOffset
3117 - (int) iter.getTrailingCollapseSpan()
3118 - (int) iter.getBorderWidth();
3119 int adj = (roundError > 0) ? 1 : -1;
3120 roundError *= adj;
3121
3122 boolean canAdjust = true;
3123 while (roundError > 0 && canAdjust) {
3124 // check for infinite loop
3125 canAdjust = false;
3126 int offsetAdjust = 0;
3127 // try to distribute roundError. one pixel per cell
3128 for (int i = 0; i < n; i++) {
3129 iter.setIndex(i);
3130 iter.setOffset(iter.getOffset() + offsetAdjust);
3131 int curSpan = iter.getSpan();
3132 if (roundError > 0) {
3133 int boundGap = (adj > 0) ? (int) Math.floor(iter
3134 .getMaximumSpan(targetSpan))
3135 - curSpan : curSpan
3136 - (int) Math.ceil(iter
3137 .getMinimumSpan(targetSpan));
3138 if (boundGap >= 1) {
3139 canAdjust = true;
3140 iter.setSpan(curSpan + adj);
3141 offsetAdjust += adj;
3142 roundError--;
3143 }
3144 }
3145 }
3146 }
3147 }
3148
3149 /**
3150 * An iterator to express the requirements to use when computing
3151 * layout.
3152 */
3153 interface LayoutIterator {
3154
3155 void setOffset(int offs);
3156
3157 int getOffset();
3158
3159 void setSpan(int span);
3160
3161 int getSpan();
3162
3163 int getCount();
3164
3165 void setIndex(int i);
3166
3167 float getMinimumSpan(float parentSpan);
3168
3169 float getPreferredSpan(float parentSpan);
3170
3171 float getMaximumSpan(float parentSpan);
3172
3173 int getAdjustmentWeight(); //0 is the best weight WorstAdjustmentWeight is a worst one
3174
3175 //float getAlignment();
3176
3177 float getBorderWidth();
3178
3179 float getLeadingCollapseSpan();
3180
3181 float getTrailingCollapseSpan();
3182
3183 public static final int WorstAdjustmentWeight = 2;
3184 }
3185
3186 //
3187 // Serialization support
3188 //
3189
3190 private void writeObject(java.io.ObjectOutputStream s)
3191 throws IOException {
3192 s.defaultWriteObject();
3193
3194 // Determine what values in valueConvertor need to be written out.
3195 Enumeration keys = valueConvertor.keys();
3196 s.writeInt(valueConvertor.size());
3197 if (keys != null) {
3198 while (keys.hasMoreElements()) {
3199 Object key = keys.nextElement();
3200 Object value = valueConvertor.get(key);
3201 if (!(key instanceof Serializable)
3202 && (key = StyleContext
3203 .getStaticAttributeKey(key)) == null) {
3204 // Should we throw an exception here?
3205 key = null;
3206 value = null;
3207 } else if (!(value instanceof Serializable)
3208 && (value = StyleContext
3209 .getStaticAttributeKey(value)) == null) {
3210 // Should we throw an exception here?
3211 key = null;
3212 value = null;
3213 }
3214 s.writeObject(key);
3215 s.writeObject(value);
3216 }
3217 }
3218 }
3219
3220 private void readObject(ObjectInputStream s)
3221 throws ClassNotFoundException, IOException {
3222 s.defaultReadObject();
3223 // Reconstruct the hashtable.
3224 int numValues = s.readInt();
3225 valueConvertor = new Hashtable(Math.max(1, numValues));
3226 while (numValues-- > 0) {
3227 Object key = s.readObject();
3228 Object value = s.readObject();
3229 Object staticKey = StyleContext.getStaticAttribute(key);
3230 if (staticKey != null) {
3231 key = staticKey;
3232 }
3233 Object staticValue = StyleContext.getStaticAttribute(value);
3234 if (staticValue != null) {
3235 value = staticValue;
3236 }
3237 if (key != null && value != null) {
3238 valueConvertor.put(key, value);
3239 }
3240 }
3241 }
3242
3243 /*
3244 * we need StyleSheet for resolving lenght units. (see
3245 * isW3CLengthUnits)
3246 * we can not pass stylesheet for handling relative sizes. (do not
3247 * think changing public API is necessary)
3248 * CSS is not likely to be accessed from more then one thread.
3249 * Having local storage for StyleSheet for resolving relative
3250 * sizes is safe
3251 *
3252 * idk 08/30/2004
3253 */
3254 private StyleSheet getStyleSheet(StyleSheet ss) {
3255 if (ss != null) {
3256 styleSheet = ss;
3257 }
3258 return styleSheet;
3259 }
3260
3261 //
3262 // Instance variables
3263 //
3264
3265 /** Maps from CSS key to CssValue. */
3266 private transient Hashtable valueConvertor;
3267
3268 /** Size used for relative units. */
3269 private int baseFontSize;
3270
3271 private transient StyleSheet styleSheet = null;
3272
3273 static int baseFontSizeIndex = 3;
3274 }
|