1 /* 2 * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing.text.html; 26 27 import java.awt.Color; 28 import java.awt.Font; 29 import java.awt.GraphicsEnvironment; 30 import java.awt.Toolkit; 31 import java.awt.HeadlessException; 32 import java.awt.Image; 33 import java.io.*; 34 import java.lang.reflect.Method; 35 import java.net.URL; 36 import java.net.MalformedURLException; 37 import java.util.Enumeration; 38 import java.util.Hashtable; 39 import java.util.Vector; 40 import java.util.Locale; 41 import javax.swing.ImageIcon; 42 import javax.swing.SizeRequirements; 43 import javax.swing.text.*; 44 45 /** 46 * Defines a set of 47 * <a href="http://www.w3.org/TR/REC-CSS1">CSS attributes</a> 48 * as a typesafe enumeration. The HTML View implementations use 49 * CSS attributes to determine how they will render. This also defines 50 * methods to map between CSS/HTML/StyleConstants. Any shorthand 51 * properties, such as font, are mapped to the intrinsic properties. 52 * <p>The following describes the CSS properties that are supported by the 53 * rendering engine: 54 * <ul><li>font-family 55 * <li>font-style 56 * <li>font-size (supports relative units) 57 * <li>font-weight 58 * <li>font 59 * <li>color 60 * <li>background-color (with the exception of transparent) 61 * <li>background-image 62 * <li>background-repeat 63 * <li>background-position 64 * <li>background 65 * <li>text-decoration (with the exception of blink and overline) 66 * <li>vertical-align (only sup and super) 67 * <li>text-align (justify is treated as center) 68 * <li>margin-top 69 * <li>margin-right 70 * <li>margin-bottom 71 * <li>margin-left 72 * <li>margin 73 * <li>padding-top 74 * <li>padding-right 75 * <li>padding-bottom 76 * <li>padding-left 77 * <li>padding 78 * <li>border-top-style 79 * <li>border-right-style 80 * <li>border-bottom-style 81 * <li>border-left-style 82 * <li>border-style (only supports inset, outset and none) 83 * <li>border-top-color 84 * <li>border-right-color 85 * <li>border-bottom-color 86 * <li>border-left-color 87 * <li>border-color 88 * <li>list-style-image 89 * <li>list-style-type 90 * <li>list-style-position 91 * </ul> 92 * The following are modeled, but currently not rendered. 93 * <ul><li>font-variant 94 * <li>background-attachment (background always treated as scroll) 95 * <li>word-spacing 96 * <li>letter-spacing 97 * <li>text-indent 98 * <li>text-transform 99 * <li>line-height 100 * <li>border-top-width (this is used to indicate if a border should be used) 101 * <li>border-right-width 102 * <li>border-bottom-width 103 * <li>border-left-width 104 * <li>border-width 105 * <li>border-top 106 * <li>border-right 107 * <li>border-bottom 108 * <li>border-left 109 * <li>border 110 * <li>width 111 * <li>height 112 * <li>float 113 * <li>clear 114 * <li>display 115 * <li>white-space 116 * <li>list-style 117 * </ul> 118 * <p><b>Note: for the time being we do not fully support relative units, 119 * unless noted, so that 120 * p { margin-top: 10% } will be treated as if no margin-top was specified.</b> 121 * 122 * @author Timothy Prinzing 123 * @author Scott Violet 124 * @see StyleSheet 125 */ 126 public class CSS implements Serializable { 127 128 /** 129 * Definitions to be used as a key on AttributeSet's 130 * that might hold CSS attributes. Since this is a 131 * closed set (i.e. defined exactly by the specification), 132 * it is final and cannot be extended. 133 */ 134 public static final class Attribute { 135 136 private Attribute(String name, String defaultValue, boolean inherited) { 137 this.name = name; 138 this.defaultValue = defaultValue; 139 this.inherited = inherited; 140 } 141 142 /** 143 * The string representation of the attribute. This 144 * should exactly match the string specified in the 145 * CSS specification. 146 */ 147 public String toString() { 148 return name; 149 } 150 151 /** 152 * Fetch the default value for the attribute. 153 * If there is no default value (such as for 154 * composite attributes), null will be returned. 155 */ 156 public String getDefaultValue() { 157 return defaultValue; 158 } 159 160 /** 161 * Indicates if the attribute should be inherited 162 * from the parent or not. 163 */ 164 public boolean isInherited() { 165 return inherited; 166 } 167 168 private String name; 169 private String defaultValue; 170 private boolean inherited; 171 172 173 public static final Attribute BACKGROUND = 174 new Attribute("background", null, false); 175 176 public static final Attribute BACKGROUND_ATTACHMENT = 177 new Attribute("background-attachment", "scroll", false); 178 179 public static final Attribute BACKGROUND_COLOR = 180 new Attribute("background-color", "transparent", false); 181 182 public static final Attribute BACKGROUND_IMAGE = 183 new Attribute("background-image", "none", false); 184 185 public static final Attribute BACKGROUND_POSITION = 186 new Attribute("background-position", null, false); 187 188 public static final Attribute BACKGROUND_REPEAT = 189 new Attribute("background-repeat", "repeat", false); 190 191 public static final Attribute BORDER = 192 new Attribute("border", null, false); 193 194 public static final Attribute BORDER_BOTTOM = 195 new Attribute("border-bottom", null, false); 196 197 public static final Attribute BORDER_BOTTOM_COLOR = 198 new Attribute("border-bottom-color", null, false); 199 200 public static final Attribute BORDER_BOTTOM_STYLE = 201 new Attribute("border-bottom-style", "none", false); 202 203 public static final Attribute BORDER_BOTTOM_WIDTH = 204 new Attribute("border-bottom-width", "medium", false); 205 206 public static final Attribute BORDER_COLOR = 207 new Attribute("border-color", null, false); 208 209 public static final Attribute BORDER_LEFT = 210 new Attribute("border-left", null, false); 211 212 public static final Attribute BORDER_LEFT_COLOR = 213 new Attribute("border-left-color", null, false); 214 215 public static final Attribute BORDER_LEFT_STYLE = 216 new Attribute("border-left-style", "none", false); 217 218 public static final Attribute BORDER_LEFT_WIDTH = 219 new Attribute("border-left-width", "medium", false); 220 221 public static final Attribute BORDER_RIGHT = 222 new Attribute("border-right", null, false); 223 224 public static final Attribute BORDER_RIGHT_COLOR = 225 new Attribute("border-right-color", null, false); 226 227 public static final Attribute BORDER_RIGHT_STYLE = 228 new Attribute("border-right-style", "none", false); 229 230 public static final Attribute BORDER_RIGHT_WIDTH = 231 new Attribute("border-right-width", "medium", false); 232 233 public static final Attribute BORDER_STYLE = 234 new Attribute("border-style", "none", false); 235 236 public static final Attribute BORDER_TOP = 237 new Attribute("border-top", null, false); 238 239 public static final Attribute BORDER_TOP_COLOR = 240 new Attribute("border-top-color", null, false); 241 242 public static final Attribute BORDER_TOP_STYLE = 243 new Attribute("border-top-style", "none", false); 244 245 public static final Attribute BORDER_TOP_WIDTH = 246 new Attribute("border-top-width", "medium", false); 247 248 public static final Attribute BORDER_WIDTH = 249 new Attribute("border-width", "medium", false); 250 251 public static final Attribute CLEAR = 252 new Attribute("clear", "none", false); 253 254 public static final Attribute COLOR = 255 new Attribute("color", "black", true); 256 257 public static final Attribute DISPLAY = 258 new Attribute("display", "block", false); 259 260 public static final Attribute FLOAT = 261 new Attribute("float", "none", false); 262 263 public static final Attribute FONT = 264 new Attribute("font", null, true); 265 266 public static final Attribute FONT_FAMILY = 267 new Attribute("font-family", null, true); 268 269 public static final Attribute FONT_SIZE = 270 new Attribute("font-size", "medium", true); 271 272 public static final Attribute FONT_STYLE = 273 new Attribute("font-style", "normal", true); 274 275 public static final Attribute FONT_VARIANT = 276 new Attribute("font-variant", "normal", true); 277 278 public static final Attribute FONT_WEIGHT = 279 new Attribute("font-weight", "normal", true); 280 281 public static final Attribute HEIGHT = 282 new Attribute("height", "auto", false); 283 284 public static final Attribute LETTER_SPACING = 285 new Attribute("letter-spacing", "normal", true); 286 287 public static final Attribute LINE_HEIGHT = 288 new Attribute("line-height", "normal", true); 289 290 public static final Attribute LIST_STYLE = 291 new Attribute("list-style", null, true); 292 293 public static final Attribute LIST_STYLE_IMAGE = 294 new Attribute("list-style-image", "none", true); 295 296 public static final Attribute LIST_STYLE_POSITION = 297 new Attribute("list-style-position", "outside", true); 298 299 public static final Attribute LIST_STYLE_TYPE = 300 new Attribute("list-style-type", "disc", true); 301 302 public static final Attribute MARGIN = 303 new Attribute("margin", null, false); 304 305 public static final Attribute MARGIN_BOTTOM = 306 new Attribute("margin-bottom", "0", false); 307 308 public static final Attribute MARGIN_LEFT = 309 new Attribute("margin-left", "0", false); 310 311 public static final Attribute MARGIN_RIGHT = 312 new Attribute("margin-right", "0", false); 313 314 /* 315 * made up css attributes to describe orientation depended 316 * margins. used for <dir>, <menu>, <ul> etc. see 317 * 5088268 for more details 318 */ 319 static final Attribute MARGIN_LEFT_LTR = 320 new Attribute("margin-left-ltr", 321 Integer.toString(Integer.MIN_VALUE), false); 322 323 static final Attribute MARGIN_LEFT_RTL = 324 new Attribute("margin-left-rtl", 325 Integer.toString(Integer.MIN_VALUE), false); 326 327 static final Attribute MARGIN_RIGHT_LTR = 328 new Attribute("margin-right-ltr", 329 Integer.toString(Integer.MIN_VALUE), false); 330 331 static final Attribute MARGIN_RIGHT_RTL = 332 new Attribute("margin-right-rtl", 333 Integer.toString(Integer.MIN_VALUE), false); 334 335 336 public static final Attribute MARGIN_TOP = 337 new Attribute("margin-top", "0", false); 338 339 public static final Attribute PADDING = 340 new Attribute("padding", null, false); 341 342 public static final Attribute PADDING_BOTTOM = 343 new Attribute("padding-bottom", "0", false); 344 345 public static final Attribute PADDING_LEFT = 346 new Attribute("padding-left", "0", false); 347 348 public static final Attribute PADDING_RIGHT = 349 new Attribute("padding-right", "0", false); 350 351 public static final Attribute PADDING_TOP = 352 new Attribute("padding-top", "0", false); 353 354 public static final Attribute TEXT_ALIGN = 355 new Attribute("text-align", null, true); 356 357 public static final Attribute TEXT_DECORATION = 358 new Attribute("text-decoration", "none", true); 359 360 public static final Attribute TEXT_INDENT = 361 new Attribute("text-indent", "0", true); 362 363 public static final Attribute TEXT_TRANSFORM = 364 new Attribute("text-transform", "none", true); 365 366 public static final Attribute VERTICAL_ALIGN = 367 new Attribute("vertical-align", "baseline", false); 368 369 public static final Attribute WORD_SPACING = 370 new Attribute("word-spacing", "normal", true); 371 372 public static final Attribute WHITE_SPACE = 373 new Attribute("white-space", "normal", true); 374 375 public static final Attribute WIDTH = 376 new Attribute("width", "auto", false); 377 378 /*public*/ static final Attribute BORDER_SPACING = 379 new Attribute("border-spacing", "0", true); 380 381 /*public*/ static final Attribute CAPTION_SIDE = 382 new Attribute("caption-side", "left", true); 383 384 // All possible CSS attribute keys. 385 static final Attribute[] allAttributes = { 386 BACKGROUND, BACKGROUND_ATTACHMENT, BACKGROUND_COLOR, 387 BACKGROUND_IMAGE, BACKGROUND_POSITION, BACKGROUND_REPEAT, 388 BORDER, BORDER_BOTTOM, BORDER_BOTTOM_WIDTH, BORDER_COLOR, 389 BORDER_LEFT, BORDER_LEFT_WIDTH, BORDER_RIGHT, BORDER_RIGHT_WIDTH, 390 BORDER_STYLE, BORDER_TOP, BORDER_TOP_WIDTH, BORDER_WIDTH, 391 BORDER_TOP_STYLE, BORDER_RIGHT_STYLE, BORDER_BOTTOM_STYLE, 392 BORDER_LEFT_STYLE, 393 BORDER_TOP_COLOR, BORDER_RIGHT_COLOR, BORDER_BOTTOM_COLOR, 394 BORDER_LEFT_COLOR, 395 CLEAR, COLOR, DISPLAY, FLOAT, FONT, FONT_FAMILY, FONT_SIZE, 396 FONT_STYLE, FONT_VARIANT, FONT_WEIGHT, HEIGHT, LETTER_SPACING, 397 LINE_HEIGHT, LIST_STYLE, LIST_STYLE_IMAGE, LIST_STYLE_POSITION, 398 LIST_STYLE_TYPE, MARGIN, MARGIN_BOTTOM, MARGIN_LEFT, MARGIN_RIGHT, 399 MARGIN_TOP, PADDING, PADDING_BOTTOM, PADDING_LEFT, PADDING_RIGHT, 400 PADDING_TOP, TEXT_ALIGN, TEXT_DECORATION, TEXT_INDENT, TEXT_TRANSFORM, 401 VERTICAL_ALIGN, WORD_SPACING, WHITE_SPACE, WIDTH, 402 BORDER_SPACING, CAPTION_SIDE, 403 MARGIN_LEFT_LTR, MARGIN_LEFT_RTL, MARGIN_RIGHT_LTR, MARGIN_RIGHT_RTL 404 }; 405 406 private static final Attribute[] ALL_MARGINS = 407 { MARGIN_TOP, MARGIN_RIGHT, MARGIN_BOTTOM, MARGIN_LEFT }; 408 private static final Attribute[] ALL_PADDING = 409 { PADDING_TOP, PADDING_RIGHT, PADDING_BOTTOM, PADDING_LEFT }; 410 private static final Attribute[] ALL_BORDER_WIDTHS = 411 { BORDER_TOP_WIDTH, BORDER_RIGHT_WIDTH, BORDER_BOTTOM_WIDTH, 412 BORDER_LEFT_WIDTH }; 413 private static final Attribute[] ALL_BORDER_STYLES = 414 { BORDER_TOP_STYLE, BORDER_RIGHT_STYLE, BORDER_BOTTOM_STYLE, 415 BORDER_LEFT_STYLE }; 416 private static final Attribute[] ALL_BORDER_COLORS = 417 { BORDER_TOP_COLOR, BORDER_RIGHT_COLOR, BORDER_BOTTOM_COLOR, 418 BORDER_LEFT_COLOR }; 419 420 } 421 422 static final class Value { 423 424 private Value(String name) { 425 this.name = name; 426 } 427 428 /** 429 * The string representation of the attribute. This 430 * should exactly match the string specified in the 431 * CSS specification. 432 */ 433 public String toString() { 434 return name; 435 } 436 437 static final Value INHERITED = new Value("inherited"); 438 static final Value NONE = new Value("none"); 439 static final Value HIDDEN = new Value("hidden"); 440 static final Value DOTTED = new Value("dotted"); 441 static final Value DASHED = new Value("dashed"); 442 static final Value SOLID = new Value("solid"); 443 static final Value DOUBLE = new Value("double"); 444 static final Value GROOVE = new Value("groove"); 445 static final Value RIDGE = new Value("ridge"); 446 static final Value INSET = new Value("inset"); 447 static final Value OUTSET = new Value("outset"); 448 // Lists. 449 static final Value DISC = new Value("disc"); 450 static final Value CIRCLE = new Value("circle"); 451 static final Value SQUARE = new Value("square"); 452 static final Value DECIMAL = new Value("decimal"); 453 static final Value LOWER_ROMAN = new Value("lower-roman"); 454 static final Value UPPER_ROMAN = new Value("upper-roman"); 455 static final Value LOWER_ALPHA = new Value("lower-alpha"); 456 static final Value UPPER_ALPHA = new Value("upper-alpha"); 457 // background-repeat 458 static final Value BACKGROUND_NO_REPEAT = new Value("no-repeat"); 459 static final Value BACKGROUND_REPEAT = new Value("repeat"); 460 static final Value BACKGROUND_REPEAT_X = new Value("repeat-x"); 461 static final Value BACKGROUND_REPEAT_Y = new Value("repeat-y"); 462 // background-attachment 463 static final Value BACKGROUND_SCROLL = new Value("scroll"); 464 static final Value BACKGROUND_FIXED = new Value("fixed"); 465 466 private String name; 467 468 static final Value[] allValues = { 469 INHERITED, NONE, DOTTED, DASHED, SOLID, DOUBLE, GROOVE, 470 RIDGE, INSET, OUTSET, DISC, CIRCLE, SQUARE, DECIMAL, 471 LOWER_ROMAN, UPPER_ROMAN, LOWER_ALPHA, UPPER_ALPHA, 472 BACKGROUND_NO_REPEAT, BACKGROUND_REPEAT, 473 BACKGROUND_REPEAT_X, BACKGROUND_REPEAT_Y, 474 BACKGROUND_FIXED, BACKGROUND_FIXED 475 }; 476 } 477 478 public CSS() { 479 baseFontSize = baseFontSizeIndex + 1; 480 // setup the css conversion table 481 valueConvertor = new Hashtable<Object, Object>(); 482 valueConvertor.put(CSS.Attribute.FONT_SIZE, new FontSize()); 483 valueConvertor.put(CSS.Attribute.FONT_FAMILY, new FontFamily()); 484 valueConvertor.put(CSS.Attribute.FONT_WEIGHT, new FontWeight()); 485 Object bs = new BorderStyle(); 486 valueConvertor.put(CSS.Attribute.BORDER_TOP_STYLE, bs); 487 valueConvertor.put(CSS.Attribute.BORDER_RIGHT_STYLE, bs); 488 valueConvertor.put(CSS.Attribute.BORDER_BOTTOM_STYLE, bs); 489 valueConvertor.put(CSS.Attribute.BORDER_LEFT_STYLE, bs); 490 Object cv = new ColorValue(); 491 valueConvertor.put(CSS.Attribute.COLOR, cv); 492 valueConvertor.put(CSS.Attribute.BACKGROUND_COLOR, cv); 493 valueConvertor.put(CSS.Attribute.BORDER_TOP_COLOR, cv); 494 valueConvertor.put(CSS.Attribute.BORDER_RIGHT_COLOR, cv); 495 valueConvertor.put(CSS.Attribute.BORDER_BOTTOM_COLOR, cv); 496 valueConvertor.put(CSS.Attribute.BORDER_LEFT_COLOR, cv); 497 Object lv = new LengthValue(); 498 valueConvertor.put(CSS.Attribute.MARGIN_TOP, lv); 499 valueConvertor.put(CSS.Attribute.MARGIN_BOTTOM, lv); 500 valueConvertor.put(CSS.Attribute.MARGIN_LEFT, lv); 501 valueConvertor.put(CSS.Attribute.MARGIN_LEFT_LTR, lv); 502 valueConvertor.put(CSS.Attribute.MARGIN_LEFT_RTL, lv); 503 valueConvertor.put(CSS.Attribute.MARGIN_RIGHT, lv); 504 valueConvertor.put(CSS.Attribute.MARGIN_RIGHT_LTR, lv); 505 valueConvertor.put(CSS.Attribute.MARGIN_RIGHT_RTL, lv); 506 valueConvertor.put(CSS.Attribute.PADDING_TOP, lv); 507 valueConvertor.put(CSS.Attribute.PADDING_BOTTOM, lv); 508 valueConvertor.put(CSS.Attribute.PADDING_LEFT, lv); 509 valueConvertor.put(CSS.Attribute.PADDING_RIGHT, lv); 510 Object bv = new BorderWidthValue(null, 0); 511 valueConvertor.put(CSS.Attribute.BORDER_TOP_WIDTH, bv); 512 valueConvertor.put(CSS.Attribute.BORDER_BOTTOM_WIDTH, bv); 513 valueConvertor.put(CSS.Attribute.BORDER_LEFT_WIDTH, bv); 514 valueConvertor.put(CSS.Attribute.BORDER_RIGHT_WIDTH, bv); 515 Object nlv = new LengthValue(true); 516 valueConvertor.put(CSS.Attribute.TEXT_INDENT, nlv); 517 valueConvertor.put(CSS.Attribute.WIDTH, lv); 518 valueConvertor.put(CSS.Attribute.HEIGHT, lv); 519 valueConvertor.put(CSS.Attribute.BORDER_SPACING, lv); 520 Object sv = new StringValue(); 521 valueConvertor.put(CSS.Attribute.FONT_STYLE, sv); 522 valueConvertor.put(CSS.Attribute.TEXT_DECORATION, sv); 523 valueConvertor.put(CSS.Attribute.TEXT_ALIGN, sv); 524 valueConvertor.put(CSS.Attribute.VERTICAL_ALIGN, sv); 525 Object valueMapper = new CssValueMapper(); 526 valueConvertor.put(CSS.Attribute.LIST_STYLE_TYPE, 527 valueMapper); 528 valueConvertor.put(CSS.Attribute.BACKGROUND_IMAGE, 529 new BackgroundImage()); 530 valueConvertor.put(CSS.Attribute.BACKGROUND_POSITION, 531 new BackgroundPosition()); 532 valueConvertor.put(CSS.Attribute.BACKGROUND_REPEAT, 533 valueMapper); 534 valueConvertor.put(CSS.Attribute.BACKGROUND_ATTACHMENT, 535 valueMapper); 536 Object generic = new CssValue(); 537 int n = CSS.Attribute.allAttributes.length; 538 for (int i = 0; i < n; i++) { 539 CSS.Attribute key = CSS.Attribute.allAttributes[i]; 540 if (valueConvertor.get(key) == null) { 541 valueConvertor.put(key, generic); 542 } 543 } 544 } 545 546 /** 547 * Sets the base font size. <code>sz</code> is a CSS value, and is 548 * not necessarily the point size. Use getPointSize to determine the 549 * point size corresponding to <code>sz</code>. 550 */ 551 void setBaseFontSize(int sz) { 552 if (sz < 1) 553 baseFontSize = 0; 554 else if (sz > 7) 555 baseFontSize = 7; 556 else 557 baseFontSize = sz; 558 } 559 560 /** 561 * Sets the base font size from the passed in string. 562 */ 563 void setBaseFontSize(String size) { 564 int relSize, absSize, diff; 565 566 if (size != null) { 567 if (size.startsWith("+")) { 568 relSize = Integer.valueOf(size.substring(1)).intValue(); 569 setBaseFontSize(baseFontSize + relSize); 570 } else if (size.startsWith("-")) { 571 relSize = -Integer.valueOf(size.substring(1)).intValue(); 572 setBaseFontSize(baseFontSize + relSize); 573 } else { 574 setBaseFontSize(Integer.valueOf(size).intValue()); 575 } 576 } 577 } 578 579 /** 580 * Returns the base font size. 581 */ 582 int getBaseFontSize() { 583 return baseFontSize; 584 } 585 586 /** 587 * Parses the CSS property <code>key</code> with value 588 * <code>value</code> placing the result in <code>att</code>. 589 */ 590 void addInternalCSSValue(MutableAttributeSet attr, 591 CSS.Attribute key, String value) { 592 if (key == CSS.Attribute.FONT) { 593 ShorthandFontParser.parseShorthandFont(this, value, attr); 594 } 595 else if (key == CSS.Attribute.BACKGROUND) { 596 ShorthandBackgroundParser.parseShorthandBackground 597 (this, value, attr); 598 } 599 else if (key == CSS.Attribute.MARGIN) { 600 ShorthandMarginParser.parseShorthandMargin(this, value, attr, 601 CSS.Attribute.ALL_MARGINS); 602 } 603 else if (key == CSS.Attribute.PADDING) { 604 ShorthandMarginParser.parseShorthandMargin(this, value, attr, 605 CSS.Attribute.ALL_PADDING); 606 } 607 else if (key == CSS.Attribute.BORDER_WIDTH) { 608 ShorthandMarginParser.parseShorthandMargin(this, value, attr, 609 CSS.Attribute.ALL_BORDER_WIDTHS); 610 } 611 else if (key == CSS.Attribute.BORDER_COLOR) { 612 ShorthandMarginParser.parseShorthandMargin(this, value, attr, 613 CSS.Attribute.ALL_BORDER_COLORS); 614 } 615 else if (key == CSS.Attribute.BORDER_STYLE) { 616 ShorthandMarginParser.parseShorthandMargin(this, value, attr, 617 CSS.Attribute.ALL_BORDER_STYLES); 618 } 619 else if ((key == CSS.Attribute.BORDER) || 620 (key == CSS.Attribute.BORDER_TOP) || 621 (key == CSS.Attribute.BORDER_RIGHT) || 622 (key == CSS.Attribute.BORDER_BOTTOM) || 623 (key == CSS.Attribute.BORDER_LEFT)) { 624 ShorthandBorderParser.parseShorthandBorder(attr, key, value); 625 } 626 else { 627 Object iValue = getInternalCSSValue(key, value); 628 if (iValue != null) { 629 attr.addAttribute(key, iValue); 630 } 631 } 632 } 633 634 /** 635 * Gets the internal CSS representation of <code>value</code> which is 636 * a CSS value of the CSS attribute named <code>key</code>. The receiver 637 * should not modify <code>value</code>, and the first <code>count</code> 638 * strings are valid. 639 */ 640 Object getInternalCSSValue(CSS.Attribute key, String value) { 641 CssValue conv = (CssValue) valueConvertor.get(key); 642 Object r = conv.parseCssValue(value); 643 return r != null ? r : conv.parseCssValue(key.getDefaultValue()); 644 } 645 646 /** 647 * Maps from a StyleConstants to a CSS Attribute. 648 */ 649 Attribute styleConstantsKeyToCSSKey(StyleConstants sc) { 650 return styleConstantToCssMap.get(sc); 651 } 652 653 /** 654 * Maps from a StyleConstants value to a CSS value. 655 */ 656 Object styleConstantsValueToCSSValue(StyleConstants sc, 657 Object styleValue) { 658 Attribute cssKey = styleConstantsKeyToCSSKey(sc); 659 if (cssKey != null) { 660 CssValue conv = (CssValue)valueConvertor.get(cssKey); 661 return conv.fromStyleConstants(sc, styleValue); 662 } 663 return null; 664 } 665 666 /** 667 * Converts the passed in CSS value to a StyleConstants value. 668 * <code>key</code> identifies the CSS attribute being mapped. 669 */ 670 Object cssValueToStyleConstantsValue(StyleConstants key, Object value) { 671 if (value instanceof CssValue) { 672 return ((CssValue)value).toStyleConstants(key, null); 673 } 674 return null; 675 } 676 677 /** 678 * Returns the font for the values in the passed in AttributeSet. 679 * It is assumed the keys will be CSS.Attribute keys. 680 * <code>sc</code> is the StyleContext that will be messaged to get 681 * the font once the size, name and style have been determined. 682 */ 683 Font getFont(StyleContext sc, AttributeSet a, int defaultSize, StyleSheet ss) { 684 ss = getStyleSheet(ss); 685 int size = getFontSize(a, defaultSize, ss); 686 687 /* 688 * If the vertical alignment is set to either superscirpt or 689 * subscript we reduce the font size by 2 points. 690 */ 691 StringValue vAlignV = (StringValue)a.getAttribute 692 (CSS.Attribute.VERTICAL_ALIGN); 693 if ((vAlignV != null)) { 694 String vAlign = vAlignV.toString(); 695 if ((vAlign.indexOf("sup") >= 0) || 696 (vAlign.indexOf("sub") >= 0)) { 697 size -= 2; 698 } 699 } 700 701 FontFamily familyValue = (FontFamily)a.getAttribute 702 (CSS.Attribute.FONT_FAMILY); 703 String family = (familyValue != null) ? familyValue.getValue() : 704 Font.SANS_SERIF; 705 int style = Font.PLAIN; 706 FontWeight weightValue = (FontWeight) a.getAttribute 707 (CSS.Attribute.FONT_WEIGHT); 708 if ((weightValue != null) && (weightValue.getValue() > 400)) { 709 style |= Font.BOLD; 710 } 711 Object fs = a.getAttribute(CSS.Attribute.FONT_STYLE); 712 if ((fs != null) && (fs.toString().indexOf("italic") >= 0)) { 713 style |= Font.ITALIC; 714 } 715 if (family.equalsIgnoreCase("monospace")) { 716 family = Font.MONOSPACED; 717 } 718 Font f = sc.getFont(family, style, size); 719 if (f == null 720 || (f.getFamily().equals(Font.DIALOG) 721 && ! family.equalsIgnoreCase(Font.DIALOG))) { 722 family = Font.SANS_SERIF; 723 f = sc.getFont(family, style, size); 724 } 725 return f; 726 } 727 728 static int getFontSize(AttributeSet attr, int defaultSize, StyleSheet ss) { 729 // PENDING(prinz) this is a 1.1 based implementation, need to also 730 // have a 1.2 version. 731 FontSize sizeValue = (FontSize)attr.getAttribute(CSS.Attribute. 732 FONT_SIZE); 733 734 return (sizeValue != null) ? sizeValue.getValue(attr, ss) 735 : defaultSize; 736 } 737 738 /** 739 * Takes a set of attributes and turn it into a color 740 * specification. This might be used to specify things 741 * like brighter, more hue, etc. 742 * This will return null if there is no value for <code>key</code>. 743 * 744 * @param key CSS.Attribute identifying where color is stored. 745 * @param a the set of attributes 746 * @return the color 747 */ 748 Color getColor(AttributeSet a, CSS.Attribute key) { 749 ColorValue cv = (ColorValue) a.getAttribute(key); 750 if (cv != null) { 751 return cv.getValue(); 752 } 753 return null; 754 } 755 756 /** 757 * Returns the size of a font from the passed in string. 758 * 759 * @param size CSS string describing font size 760 * @param baseFontSize size to use for relative units. 761 */ 762 float getPointSize(String size, StyleSheet ss) { 763 int relSize, absSize, diff, index; 764 ss = getStyleSheet(ss); 765 if (size != null) { 766 if (size.startsWith("+")) { 767 relSize = Integer.valueOf(size.substring(1)).intValue(); 768 return getPointSize(baseFontSize + relSize, ss); 769 } else if (size.startsWith("-")) { 770 relSize = -Integer.valueOf(size.substring(1)).intValue(); 771 return getPointSize(baseFontSize + relSize, ss); 772 } else { 773 absSize = Integer.valueOf(size).intValue(); 774 return getPointSize(absSize, ss); 775 } 776 } 777 return 0; 778 } 779 780 /** 781 * Returns the length of the attribute in <code>a</code> with 782 * key <code>key</code>. 783 */ 784 float getLength(AttributeSet a, CSS.Attribute key, StyleSheet ss) { 785 ss = getStyleSheet(ss); 786 LengthValue lv = (LengthValue) a.getAttribute(key); 787 boolean isW3CLengthUnits = (ss == null) ? false : ss.isW3CLengthUnits(); 788 float len = (lv != null) ? lv.getValue(isW3CLengthUnits) : 0; 789 return len; 790 } 791 792 /** 793 * Convert a set of HTML attributes to an equivalent 794 * set of CSS attributes. 795 * 796 * @param htmlAttrSet AttributeSet containing the HTML attributes. 797 * @return AttributeSet containing the corresponding CSS attributes. 798 * The AttributeSet will be empty if there are no mapping 799 * CSS attributes. 800 */ 801 AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet) { 802 MutableAttributeSet cssAttrSet = new SimpleAttributeSet(); 803 Element elem = (Element)htmlAttrSet; 804 HTML.Tag tag = getHTMLTag(htmlAttrSet); 805 if ((tag == HTML.Tag.TD) || (tag == HTML.Tag.TH)) { 806 // translate border width into the cells, if it has non-zero value. 807 AttributeSet tableAttr = elem.getParentElement(). 808 getParentElement().getAttributes(); 809 810 int borderWidth = getTableBorder(tableAttr); 811 if (borderWidth > 0) { 812 // If table contains the BORDER attribute cells should have border width equals 1 813 translateAttribute(HTML.Attribute.BORDER, "1", cssAttrSet); 814 } 815 String pad = (String)tableAttr.getAttribute(HTML.Attribute.CELLPADDING); 816 if (pad != null) { 817 LengthValue v = 818 (LengthValue)getInternalCSSValue(CSS.Attribute.PADDING_TOP, pad); 819 v.span = (v.span < 0) ? 0 : v.span; 820 cssAttrSet.addAttribute(CSS.Attribute.PADDING_TOP, v); 821 cssAttrSet.addAttribute(CSS.Attribute.PADDING_BOTTOM, v); 822 cssAttrSet.addAttribute(CSS.Attribute.PADDING_LEFT, v); 823 cssAttrSet.addAttribute(CSS.Attribute.PADDING_RIGHT, v); 824 } 825 } 826 if (elem.isLeaf()) { 827 translateEmbeddedAttributes(htmlAttrSet, cssAttrSet); 828 } else { 829 translateAttributes(tag, htmlAttrSet, cssAttrSet); 830 } 831 if (tag == HTML.Tag.CAPTION) { 832 /* 833 * Navigator uses ALIGN for caption placement and IE uses VALIGN. 834 */ 835 Object v = htmlAttrSet.getAttribute(HTML.Attribute.ALIGN); 836 if ((v != null) && (v.equals("top") || v.equals("bottom"))) { 837 cssAttrSet.addAttribute(CSS.Attribute.CAPTION_SIDE, v); 838 cssAttrSet.removeAttribute(CSS.Attribute.TEXT_ALIGN); 839 } else { 840 v = htmlAttrSet.getAttribute(HTML.Attribute.VALIGN); 841 if (v != null) { 842 cssAttrSet.addAttribute(CSS.Attribute.CAPTION_SIDE, v); 843 } 844 } 845 } 846 return cssAttrSet; 847 } 848 849 private static int getTableBorder(AttributeSet tableAttr) { 850 String borderValue = (String) tableAttr.getAttribute(HTML.Attribute.BORDER); 851 852 if (borderValue == HTML.NULL_ATTRIBUTE_VALUE || "".equals(borderValue)) { 853 // Some browsers accept <TABLE BORDER> and <TABLE BORDER=""> with the same semantics as BORDER=1 854 return 1; 855 } 856 857 try { 858 return Integer.parseInt(borderValue); 859 } catch (NumberFormatException e) { 860 return 0; 861 } 862 } 863 864 private static final Hashtable<String, Attribute> attributeMap = new Hashtable<String, Attribute>(); 865 private static final Hashtable<String, Value> valueMap = new Hashtable<String, Value>(); 866 867 /** 868 * The hashtable and the static initalization block below, 869 * set up a mapping from well-known HTML attributes to 870 * CSS attributes. For the most part, there is a 1-1 mapping 871 * between the two. However in the case of certain HTML 872 * attributes for example HTML.Attribute.VSPACE or 873 * HTML.Attribute.HSPACE, end up mapping to two CSS.Attribute's. 874 * Therefore, the value associated with each HTML.Attribute. 875 * key ends up being an array of CSS.Attribute.* objects. 876 */ 877 private static final Hashtable<HTML.Attribute, CSS.Attribute[]> htmlAttrToCssAttrMap = new Hashtable<HTML.Attribute, CSS.Attribute[]>(20); 878 879 /** 880 * The hashtable and static initialization that follows sets 881 * up a translation from StyleConstants (i.e. the <em>well known</em> 882 * attributes) to the associated CSS attributes. 883 */ 884 private static final Hashtable<Object, Attribute> styleConstantToCssMap = new Hashtable<Object, Attribute>(17); 885 /** Maps from HTML value to a CSS value. Used in internal mapping. */ 886 private static final Hashtable<String, CSS.Value> htmlValueToCssValueMap = new Hashtable<String, CSS.Value>(8); 887 /** Maps from CSS value (string) to internal value. */ 888 private static final Hashtable<String, CSS.Value> cssValueToInternalValueMap = new Hashtable<String, CSS.Value>(13); 889 890 static { 891 // load the attribute map 892 for (int i = 0; i < Attribute.allAttributes.length; i++ ) { 893 attributeMap.put(Attribute.allAttributes[i].toString(), 894 Attribute.allAttributes[i]); 895 } 896 // load the value map 897 for (int i = 0; i < Value.allValues.length; i++ ) { 898 valueMap.put(Value.allValues[i].toString(), 899 Value.allValues[i]); 900 } 901 902 htmlAttrToCssAttrMap.put(HTML.Attribute.COLOR, 903 new CSS.Attribute[]{CSS.Attribute.COLOR}); 904 htmlAttrToCssAttrMap.put(HTML.Attribute.TEXT, 905 new CSS.Attribute[]{CSS.Attribute.COLOR}); 906 htmlAttrToCssAttrMap.put(HTML.Attribute.CLEAR, 907 new CSS.Attribute[]{CSS.Attribute.CLEAR}); 908 htmlAttrToCssAttrMap.put(HTML.Attribute.BACKGROUND, 909 new CSS.Attribute[]{CSS.Attribute.BACKGROUND_IMAGE}); 910 htmlAttrToCssAttrMap.put(HTML.Attribute.BGCOLOR, 911 new CSS.Attribute[]{CSS.Attribute.BACKGROUND_COLOR}); 912 htmlAttrToCssAttrMap.put(HTML.Attribute.WIDTH, 913 new CSS.Attribute[]{CSS.Attribute.WIDTH}); 914 htmlAttrToCssAttrMap.put(HTML.Attribute.HEIGHT, 915 new CSS.Attribute[]{CSS.Attribute.HEIGHT}); 916 htmlAttrToCssAttrMap.put(HTML.Attribute.BORDER, 917 new CSS.Attribute[]{CSS.Attribute.BORDER_TOP_WIDTH, CSS.Attribute.BORDER_RIGHT_WIDTH, CSS.Attribute.BORDER_BOTTOM_WIDTH, CSS.Attribute.BORDER_LEFT_WIDTH}); 918 htmlAttrToCssAttrMap.put(HTML.Attribute.CELLPADDING, 919 new CSS.Attribute[]{CSS.Attribute.PADDING}); 920 htmlAttrToCssAttrMap.put(HTML.Attribute.CELLSPACING, 921 new CSS.Attribute[]{CSS.Attribute.BORDER_SPACING}); 922 htmlAttrToCssAttrMap.put(HTML.Attribute.MARGINWIDTH, 923 new CSS.Attribute[]{CSS.Attribute.MARGIN_LEFT, 924 CSS.Attribute.MARGIN_RIGHT}); 925 htmlAttrToCssAttrMap.put(HTML.Attribute.MARGINHEIGHT, 926 new CSS.Attribute[]{CSS.Attribute.MARGIN_TOP, 927 CSS.Attribute.MARGIN_BOTTOM}); 928 htmlAttrToCssAttrMap.put(HTML.Attribute.HSPACE, 929 new CSS.Attribute[]{CSS.Attribute.PADDING_LEFT, 930 CSS.Attribute.PADDING_RIGHT}); 931 htmlAttrToCssAttrMap.put(HTML.Attribute.VSPACE, 932 new CSS.Attribute[]{CSS.Attribute.PADDING_BOTTOM, 933 CSS.Attribute.PADDING_TOP}); 934 htmlAttrToCssAttrMap.put(HTML.Attribute.FACE, 935 new CSS.Attribute[]{CSS.Attribute.FONT_FAMILY}); 936 htmlAttrToCssAttrMap.put(HTML.Attribute.SIZE, 937 new CSS.Attribute[]{CSS.Attribute.FONT_SIZE}); 938 htmlAttrToCssAttrMap.put(HTML.Attribute.VALIGN, 939 new CSS.Attribute[]{CSS.Attribute.VERTICAL_ALIGN}); 940 htmlAttrToCssAttrMap.put(HTML.Attribute.ALIGN, 941 new CSS.Attribute[]{CSS.Attribute.VERTICAL_ALIGN, 942 CSS.Attribute.TEXT_ALIGN, 943 CSS.Attribute.FLOAT}); 944 htmlAttrToCssAttrMap.put(HTML.Attribute.TYPE, 945 new CSS.Attribute[]{CSS.Attribute.LIST_STYLE_TYPE}); 946 htmlAttrToCssAttrMap.put(HTML.Attribute.NOWRAP, 947 new CSS.Attribute[]{CSS.Attribute.WHITE_SPACE}); 948 949 // initialize StyleConstants mapping 950 styleConstantToCssMap.put(StyleConstants.FontFamily, 951 CSS.Attribute.FONT_FAMILY); 952 styleConstantToCssMap.put(StyleConstants.FontSize, 953 CSS.Attribute.FONT_SIZE); 954 styleConstantToCssMap.put(StyleConstants.Bold, 955 CSS.Attribute.FONT_WEIGHT); 956 styleConstantToCssMap.put(StyleConstants.Italic, 957 CSS.Attribute.FONT_STYLE); 958 styleConstantToCssMap.put(StyleConstants.Underline, 959 CSS.Attribute.TEXT_DECORATION); 960 styleConstantToCssMap.put(StyleConstants.StrikeThrough, 961 CSS.Attribute.TEXT_DECORATION); 962 styleConstantToCssMap.put(StyleConstants.Superscript, 963 CSS.Attribute.VERTICAL_ALIGN); 964 styleConstantToCssMap.put(StyleConstants.Subscript, 965 CSS.Attribute.VERTICAL_ALIGN); 966 styleConstantToCssMap.put(StyleConstants.Foreground, 967 CSS.Attribute.COLOR); 968 styleConstantToCssMap.put(StyleConstants.Background, 969 CSS.Attribute.BACKGROUND_COLOR); 970 styleConstantToCssMap.put(StyleConstants.FirstLineIndent, 971 CSS.Attribute.TEXT_INDENT); 972 styleConstantToCssMap.put(StyleConstants.LeftIndent, 973 CSS.Attribute.MARGIN_LEFT); 974 styleConstantToCssMap.put(StyleConstants.RightIndent, 975 CSS.Attribute.MARGIN_RIGHT); 976 styleConstantToCssMap.put(StyleConstants.SpaceAbove, 977 CSS.Attribute.MARGIN_TOP); 978 styleConstantToCssMap.put(StyleConstants.SpaceBelow, 979 CSS.Attribute.MARGIN_BOTTOM); 980 styleConstantToCssMap.put(StyleConstants.Alignment, 981 CSS.Attribute.TEXT_ALIGN); 982 983 // HTML->CSS 984 htmlValueToCssValueMap.put("disc", CSS.Value.DISC); 985 htmlValueToCssValueMap.put("square", CSS.Value.SQUARE); 986 htmlValueToCssValueMap.put("circle", CSS.Value.CIRCLE); 987 htmlValueToCssValueMap.put("1", CSS.Value.DECIMAL); 988 htmlValueToCssValueMap.put("a", CSS.Value.LOWER_ALPHA); 989 htmlValueToCssValueMap.put("A", CSS.Value.UPPER_ALPHA); 990 htmlValueToCssValueMap.put("i", CSS.Value.LOWER_ROMAN); 991 htmlValueToCssValueMap.put("I", CSS.Value.UPPER_ROMAN); 992 993 // CSS-> internal CSS 994 cssValueToInternalValueMap.put("none", CSS.Value.NONE); 995 cssValueToInternalValueMap.put("disc", CSS.Value.DISC); 996 cssValueToInternalValueMap.put("square", CSS.Value.SQUARE); 997 cssValueToInternalValueMap.put("circle", CSS.Value.CIRCLE); 998 cssValueToInternalValueMap.put("decimal", CSS.Value.DECIMAL); 999 cssValueToInternalValueMap.put("lower-roman", CSS.Value.LOWER_ROMAN); 1000 cssValueToInternalValueMap.put("upper-roman", CSS.Value.UPPER_ROMAN); 1001 cssValueToInternalValueMap.put("lower-alpha", CSS.Value.LOWER_ALPHA); 1002 cssValueToInternalValueMap.put("upper-alpha", CSS.Value.UPPER_ALPHA); 1003 cssValueToInternalValueMap.put("repeat", CSS.Value.BACKGROUND_REPEAT); 1004 cssValueToInternalValueMap.put("no-repeat", 1005 CSS.Value.BACKGROUND_NO_REPEAT); 1006 cssValueToInternalValueMap.put("repeat-x", 1007 CSS.Value.BACKGROUND_REPEAT_X); 1008 cssValueToInternalValueMap.put("repeat-y", 1009 CSS.Value.BACKGROUND_REPEAT_Y); 1010 cssValueToInternalValueMap.put("scroll", 1011 CSS.Value.BACKGROUND_SCROLL); 1012 cssValueToInternalValueMap.put("fixed", 1013 CSS.Value.BACKGROUND_FIXED); 1014 1015 // Register all the CSS attribute keys for archival/unarchival 1016 Object[] keys = CSS.Attribute.allAttributes; 1017 try { 1018 for (Object key : keys) { 1019 StyleContext.registerStaticAttributeKey(key); 1020 } 1021 } catch (Throwable e) { 1022 e.printStackTrace(); 1023 } 1024 1025 // Register all the CSS Values for archival/unarchival 1026 keys = CSS.Value.allValues; 1027 try { 1028 for (Object key : keys) { 1029 StyleContext.registerStaticAttributeKey(key); 1030 } 1031 } catch (Throwable e) { 1032 e.printStackTrace(); 1033 } 1034 } 1035 1036 /** 1037 * Return the set of all possible CSS attribute keys. 1038 */ 1039 public static Attribute[] getAllAttributeKeys() { 1040 Attribute[] keys = new Attribute[Attribute.allAttributes.length]; 1041 System.arraycopy(Attribute.allAttributes, 0, keys, 0, Attribute.allAttributes.length); 1042 return keys; 1043 } 1044 1045 /** 1046 * Translates a string to a <code>CSS.Attribute</code> object. 1047 * This will return <code>null</code> if there is no attribute 1048 * by the given name. 1049 * 1050 * @param name the name of the CSS attribute to fetch the 1051 * typesafe enumeration for 1052 * @return the <code>CSS.Attribute</code> object, 1053 * or <code>null</code> if the string 1054 * doesn't represent a valid attribute key 1055 */ 1056 public static final Attribute getAttribute(String name) { 1057 return attributeMap.get(name); 1058 } 1059 1060 /** 1061 * Translates a string to a <code>CSS.Value</code> object. 1062 * This will return <code>null</code> if there is no value 1063 * by the given name. 1064 * 1065 * @param name the name of the CSS value to fetch the 1066 * typesafe enumeration for 1067 * @return the <code>CSS.Value</code> object, 1068 * or <code>null</code> if the string 1069 * doesn't represent a valid CSS value name; this does 1070 * not mean that it doesn't represent a valid CSS value 1071 */ 1072 static final Value getValue(String name) { 1073 return valueMap.get(name); 1074 } 1075 1076 1077 // 1078 // Conversion related methods/classes 1079 // 1080 1081 /** 1082 * Returns a URL for the given CSS url string. If relative, 1083 * <code>base</code> is used as the parent. If a valid URL can not 1084 * be found, this will not throw a MalformedURLException, instead 1085 * null will be returned. 1086 */ 1087 static URL getURL(URL base, String cssString) { 1088 if (cssString == null) { 1089 return null; 1090 } 1091 if (cssString.startsWith("url(") && 1092 cssString.endsWith(")")) { 1093 cssString = cssString.substring(4, cssString.length() - 1); 1094 } 1095 // Absolute first 1096 try { 1097 URL url = new URL(cssString); 1098 if (url != null) { 1099 return url; 1100 } 1101 } catch (MalformedURLException mue) { 1102 } 1103 // Then relative 1104 if (base != null) { 1105 // Relative URL, try from base 1106 try { 1107 URL url = new URL(base, cssString); 1108 return url; 1109 } 1110 catch (MalformedURLException muee) { 1111 } 1112 } 1113 return null; 1114 } 1115 1116 /** 1117 * Converts a type Color to a hex string 1118 * in the format "#RRGGBB" 1119 */ 1120 static String colorToHex(Color color) { 1121 1122 String colorstr = "#"; 1123 1124 // Red 1125 String str = Integer.toHexString(color.getRed()); 1126 if (str.length() > 2) 1127 str = str.substring(0, 2); 1128 else if (str.length() < 2) 1129 colorstr += "0" + str; 1130 else 1131 colorstr += str; 1132 1133 // Green 1134 str = Integer.toHexString(color.getGreen()); 1135 if (str.length() > 2) 1136 str = str.substring(0, 2); 1137 else if (str.length() < 2) 1138 colorstr += "0" + str; 1139 else 1140 colorstr += str; 1141 1142 // Blue 1143 str = Integer.toHexString(color.getBlue()); 1144 if (str.length() > 2) 1145 str = str.substring(0, 2); 1146 else if (str.length() < 2) 1147 colorstr += "0" + str; 1148 else 1149 colorstr += str; 1150 1151 return colorstr; 1152 } 1153 1154 /** 1155 * Convert a "#FFFFFF" hex string to a Color. 1156 * If the color specification is bad, an attempt 1157 * will be made to fix it up. 1158 */ 1159 static final Color hexToColor(String value) { 1160 String digits; 1161 int n = value.length(); 1162 if (value.startsWith("#")) { 1163 digits = value.substring(1, Math.min(value.length(), 7)); 1164 } else { 1165 digits = value; 1166 } 1167 String hstr = "0x" + digits; 1168 Color c; 1169 try { 1170 c = Color.decode(hstr); 1171 } catch (NumberFormatException nfe) { 1172 c = null; 1173 } 1174 return c; 1175 } 1176 1177 /** 1178 * Convert a color string such as "RED" or "#NNNNNN" or "rgb(r, g, b)" 1179 * to a Color. 1180 */ 1181 static Color stringToColor(String str) { 1182 Color color; 1183 1184 if (str == null) { 1185 return null; 1186 } 1187 if (str.length() == 0) 1188 color = Color.black; 1189 else if (str.startsWith("rgb(")) { 1190 color = parseRGB(str); 1191 } 1192 else if (str.charAt(0) == '#') 1193 color = hexToColor(str); 1194 else if (str.equalsIgnoreCase("Black")) 1195 color = hexToColor("#000000"); 1196 else if(str.equalsIgnoreCase("Silver")) 1197 color = hexToColor("#C0C0C0"); 1198 else if(str.equalsIgnoreCase("Gray")) 1199 color = hexToColor("#808080"); 1200 else if(str.equalsIgnoreCase("White")) 1201 color = hexToColor("#FFFFFF"); 1202 else if(str.equalsIgnoreCase("Maroon")) 1203 color = hexToColor("#800000"); 1204 else if(str.equalsIgnoreCase("Red")) 1205 color = hexToColor("#FF0000"); 1206 else if(str.equalsIgnoreCase("Purple")) 1207 color = hexToColor("#800080"); 1208 else if(str.equalsIgnoreCase("Fuchsia")) 1209 color = hexToColor("#FF00FF"); 1210 else if(str.equalsIgnoreCase("Green")) 1211 color = hexToColor("#008000"); 1212 else if(str.equalsIgnoreCase("Lime")) 1213 color = hexToColor("#00FF00"); 1214 else if(str.equalsIgnoreCase("Olive")) 1215 color = hexToColor("#808000"); 1216 else if(str.equalsIgnoreCase("Yellow")) 1217 color = hexToColor("#FFFF00"); 1218 else if(str.equalsIgnoreCase("Navy")) 1219 color = hexToColor("#000080"); 1220 else if(str.equalsIgnoreCase("Blue")) 1221 color = hexToColor("#0000FF"); 1222 else if(str.equalsIgnoreCase("Teal")) 1223 color = hexToColor("#008080"); 1224 else if(str.equalsIgnoreCase("Aqua")) 1225 color = hexToColor("#00FFFF"); 1226 else if(str.equalsIgnoreCase("Orange")) 1227 color = hexToColor("#FF8000"); 1228 else 1229 color = hexToColor(str); // sometimes get specified without leading # 1230 return color; 1231 } 1232 1233 /** 1234 * Parses a String in the format <code>rgb(r, g, b)</code> where 1235 * each of the Color components is either an integer, or a floating number 1236 * with a % after indicating a percentage value of 255. Values are 1237 * constrained to fit with 0-255. The resulting Color is returned. 1238 */ 1239 private static Color parseRGB(String string) { 1240 // Find the next numeric char 1241 int[] index = new int[1]; 1242 1243 index[0] = 4; 1244 int red = getColorComponent(string, index); 1245 int green = getColorComponent(string, index); 1246 int blue = getColorComponent(string, index); 1247 1248 return new Color(red, green, blue); 1249 } 1250 1251 /** 1252 * Returns the next integer value from <code>string</code> starting 1253 * at <code>index[0]</code>. The value can either can an integer, or 1254 * a percentage (floating number ending with %), in which case it is 1255 * multiplied by 255. 1256 */ 1257 private static int getColorComponent(String string, int[] index) { 1258 int length = string.length(); 1259 char aChar; 1260 1261 // Skip non-decimal chars 1262 while(index[0] < length && (aChar = string.charAt(index[0])) != '-' && 1263 !Character.isDigit(aChar) && aChar != '.') { 1264 index[0]++; 1265 } 1266 1267 int start = index[0]; 1268 1269 if (start < length && string.charAt(index[0]) == '-') { 1270 index[0]++; 1271 } 1272 while(index[0] < length && 1273 Character.isDigit(string.charAt(index[0]))) { 1274 index[0]++; 1275 } 1276 if (index[0] < length && string.charAt(index[0]) == '.') { 1277 // Decimal value 1278 index[0]++; 1279 while(index[0] < length && 1280 Character.isDigit(string.charAt(index[0]))) { 1281 index[0]++; 1282 } 1283 } 1284 if (start != index[0]) { 1285 try { 1286 float value = Float.parseFloat(string.substring 1287 (start, index[0])); 1288 1289 if (index[0] < length && string.charAt(index[0]) == '%') { 1290 index[0]++; 1291 value = value * 255f / 100f; 1292 } 1293 return Math.min(255, Math.max(0, (int)value)); 1294 } catch (NumberFormatException nfe) { 1295 // Treat as 0 1296 } 1297 } 1298 return 0; 1299 } 1300 1301 static int getIndexOfSize(float pt, int[] sizeMap) { 1302 for (int i = 0; i < sizeMap.length; i ++ ) 1303 if (pt <= sizeMap[i]) 1304 return i + 1; 1305 return sizeMap.length; 1306 } 1307 1308 static int getIndexOfSize(float pt, StyleSheet ss) { 1309 int[] sizeMap = (ss != null) ? ss.getSizeMap() : 1310 StyleSheet.sizeMapDefault; 1311 return getIndexOfSize(pt, sizeMap); 1312 } 1313 1314 1315 /** 1316 * @return an array of all the strings in <code>value</code> 1317 * that are separated by whitespace. 1318 */ 1319 static String[] parseStrings(String value) { 1320 int current, last; 1321 int length = (value == null) ? 0 : value.length(); 1322 Vector<String> temp = new Vector<String>(4); 1323 1324 current = 0; 1325 while (current < length) { 1326 // Skip ws 1327 while (current < length && Character.isWhitespace 1328 (value.charAt(current))) { 1329 current++; 1330 } 1331 last = current; 1332 while (current < length && !Character.isWhitespace 1333 (value.charAt(current))) { 1334 current++; 1335 } 1336 if (last != current) { 1337 temp.addElement(value.substring(last, current)); 1338 } 1339 current++; 1340 } 1341 String[] retValue = new String[temp.size()]; 1342 temp.copyInto(retValue); 1343 return retValue; 1344 } 1345 1346 /** 1347 * Return the point size, given a size index. Legal HTML index sizes 1348 * are 1-7. 1349 */ 1350 float getPointSize(int index, StyleSheet ss) { 1351 ss = getStyleSheet(ss); 1352 int[] sizeMap = (ss != null) ? ss.getSizeMap() : 1353 StyleSheet.sizeMapDefault; 1354 --index; 1355 if (index < 0) 1356 return sizeMap[0]; 1357 else if (index > sizeMap.length - 1) 1358 return sizeMap[sizeMap.length - 1]; 1359 else 1360 return sizeMap[index]; 1361 } 1362 1363 1364 private void translateEmbeddedAttributes(AttributeSet htmlAttrSet, 1365 MutableAttributeSet cssAttrSet) { 1366 Enumeration keys = htmlAttrSet.getAttributeNames(); 1367 if (htmlAttrSet.getAttribute(StyleConstants.NameAttribute) == 1368 HTML.Tag.HR) { 1369 // HR needs special handling due to us treating it as a leaf. 1370 translateAttributes(HTML.Tag.HR, htmlAttrSet, cssAttrSet); 1371 } 1372 while (keys.hasMoreElements()) { 1373 Object key = keys.nextElement(); 1374 if (key instanceof HTML.Tag) { 1375 HTML.Tag tag = (HTML.Tag)key; 1376 Object o = htmlAttrSet.getAttribute(tag); 1377 if (o != null && o instanceof AttributeSet) { 1378 translateAttributes(tag, (AttributeSet)o, cssAttrSet); 1379 } 1380 } else if (key instanceof CSS.Attribute) { 1381 cssAttrSet.addAttribute(key, htmlAttrSet.getAttribute(key)); 1382 } 1383 } 1384 } 1385 1386 private void translateAttributes(HTML.Tag tag, 1387 AttributeSet htmlAttrSet, 1388 MutableAttributeSet cssAttrSet) { 1389 Enumeration names = htmlAttrSet.getAttributeNames(); 1390 while (names.hasMoreElements()) { 1391 Object name = names.nextElement(); 1392 1393 if (name instanceof HTML.Attribute) { 1394 HTML.Attribute key = (HTML.Attribute)name; 1395 1396 /* 1397 * HTML.Attribute.ALIGN needs special processing. 1398 * It can map to to 1 of many(3) possible CSS attributes 1399 * depending on the nature of the tag the attribute is 1400 * part off and depending on the value of the attribute. 1401 */ 1402 if (key == HTML.Attribute.ALIGN) { 1403 String htmlAttrValue = (String)htmlAttrSet.getAttribute(HTML.Attribute.ALIGN); 1404 if (htmlAttrValue != null) { 1405 CSS.Attribute cssAttr = getCssAlignAttribute(tag, htmlAttrSet); 1406 if (cssAttr != null) { 1407 Object o = getCssValue(cssAttr, htmlAttrValue); 1408 if (o != null) { 1409 cssAttrSet.addAttribute(cssAttr, o); 1410 } 1411 } 1412 } 1413 } else { 1414 if (key == HTML.Attribute.SIZE && !isHTMLFontTag(tag)) { 1415 /* 1416 * The html size attribute has a mapping in the CSS world only 1417 * if it is par of a font or base font tag. 1418 */ 1419 } else if (tag == HTML.Tag.TABLE && key == HTML.Attribute.BORDER) { 1420 int borderWidth = getTableBorder(htmlAttrSet); 1421 1422 if (borderWidth > 0) { 1423 translateAttribute(HTML.Attribute.BORDER, Integer.toString(borderWidth), cssAttrSet); 1424 } 1425 } else { 1426 translateAttribute(key, (String) htmlAttrSet.getAttribute(key), cssAttrSet); 1427 } 1428 } 1429 } else if (name instanceof CSS.Attribute) { 1430 cssAttrSet.addAttribute(name, htmlAttrSet.getAttribute(name)); 1431 } 1432 } 1433 } 1434 1435 private void translateAttribute(HTML.Attribute key, 1436 String htmlAttrValue, 1437 MutableAttributeSet cssAttrSet) { 1438 /* 1439 * In the case of all remaining HTML.Attribute's they 1440 * map to 1 or more CCS.Attribute. 1441 */ 1442 CSS.Attribute[] cssAttrList = getCssAttribute(key); 1443 1444 if (cssAttrList == null || htmlAttrValue == null) { 1445 return; 1446 } 1447 for (Attribute cssAttr : cssAttrList) { 1448 Object o = getCssValue(cssAttr, htmlAttrValue); 1449 if (o != null) { 1450 cssAttrSet.addAttribute(cssAttr , o); 1451 } 1452 } 1453 } 1454 1455 /** 1456 * Given a CSS.Attribute object and its corresponding HTML.Attribute's 1457 * value, this method returns a CssValue object to associate with the 1458 * CSS attribute. 1459 * 1460 * @param the CSS.Attribute 1461 * @param a String containing the value associated HTML.Attribtue. 1462 */ 1463 Object getCssValue(CSS.Attribute cssAttr, String htmlAttrValue) { 1464 CssValue value = (CssValue)valueConvertor.get(cssAttr); 1465 Object o = value.parseHtmlValue(htmlAttrValue); 1466 return o; 1467 } 1468 1469 /** 1470 * Maps an HTML.Attribute object to its appropriate CSS.Attributes. 1471 * 1472 * @param HTML.Attribute 1473 * @return CSS.Attribute[] 1474 */ 1475 private CSS.Attribute[] getCssAttribute(HTML.Attribute hAttr) { 1476 return htmlAttrToCssAttrMap.get(hAttr); 1477 } 1478 1479 /** 1480 * Maps HTML.Attribute.ALIGN to either: 1481 * CSS.Attribute.TEXT_ALIGN 1482 * CSS.Attribute.FLOAT 1483 * CSS.Attribute.VERTICAL_ALIGN 1484 * based on the tag associated with the attribute and the 1485 * value of the attribute. 1486 * 1487 * @param AttributeSet containing HTML attributes. 1488 * @return CSS.Attribute mapping for HTML.Attribute.ALIGN. 1489 */ 1490 private CSS.Attribute getCssAlignAttribute(HTML.Tag tag, 1491 AttributeSet htmlAttrSet) { 1492 return CSS.Attribute.TEXT_ALIGN; 1493 /* 1494 String htmlAttrValue = (String)htmlAttrSet.getAttribute(HTML.Attribute.ALIGN); 1495 CSS.Attribute cssAttr = CSS.Attribute.TEXT_ALIGN; 1496 if (htmlAttrValue != null && htmlAttrSet instanceof Element) { 1497 Element elem = (Element)htmlAttrSet; 1498 if (!elem.isLeaf() && tag.isBlock() && validTextAlignValue(htmlAttrValue)) { 1499 return CSS.Attribute.TEXT_ALIGN; 1500 } else if (isFloater(htmlAttrValue)) { 1501 return CSS.Attribute.FLOAT; 1502 } else if (elem.isLeaf()) { 1503 return CSS.Attribute.VERTICAL_ALIGN; 1504 } 1505 } 1506 return null; 1507 */ 1508 } 1509 1510 /** 1511 * Fetches the tag associated with the HTML AttributeSet. 1512 * 1513 * @param AttributeSet containing the HTML attributes. 1514 * @return HTML.Tag 1515 */ 1516 private HTML.Tag getHTMLTag(AttributeSet htmlAttrSet) { 1517 Object o = htmlAttrSet.getAttribute(StyleConstants.NameAttribute); 1518 if (o instanceof HTML.Tag) { 1519 HTML.Tag tag = (HTML.Tag) o; 1520 return tag; 1521 } 1522 return null; 1523 } 1524 1525 1526 private boolean isHTMLFontTag(HTML.Tag tag) { 1527 return (tag != null && ((tag == HTML.Tag.FONT) || (tag == HTML.Tag.BASEFONT))); 1528 } 1529 1530 1531 private boolean isFloater(String alignValue) { 1532 return (alignValue.equals("left") || alignValue.equals("right")); 1533 } 1534 1535 private boolean validTextAlignValue(String alignValue) { 1536 return (isFloater(alignValue) || alignValue.equals("center")); 1537 } 1538 1539 /** 1540 * Base class to CSS values in the attribute sets. This 1541 * is intended to act as a convertor to/from other attribute 1542 * formats. 1543 * <p> 1544 * The CSS parser uses the parseCssValue method to convert 1545 * a string to whatever format is appropriate a given key 1546 * (i.e. these convertors are stored in a map using the 1547 * CSS.Attribute as a key and the CssValue as the value). 1548 * <p> 1549 * The HTML to CSS conversion process first converts the 1550 * HTML.Attribute to a CSS.Attribute, and then calls 1551 * the parseHtmlValue method on the value of the HTML 1552 * attribute to produce the corresponding CSS value. 1553 * <p> 1554 * The StyleConstants to CSS conversion process first 1555 * converts the StyleConstants attribute to a 1556 * CSS.Attribute, and then calls the fromStyleConstants 1557 * method to convert the StyleConstants value to a 1558 * CSS value. 1559 * <p> 1560 * The CSS to StyleConstants conversion process first 1561 * converts the StyleConstants attribute to a 1562 * CSS.Attribute, and then calls the toStyleConstants 1563 * method to convert the CSS value to a StyleConstants 1564 * value. 1565 */ 1566 static class CssValue implements Serializable { 1567 1568 /** 1569 * Convert a CSS value string to the internal format 1570 * (for fast processing) used in the attribute sets. 1571 * The fallback storage for any value that we don't 1572 * have a special binary format for is a String. 1573 */ 1574 Object parseCssValue(String value) { 1575 return value; 1576 } 1577 1578 /** 1579 * Convert an HTML attribute value to a CSS attribute 1580 * value. If there is no conversion, return null. 1581 * This is implemented to simply forward to the CSS 1582 * parsing by default (since some of the attribute 1583 * values are the same). If the attribute value 1584 * isn't recognized as a CSS value it is generally 1585 * returned as null. 1586 */ 1587 Object parseHtmlValue(String value) { 1588 return parseCssValue(value); 1589 } 1590 1591 /** 1592 * Converts a <code>StyleConstants</code> attribute value to 1593 * a CSS attribute value. If there is no conversion, 1594 * returns <code>null</code>. By default, there is no conversion. 1595 * 1596 * @param key the <code>StyleConstants</code> attribute 1597 * @param value the value of a <code>StyleConstants</code> 1598 * attribute to be converted 1599 * @return the CSS value that represents the 1600 * <code>StyleConstants</code> value 1601 */ 1602 Object fromStyleConstants(StyleConstants key, Object value) { 1603 return null; 1604 } 1605 1606 /** 1607 * Converts a CSS attribute value to a 1608 * <code>StyleConstants</code> 1609 * value. If there is no conversion, returns 1610 * <code>null</code>. 1611 * By default, there is no conversion. 1612 * 1613 * @param key the <code>StyleConstants</code> attribute 1614 * @param v the view containing <code>AttributeSet</code> 1615 * @return the <code>StyleConstants</code> attribute value that 1616 * represents the CSS attribute value 1617 */ 1618 Object toStyleConstants(StyleConstants key, View v) { 1619 return null; 1620 } 1621 1622 /** 1623 * Return the CSS format of the value 1624 */ 1625 public String toString() { 1626 return svalue; 1627 } 1628 1629 /** 1630 * The value as a string... before conversion to a 1631 * binary format. 1632 */ 1633 String svalue; 1634 } 1635 1636 /** 1637 * By default CSS attributes are represented as simple 1638 * strings. They also have no conversion to/from 1639 * StyleConstants by default. This class represents the 1640 * value as a string (via the superclass), but 1641 * provides StyleConstants conversion support for the 1642 * CSS attributes that are held as strings. 1643 */ 1644 static class StringValue extends CssValue { 1645 1646 /** 1647 * Convert a CSS value string to the internal format 1648 * (for fast processing) used in the attribute sets. 1649 * This produces a StringValue, so that it can be 1650 * used to convert from CSS to StyleConstants values. 1651 */ 1652 Object parseCssValue(String value) { 1653 StringValue sv = new StringValue(); 1654 sv.svalue = value; 1655 return sv; 1656 } 1657 1658 /** 1659 * Converts a <code>StyleConstants</code> attribute value to 1660 * a CSS attribute value. If there is no conversion 1661 * returns <code>null</code>. 1662 * 1663 * @param key the <code>StyleConstants</code> attribute 1664 * @param value the value of a <code>StyleConstants</code> 1665 * attribute to be converted 1666 * @return the CSS value that represents the 1667 * <code>StyleConstants</code> value 1668 */ 1669 Object fromStyleConstants(StyleConstants key, Object value) { 1670 if (key == StyleConstants.Italic) { 1671 if (value.equals(Boolean.TRUE)) { 1672 return parseCssValue("italic"); 1673 } 1674 return parseCssValue(""); 1675 } else if (key == StyleConstants.Underline) { 1676 if (value.equals(Boolean.TRUE)) { 1677 return parseCssValue("underline"); 1678 } 1679 return parseCssValue(""); 1680 } else if (key == StyleConstants.Alignment) { 1681 int align = ((Integer)value).intValue(); 1682 String ta; 1683 switch(align) { 1684 case StyleConstants.ALIGN_LEFT: 1685 ta = "left"; 1686 break; 1687 case StyleConstants.ALIGN_RIGHT: 1688 ta = "right"; 1689 break; 1690 case StyleConstants.ALIGN_CENTER: 1691 ta = "center"; 1692 break; 1693 case StyleConstants.ALIGN_JUSTIFIED: 1694 ta = "justify"; 1695 break; 1696 default: 1697 ta = "left"; 1698 } 1699 return parseCssValue(ta); 1700 } else if (key == StyleConstants.StrikeThrough) { 1701 if (value.equals(Boolean.TRUE)) { 1702 return parseCssValue("line-through"); 1703 } 1704 return parseCssValue(""); 1705 } else if (key == StyleConstants.Superscript) { 1706 if (value.equals(Boolean.TRUE)) { 1707 return parseCssValue("super"); 1708 } 1709 return parseCssValue(""); 1710 } else if (key == StyleConstants.Subscript) { 1711 if (value.equals(Boolean.TRUE)) { 1712 return parseCssValue("sub"); 1713 } 1714 return parseCssValue(""); 1715 } 1716 return null; 1717 } 1718 1719 /** 1720 * Converts a CSS attribute value to a 1721 * <code>StyleConstants</code> value. 1722 * If there is no conversion, returns <code>null</code>. 1723 * By default, there is no conversion. 1724 * 1725 * @param key the <code>StyleConstants</code> attribute 1726 * @return the <code>StyleConstants</code> attribute value that 1727 * represents the CSS attribute value 1728 */ 1729 Object toStyleConstants(StyleConstants key, View v) { 1730 if (key == StyleConstants.Italic) { 1731 if (svalue.indexOf("italic") >= 0) { 1732 return Boolean.TRUE; 1733 } 1734 return Boolean.FALSE; 1735 } else if (key == StyleConstants.Underline) { 1736 if (svalue.indexOf("underline") >= 0) { 1737 return Boolean.TRUE; 1738 } 1739 return Boolean.FALSE; 1740 } else if (key == StyleConstants.Alignment) { 1741 if (svalue.equals("right")) { 1742 return new Integer(StyleConstants.ALIGN_RIGHT); 1743 } else if (svalue.equals("center")) { 1744 return new Integer(StyleConstants.ALIGN_CENTER); 1745 } else if (svalue.equals("justify")) { 1746 return new Integer(StyleConstants.ALIGN_JUSTIFIED); 1747 } 1748 return new Integer(StyleConstants.ALIGN_LEFT); 1749 } else if (key == StyleConstants.StrikeThrough) { 1750 if (svalue.indexOf("line-through") >= 0) { 1751 return Boolean.TRUE; 1752 } 1753 return Boolean.FALSE; 1754 } else if (key == StyleConstants.Superscript) { 1755 if (svalue.indexOf("super") >= 0) { 1756 return Boolean.TRUE; 1757 } 1758 return Boolean.FALSE; 1759 } else if (key == StyleConstants.Subscript) { 1760 if (svalue.indexOf("sub") >= 0) { 1761 return Boolean.TRUE; 1762 } 1763 return Boolean.FALSE; 1764 } 1765 return null; 1766 } 1767 1768 // Used by ViewAttributeSet 1769 boolean isItalic() { 1770 return (svalue.indexOf("italic") != -1); 1771 } 1772 1773 boolean isStrike() { 1774 return (svalue.indexOf("line-through") != -1); 1775 } 1776 1777 boolean isUnderline() { 1778 return (svalue.indexOf("underline") != -1); 1779 } 1780 1781 boolean isSub() { 1782 return (svalue.indexOf("sub") != -1); 1783 } 1784 1785 boolean isSup() { 1786 return (svalue.indexOf("sup") != -1); 1787 } 1788 } 1789 1790 /** 1791 * Represents a value for the CSS.FONT_SIZE attribute. 1792 * The binary format of the value can be one of several 1793 * types. If the type is Float, 1794 * the value is specified in terms of point or 1795 * percentage, depending upon the ending of the 1796 * associated string. 1797 * If the type is Integer, the value is specified 1798 * in terms of a size index. 1799 */ 1800 class FontSize extends CssValue { 1801 1802 /** 1803 * Returns the size in points. This is ultimately 1804 * what we need for the purpose of creating/fetching 1805 * a Font object. 1806 * 1807 * @param a the attribute set the value is being 1808 * requested from. We may need to walk up the 1809 * resolve hierarchy if it's relative. 1810 */ 1811 int getValue(AttributeSet a, StyleSheet ss) { 1812 ss = getStyleSheet(ss); 1813 if (index) { 1814 // it's an index, translate from size table 1815 return Math.round(getPointSize((int) value, ss)); 1816 } 1817 else if (lu == null) { 1818 return Math.round(value); 1819 } 1820 else { 1821 if (lu.type == 0) { 1822 boolean isW3CLengthUnits = (ss == null) ? false : ss.isW3CLengthUnits(); 1823 return Math.round(lu.getValue(isW3CLengthUnits)); 1824 } 1825 if (a != null) { 1826 AttributeSet resolveParent = a.getResolveParent(); 1827 1828 if (resolveParent != null) { 1829 int pValue = StyleConstants.getFontSize(resolveParent); 1830 1831 float retValue; 1832 if (lu.type == 1 || lu.type == 3) { 1833 retValue = lu.value * (float)pValue; 1834 } 1835 else { 1836 retValue = lu.value + (float)pValue; 1837 } 1838 return Math.round(retValue); 1839 } 1840 } 1841 // a is null, or no resolve parent. 1842 return 12; 1843 } 1844 } 1845 1846 Object parseCssValue(String value) { 1847 FontSize fs = new FontSize(); 1848 fs.svalue = value; 1849 try { 1850 if (value.equals("xx-small")) { 1851 fs.value = 1; 1852 fs.index = true; 1853 } else if (value.equals("x-small")) { 1854 fs.value = 2; 1855 fs.index = true; 1856 } else if (value.equals("small")) { 1857 fs.value = 3; 1858 fs.index = true; 1859 } else if (value.equals("medium")) { 1860 fs.value = 4; 1861 fs.index = true; 1862 } else if (value.equals("large")) { 1863 fs.value = 5; 1864 fs.index = true; 1865 } else if (value.equals("x-large")) { 1866 fs.value = 6; 1867 fs.index = true; 1868 } else if (value.equals("xx-large")) { 1869 fs.value = 7; 1870 fs.index = true; 1871 } else { 1872 fs.lu = new LengthUnit(value, (short)1, 1f); 1873 } 1874 // relative sizes, larger | smaller (adjust from parent by 1875 // 1.5 pixels) 1876 // em, ex refer to parent sizes 1877 // lengths: pt, mm, cm, pc, in, px 1878 // em (font height 3em would be 3 times font height) 1879 // ex (height of X) 1880 // lengths are (+/-) followed by a number and two letter 1881 // unit identifier 1882 } catch (NumberFormatException nfe) { 1883 fs = null; 1884 } 1885 return fs; 1886 } 1887 1888 Object parseHtmlValue(String value) { 1889 if ((value == null) || (value.length() == 0)) { 1890 return null; 1891 } 1892 FontSize fs = new FontSize(); 1893 fs.svalue = value; 1894 1895 try { 1896 /* 1897 * relative sizes in the size attribute are relative 1898 * to the <basefont>'s size. 1899 */ 1900 int baseFontSize = getBaseFontSize(); 1901 if (value.charAt(0) == '+') { 1902 int relSize = Integer.valueOf(value.substring(1)).intValue(); 1903 fs.value = baseFontSize + relSize; 1904 fs.index = true; 1905 } else if (value.charAt(0) == '-') { 1906 int relSize = -Integer.valueOf(value.substring(1)).intValue(); 1907 fs.value = baseFontSize + relSize; 1908 fs.index = true; 1909 } else { 1910 fs.value = Integer.parseInt(value); 1911 if (fs.value > 7) { 1912 fs.value = 7; 1913 } else if (fs.value < 0) { 1914 fs.value = 0; 1915 } 1916 fs.index = true; 1917 } 1918 1919 } catch (NumberFormatException nfe) { 1920 fs = null; 1921 } 1922 return fs; 1923 } 1924 1925 /** 1926 * Converts a <code>StyleConstants</code> attribute value to 1927 * a CSS attribute value. If there is no conversion 1928 * returns <code>null</code>. By default, there is no conversion. 1929 * 1930 * @param key the <code>StyleConstants</code> attribute 1931 * @param value the value of a <code>StyleConstants</code> 1932 * attribute to be converted 1933 * @return the CSS value that represents the 1934 * <code>StyleConstants</code> value 1935 */ 1936 Object fromStyleConstants(StyleConstants key, Object value) { 1937 if (value instanceof Number) { 1938 FontSize fs = new FontSize(); 1939 1940 fs.value = getIndexOfSize(((Number)value).floatValue(), StyleSheet.sizeMapDefault); 1941 fs.svalue = Integer.toString((int)fs.value); 1942 fs.index = true; 1943 return fs; 1944 } 1945 return parseCssValue(value.toString()); 1946 } 1947 1948 /** 1949 * Converts a CSS attribute value to a <code>StyleConstants</code> 1950 * value. If there is no conversion, returns <code>null</code>. 1951 * By default, there is no conversion. 1952 * 1953 * @param key the <code>StyleConstants</code> attribute 1954 * @return the <code>StyleConstants</code> attribute value that 1955 * represents the CSS attribute value 1956 */ 1957 Object toStyleConstants(StyleConstants key, View v) { 1958 if (v != null) { 1959 return Integer.valueOf(getValue(v.getAttributes(), null)); 1960 } 1961 return Integer.valueOf(getValue(null, null)); 1962 } 1963 1964 float value; 1965 boolean index; 1966 LengthUnit lu; 1967 } 1968 1969 static class FontFamily extends CssValue { 1970 1971 /** 1972 * Returns the font family to use. 1973 */ 1974 String getValue() { 1975 return family; 1976 } 1977 1978 Object parseCssValue(String value) { 1979 int cIndex = value.indexOf(','); 1980 FontFamily ff = new FontFamily(); 1981 ff.svalue = value; 1982 ff.family = null; 1983 1984 if (cIndex == -1) { 1985 setFontName(ff, value); 1986 } 1987 else { 1988 boolean done = false; 1989 int lastIndex; 1990 int length = value.length(); 1991 cIndex = 0; 1992 while (!done) { 1993 // skip ws. 1994 while (cIndex < length && 1995 Character.isWhitespace(value.charAt(cIndex))) 1996 cIndex++; 1997 // Find next ',' 1998 lastIndex = cIndex; 1999 cIndex = value.indexOf(',', cIndex); 2000 if (cIndex == -1) { 2001 cIndex = length; 2002 } 2003 if (lastIndex < length) { 2004 if (lastIndex != cIndex) { 2005 int lastCharIndex = cIndex; 2006 if (cIndex > 0 && value.charAt(cIndex - 1) == ' '){ 2007 lastCharIndex--; 2008 } 2009 setFontName(ff, value.substring 2010 (lastIndex, lastCharIndex)); 2011 done = (ff.family != null); 2012 } 2013 cIndex++; 2014 } 2015 else { 2016 done = true; 2017 } 2018 } 2019 } 2020 if (ff.family == null) { 2021 ff.family = Font.SANS_SERIF; 2022 } 2023 return ff; 2024 } 2025 2026 private void setFontName(FontFamily ff, String fontName) { 2027 ff.family = fontName; 2028 } 2029 2030 Object parseHtmlValue(String value) { 2031 // TBD 2032 return parseCssValue(value); 2033 } 2034 2035 /** 2036 * Converts a <code>StyleConstants</code> attribute value to 2037 * a CSS attribute value. If there is no conversion 2038 * returns <code>null</code>. By default, there is no conversion. 2039 * 2040 * @param key the <code>StyleConstants</code> attribute 2041 * @param value the value of a <code>StyleConstants</code> 2042 * attribute to be converted 2043 * @return the CSS value that represents the 2044 * <code>StyleConstants</code> value 2045 */ 2046 Object fromStyleConstants(StyleConstants key, Object value) { 2047 return parseCssValue(value.toString()); 2048 } 2049 2050 /** 2051 * Converts a CSS attribute value to a <code>StyleConstants</code> 2052 * value. If there is no conversion, returns <code>null</code>. 2053 * By default, there is no conversion. 2054 * 2055 * @param key the <code>StyleConstants</code> attribute 2056 * @return the <code>StyleConstants</code> attribute value that 2057 * represents the CSS attribute value 2058 */ 2059 Object toStyleConstants(StyleConstants key, View v) { 2060 return family; 2061 } 2062 2063 String family; 2064 } 2065 2066 static class FontWeight extends CssValue { 2067 2068 int getValue() { 2069 return weight; 2070 } 2071 2072 Object parseCssValue(String value) { 2073 FontWeight fw = new FontWeight(); 2074 fw.svalue = value; 2075 if (value.equals("bold")) { 2076 fw.weight = 700; 2077 } else if (value.equals("normal")) { 2078 fw.weight = 400; 2079 } else { 2080 // PENDING(prinz) add support for relative values 2081 try { 2082 fw.weight = Integer.parseInt(value); 2083 } catch (NumberFormatException nfe) { 2084 fw = null; 2085 } 2086 } 2087 return fw; 2088 } 2089 2090 /** 2091 * Converts a <code>StyleConstants</code> attribute value to 2092 * a CSS attribute value. If there is no conversion 2093 * returns <code>null</code>. By default, there is no conversion. 2094 * 2095 * @param key the <code>StyleConstants</code> attribute 2096 * @param value the value of a <code>StyleConstants</code> 2097 * attribute to be converted 2098 * @return the CSS value that represents the 2099 * <code>StyleConstants</code> value 2100 */ 2101 Object fromStyleConstants(StyleConstants key, Object value) { 2102 if (value.equals(Boolean.TRUE)) { 2103 return parseCssValue("bold"); 2104 } 2105 return parseCssValue("normal"); 2106 } 2107 2108 /** 2109 * Converts a CSS attribute value to a <code>StyleConstants</code> 2110 * value. If there is no conversion, returns <code>null</code>. 2111 * By default, there is no conversion. 2112 * 2113 * @param key the <code>StyleConstants</code> attribute 2114 * @return the <code>StyleConstants</code> attribute value that 2115 * represents the CSS attribute value 2116 */ 2117 Object toStyleConstants(StyleConstants key, View v) { 2118 return (weight > 500) ? Boolean.TRUE : Boolean.FALSE; 2119 } 2120 2121 boolean isBold() { 2122 return (weight > 500); 2123 } 2124 2125 int weight; 2126 } 2127 2128 static class ColorValue extends CssValue { 2129 2130 /** 2131 * Returns the color to use. 2132 */ 2133 Color getValue() { 2134 return c; 2135 } 2136 2137 Object parseCssValue(String value) { 2138 2139 Color c = stringToColor(value); 2140 if (c != null) { 2141 ColorValue cv = new ColorValue(); 2142 cv.svalue = value; 2143 cv.c = c; 2144 return cv; 2145 } 2146 return null; 2147 } 2148 2149 Object parseHtmlValue(String value) { 2150 return parseCssValue(value); 2151 } 2152 2153 /** 2154 * Converts a <code>StyleConstants</code> attribute value to 2155 * a CSS attribute value. If there is no conversion 2156 * returns <code>null</code>. By default, there is no conversion. 2157 * 2158 * @param key the <code>StyleConstants</code> attribute 2159 * @param value the value of a <code>StyleConstants</code> 2160 * attribute to be converted 2161 * @return the CSS value that represents the 2162 * <code>StyleConstants</code> value 2163 */ 2164 Object fromStyleConstants(StyleConstants key, Object value) { 2165 ColorValue colorValue = new ColorValue(); 2166 colorValue.c = (Color)value; 2167 colorValue.svalue = colorToHex(colorValue.c); 2168 return colorValue; 2169 } 2170 2171 /** 2172 * Converts a CSS attribute value to a <code>StyleConstants</code> 2173 * value. If there is no conversion, returns <code>null</code>. 2174 * By default, there is no conversion. 2175 * 2176 * @param key the <code>StyleConstants</code> attribute 2177 * @return the <code>StyleConstants</code> attribute value that 2178 * represents the CSS attribute value 2179 */ 2180 Object toStyleConstants(StyleConstants key, View v) { 2181 return c; 2182 } 2183 2184 Color c; 2185 } 2186 2187 static class BorderStyle extends CssValue { 2188 2189 CSS.Value getValue() { 2190 return style; 2191 } 2192 2193 Object parseCssValue(String value) { 2194 CSS.Value cssv = CSS.getValue(value); 2195 if (cssv != null) { 2196 if ((cssv == CSS.Value.INSET) || 2197 (cssv == CSS.Value.OUTSET) || 2198 (cssv == CSS.Value.NONE) || 2199 (cssv == CSS.Value.DOTTED) || 2200 (cssv == CSS.Value.DASHED) || 2201 (cssv == CSS.Value.SOLID) || 2202 (cssv == CSS.Value.DOUBLE) || 2203 (cssv == CSS.Value.GROOVE) || 2204 (cssv == CSS.Value.RIDGE)) { 2205 2206 BorderStyle bs = new BorderStyle(); 2207 bs.svalue = value; 2208 bs.style = cssv; 2209 return bs; 2210 } 2211 } 2212 return null; 2213 } 2214 2215 private void writeObject(java.io.ObjectOutputStream s) 2216 throws IOException { 2217 s.defaultWriteObject(); 2218 if (style == null) { 2219 s.writeObject(null); 2220 } 2221 else { 2222 s.writeObject(style.toString()); 2223 } 2224 } 2225 2226 private void readObject(ObjectInputStream s) 2227 throws ClassNotFoundException, IOException { 2228 s.defaultReadObject(); 2229 Object value = s.readObject(); 2230 if (value != null) { 2231 style = CSS.getValue((String)value); 2232 } 2233 } 2234 2235 // CSS.Values are static, don't archive it. 2236 transient private CSS.Value style; 2237 } 2238 2239 static class LengthValue extends CssValue { 2240 2241 /** 2242 * if this length value may be negative. 2243 */ 2244 boolean mayBeNegative; 2245 2246 LengthValue() { 2247 this(false); 2248 } 2249 2250 LengthValue(boolean mayBeNegative) { 2251 this.mayBeNegative = mayBeNegative; 2252 } 2253 2254 /** 2255 * Returns the length (span) to use. 2256 */ 2257 float getValue() { 2258 return getValue(false); 2259 } 2260 2261 float getValue(boolean isW3CLengthUnits) { 2262 return getValue(0, isW3CLengthUnits); 2263 } 2264 2265 /** 2266 * Returns the length (span) to use. If the value represents 2267 * a percentage, it is scaled based on <code>currentValue</code>. 2268 */ 2269 float getValue(float currentValue) { 2270 return getValue(currentValue, false); 2271 } 2272 float getValue(float currentValue, boolean isW3CLengthUnits) { 2273 if (percentage) { 2274 return span * currentValue; 2275 } 2276 return LengthUnit.getValue(span, units, isW3CLengthUnits); 2277 } 2278 2279 /** 2280 * Returns true if the length represents a percentage of the 2281 * containing box. 2282 */ 2283 boolean isPercentage() { 2284 return percentage; 2285 } 2286 2287 Object parseCssValue(String value) { 2288 LengthValue lv; 2289 try { 2290 // Assume pixels 2291 float absolute = Float.valueOf(value).floatValue(); 2292 lv = new LengthValue(); 2293 lv.span = absolute; 2294 } catch (NumberFormatException nfe) { 2295 // Not pixels, use LengthUnit 2296 LengthUnit lu = new LengthUnit(value, 2297 LengthUnit.UNINITALIZED_LENGTH, 2298 0); 2299 2300 // PENDING: currently, we only support absolute values and 2301 // percentages. 2302 switch (lu.type) { 2303 case 0: 2304 // Absolute 2305 lv = new LengthValue(); 2306 lv.span = 2307 (mayBeNegative) ? lu.value : Math.max(0, lu.value); 2308 lv.units = lu.units; 2309 break; 2310 case 1: 2311 // % 2312 lv = new LengthValue(); 2313 lv.span = Math.max(0, Math.min(1, lu.value)); 2314 lv.percentage = true; 2315 break; 2316 default: 2317 return null; 2318 } 2319 } 2320 lv.svalue = value; 2321 return lv; 2322 } 2323 2324 Object parseHtmlValue(String value) { 2325 if (value.equals(HTML.NULL_ATTRIBUTE_VALUE)) { 2326 value = "1"; 2327 } 2328 return parseCssValue(value); 2329 } 2330 /** 2331 * Converts a <code>StyleConstants</code> attribute value to 2332 * a CSS attribute value. If there is no conversion, 2333 * returns <code>null</code>. By default, there is no conversion. 2334 * 2335 * @param key the <code>StyleConstants</code> attribute 2336 * @param value the value of a <code>StyleConstants</code> 2337 * attribute to be converted 2338 * @return the CSS value that represents the 2339 * <code>StyleConstants</code> value 2340 */ 2341 Object fromStyleConstants(StyleConstants key, Object value) { 2342 LengthValue v = new LengthValue(); 2343 v.svalue = value.toString(); 2344 v.span = ((Float)value).floatValue(); 2345 return v; 2346 } 2347 2348 /** 2349 * Converts a CSS attribute value to a <code>StyleConstants</code> 2350 * value. If there is no conversion, returns <code>null</code>. 2351 * By default, there is no conversion. 2352 * 2353 * @param key the <code>StyleConstants</code> attribute 2354 * @return the <code>StyleConstants</code> attribute value that 2355 * represents the CSS attribute value 2356 */ 2357 Object toStyleConstants(StyleConstants key, View v) { 2358 return new Float(getValue(false)); 2359 } 2360 2361 /** If true, span is a percentage value, and that to determine 2362 * the length another value needs to be passed in. */ 2363 boolean percentage; 2364 /** Either the absolute value (percentage == false) or 2365 * a percentage value. */ 2366 float span; 2367 2368 String units = null; 2369 } 2370 2371 2372 /** 2373 * BorderWidthValue is used to model BORDER_XXX_WIDTH and adds support 2374 * for the thin/medium/thick values. 2375 */ 2376 static class BorderWidthValue extends LengthValue { 2377 BorderWidthValue(String svalue, int index) { 2378 this.svalue = svalue; 2379 span = values[index]; 2380 percentage = false; 2381 } 2382 2383 Object parseCssValue(String value) { 2384 if (value != null) { 2385 if (value.equals("thick")) { 2386 return new BorderWidthValue(value, 2); 2387 } 2388 else if (value.equals("medium")) { 2389 return new BorderWidthValue(value, 1); 2390 } 2391 else if (value.equals("thin")) { 2392 return new BorderWidthValue(value, 0); 2393 } 2394 } 2395 // Assume its a length. 2396 return super.parseCssValue(value); 2397 } 2398 2399 Object parseHtmlValue(String value) { 2400 if (value == HTML.NULL_ATTRIBUTE_VALUE) { 2401 return parseCssValue("medium"); 2402 } 2403 return parseCssValue(value); 2404 } 2405 2406 /** Values used to represent border width. */ 2407 private static final float[] values = { 1, 2, 4 }; 2408 } 2409 2410 2411 /** 2412 * Handles uniquing of CSS values, like lists, and background image 2413 * repeating. 2414 */ 2415 static class CssValueMapper extends CssValue { 2416 Object parseCssValue(String value) { 2417 Object retValue = cssValueToInternalValueMap.get(value); 2418 if (retValue == null) { 2419 retValue = cssValueToInternalValueMap.get(value.toLowerCase()); 2420 } 2421 return retValue; 2422 } 2423 2424 2425 Object parseHtmlValue(String value) { 2426 Object retValue = htmlValueToCssValueMap.get(value); 2427 if (retValue == null) { 2428 retValue = htmlValueToCssValueMap.get(value.toLowerCase()); 2429 } 2430 return retValue; 2431 } 2432 } 2433 2434 2435 /** 2436 * Used for background images, to represent the position. 2437 */ 2438 static class BackgroundPosition extends CssValue { 2439 float horizontalPosition; 2440 float verticalPosition; 2441 // bitmask: bit 0, horizontal relative, bit 1 horizontal relative to 2442 // font size, 2 vertical relative to size, 3 vertical relative to 2443 // font size. 2444 // 2445 short relative; 2446 2447 Object parseCssValue(String value) { 2448 // 'top left' and 'left top' both mean the same as '0% 0%'. 2449 // 'top', 'top center' and 'center top' mean the same as '50% 0%'. 2450 // 'right top' and 'top right' mean the same as '100% 0%'. 2451 // 'left', 'left center' and 'center left' mean the same as 2452 // '0% 50%'. 2453 // 'center' and 'center center' mean the same as '50% 50%'. 2454 // 'right', 'right center' and 'center right' mean the same as 2455 // '100% 50%'. 2456 // 'bottom left' and 'left bottom' mean the same as '0% 100%'. 2457 // 'bottom', 'bottom center' and 'center bottom' mean the same as 2458 // '50% 100%'. 2459 // 'bottom right' and 'right bottom' mean the same as '100% 100%'. 2460 String[] strings = CSS.parseStrings(value); 2461 int count = strings.length; 2462 BackgroundPosition bp = new BackgroundPosition(); 2463 bp.relative = 5; 2464 bp.svalue = value; 2465 2466 if (count > 0) { 2467 // bit 0 for vert, 1 hor, 2 for center 2468 short found = 0; 2469 int index = 0; 2470 while (index < count) { 2471 // First, check for keywords 2472 String string = strings[index++]; 2473 if (string.equals("center")) { 2474 found |= 4; 2475 continue; 2476 } 2477 else { 2478 if ((found & 1) == 0) { 2479 if (string.equals("top")) { 2480 found |= 1; 2481 } 2482 else if (string.equals("bottom")) { 2483 found |= 1; 2484 bp.verticalPosition = 1; 2485 continue; 2486 } 2487 } 2488 if ((found & 2) == 0) { 2489 if (string.equals("left")) { 2490 found |= 2; 2491 bp.horizontalPosition = 0; 2492 } 2493 else if (string.equals("right")) { 2494 found |= 2; 2495 bp.horizontalPosition = 1; 2496 } 2497 } 2498 } 2499 } 2500 if (found != 0) { 2501 if ((found & 1) == 1) { 2502 if ((found & 2) == 0) { 2503 // vert and no horiz. 2504 bp.horizontalPosition = .5f; 2505 } 2506 } 2507 else if ((found & 2) == 2) { 2508 // horiz and no vert. 2509 bp.verticalPosition = .5f; 2510 } 2511 else { 2512 // no horiz, no vert, but center 2513 bp.horizontalPosition = bp.verticalPosition = .5f; 2514 } 2515 } 2516 else { 2517 // Assume lengths 2518 LengthUnit lu = new LengthUnit(strings[0], (short)0, 0f); 2519 2520 if (lu.type == 0) { 2521 bp.horizontalPosition = lu.value; 2522 bp.relative = (short)(1 ^ bp.relative); 2523 } 2524 else if (lu.type == 1) { 2525 bp.horizontalPosition = lu.value; 2526 } 2527 else if (lu.type == 3) { 2528 bp.horizontalPosition = lu.value; 2529 bp.relative = (short)((1 ^ bp.relative) | 2); 2530 } 2531 if (count > 1) { 2532 lu = new LengthUnit(strings[1], (short)0, 0f); 2533 2534 if (lu.type == 0) { 2535 bp.verticalPosition = lu.value; 2536 bp.relative = (short)(4 ^ bp.relative); 2537 } 2538 else if (lu.type == 1) { 2539 bp.verticalPosition = lu.value; 2540 } 2541 else if (lu.type == 3) { 2542 bp.verticalPosition = lu.value; 2543 bp.relative = (short)((4 ^ bp.relative) | 8); 2544 } 2545 } 2546 else { 2547 bp.verticalPosition = .5f; 2548 } 2549 } 2550 } 2551 return bp; 2552 } 2553 2554 boolean isHorizontalPositionRelativeToSize() { 2555 return ((relative & 1) == 1); 2556 } 2557 2558 boolean isHorizontalPositionRelativeToFontSize() { 2559 return ((relative & 2) == 2); 2560 } 2561 2562 float getHorizontalPosition() { 2563 return horizontalPosition; 2564 } 2565 2566 boolean isVerticalPositionRelativeToSize() { 2567 return ((relative & 4) == 4); 2568 } 2569 2570 boolean isVerticalPositionRelativeToFontSize() { 2571 return ((relative & 8) == 8); 2572 } 2573 2574 float getVerticalPosition() { 2575 return verticalPosition; 2576 } 2577 } 2578 2579 2580 /** 2581 * Used for BackgroundImages. 2582 */ 2583 static class BackgroundImage extends CssValue { 2584 private boolean loadedImage; 2585 private ImageIcon image; 2586 2587 Object parseCssValue(String value) { 2588 BackgroundImage retValue = new BackgroundImage(); 2589 retValue.svalue = value; 2590 return retValue; 2591 } 2592 2593 Object parseHtmlValue(String value) { 2594 return parseCssValue(value); 2595 } 2596 2597 // PENDING: this base is wrong for linked style sheets. 2598 ImageIcon getImage(URL base) { 2599 if (!loadedImage) { 2600 synchronized(this) { 2601 if (!loadedImage) { 2602 URL url = CSS.getURL(base, svalue); 2603 loadedImage = true; 2604 if (url != null) { 2605 image = new ImageIcon(); 2606 Image tmpImg = Toolkit.getDefaultToolkit().createImage(url); 2607 if (tmpImg != null) { 2608 image.setImage(tmpImg); 2609 } 2610 } 2611 } 2612 } 2613 } 2614 return image; 2615 } 2616 } 2617 2618 /** 2619 * Parses a length value, this is used internally, and never added 2620 * to an AttributeSet or returned to the developer. 2621 */ 2622 static class LengthUnit implements Serializable { 2623 static Hashtable<String, Float> lengthMapping = new Hashtable<String, Float>(6); 2624 static Hashtable<String, Float> w3cLengthMapping = new Hashtable<String, Float>(6); 2625 static { 2626 lengthMapping.put("pt", new Float(1f)); 2627 // Not sure about 1.3, determined by experiementation. 2628 lengthMapping.put("px", new Float(1.3f)); 2629 lengthMapping.put("mm", new Float(2.83464f)); 2630 lengthMapping.put("cm", new Float(28.3464f)); 2631 lengthMapping.put("pc", new Float(12f)); 2632 lengthMapping.put("in", new Float(72f)); 2633 int res = 72; 2634 try { 2635 res = Toolkit.getDefaultToolkit().getScreenResolution(); 2636 } catch (HeadlessException e) { 2637 } 2638 // mapping according to the CSS2 spec 2639 w3cLengthMapping.put("pt", new Float(res/72f)); 2640 w3cLengthMapping.put("px", new Float(1f)); 2641 w3cLengthMapping.put("mm", new Float(res/25.4f)); 2642 w3cLengthMapping.put("cm", new Float(res/2.54f)); 2643 w3cLengthMapping.put("pc", new Float(res/6f)); 2644 w3cLengthMapping.put("in", new Float(res)); 2645 } 2646 2647 LengthUnit(String value, short defaultType, float defaultValue) { 2648 parse(value, defaultType, defaultValue); 2649 } 2650 2651 void parse(String value, short defaultType, float defaultValue) { 2652 type = defaultType; 2653 this.value = defaultValue; 2654 2655 int length = value.length(); 2656 if (length > 0 && value.charAt(length - 1) == '%') { 2657 try { 2658 this.value = Float.valueOf(value.substring(0, length - 1)). 2659 floatValue() / 100.0f; 2660 type = 1; 2661 } 2662 catch (NumberFormatException nfe) { } 2663 } 2664 if (length >= 2) { 2665 units = value.substring(length - 2, length); 2666 Float scale = lengthMapping.get(units); 2667 if (scale != null) { 2668 try { 2669 this.value = Float.valueOf(value.substring(0, 2670 length - 2)).floatValue(); 2671 type = 0; 2672 } 2673 catch (NumberFormatException nfe) { } 2674 } 2675 else if (units.equals("em") || 2676 units.equals("ex")) { 2677 try { 2678 this.value = Float.valueOf(value.substring(0, 2679 length - 2)).floatValue(); 2680 type = 3; 2681 } 2682 catch (NumberFormatException nfe) { } 2683 } 2684 else if (value.equals("larger")) { 2685 this.value = 2f; 2686 type = 2; 2687 } 2688 else if (value.equals("smaller")) { 2689 this.value = -2; 2690 type = 2; 2691 } 2692 else { 2693 // treat like points. 2694 try { 2695 this.value = Float.valueOf(value).floatValue(); 2696 type = 0; 2697 } catch (NumberFormatException nfe) {} 2698 } 2699 } 2700 else if (length > 0) { 2701 // treat like points. 2702 try { 2703 this.value = Float.valueOf(value).floatValue(); 2704 type = 0; 2705 } catch (NumberFormatException nfe) {} 2706 } 2707 } 2708 2709 float getValue(boolean w3cLengthUnits) { 2710 Hashtable<String, Float> mapping = (w3cLengthUnits) ? w3cLengthMapping : lengthMapping; 2711 float scale = 1; 2712 if (units != null) { 2713 Float scaleFloat = mapping.get(units); 2714 if (scaleFloat != null) { 2715 scale = scaleFloat.floatValue(); 2716 } 2717 } 2718 return this.value * scale; 2719 2720 } 2721 2722 static float getValue(float value, String units, Boolean w3cLengthUnits) { 2723 Hashtable<String, Float> mapping = (w3cLengthUnits) ? w3cLengthMapping : lengthMapping; 2724 float scale = 1; 2725 if (units != null) { 2726 Float scaleFloat = mapping.get(units); 2727 if (scaleFloat != null) { 2728 scale = scaleFloat.floatValue(); 2729 } 2730 } 2731 return value * scale; 2732 } 2733 2734 public String toString() { 2735 return type + " " + value; 2736 } 2737 2738 // 0 - value indicates real value 2739 // 1 - % value, value relative to depends upon key. 2740 // 50% will have a value = .5 2741 // 2 - add value to parent value. 2742 // 3 - em/ex relative to font size of element (except for 2743 // font-size, which is relative to parent). 2744 short type; 2745 float value; 2746 String units = null; 2747 2748 2749 static final short UNINITALIZED_LENGTH = (short)10; 2750 } 2751 2752 2753 /** 2754 * Class used to parse font property. The font property is shorthand 2755 * for the other font properties. This expands the properties, placing 2756 * them in the attributeset. 2757 */ 2758 static class ShorthandFontParser { 2759 /** 2760 * Parses the shorthand font string <code>value</code>, placing the 2761 * result in <code>attr</code>. 2762 */ 2763 static void parseShorthandFont(CSS css, String value, 2764 MutableAttributeSet attr) { 2765 // font is of the form: 2766 // [ <font-style> || <font-variant> || <font-weight> ]? <font-size> 2767 // [ / <line-height> ]? <font-family> 2768 String[] strings = CSS.parseStrings(value); 2769 int count = strings.length; 2770 int index = 0; 2771 // bitmask, 1 for style, 2 for variant, 3 for weight 2772 short found = 0; 2773 int maxC = Math.min(3, count); 2774 2775 // Check for font-style font-variant font-weight 2776 while (index < maxC) { 2777 if ((found & 1) == 0 && isFontStyle(strings[index])) { 2778 css.addInternalCSSValue(attr, CSS.Attribute.FONT_STYLE, 2779 strings[index++]); 2780 found |= 1; 2781 } 2782 else if ((found & 2) == 0 && isFontVariant(strings[index])) { 2783 css.addInternalCSSValue(attr, CSS.Attribute.FONT_VARIANT, 2784 strings[index++]); 2785 found |= 2; 2786 } 2787 else if ((found & 4) == 0 && isFontWeight(strings[index])) { 2788 css.addInternalCSSValue(attr, CSS.Attribute.FONT_WEIGHT, 2789 strings[index++]); 2790 found |= 4; 2791 } 2792 else if (strings[index].equals("normal")) { 2793 index++; 2794 } 2795 else { 2796 break; 2797 } 2798 } 2799 if ((found & 1) == 0) { 2800 css.addInternalCSSValue(attr, CSS.Attribute.FONT_STYLE, 2801 "normal"); 2802 } 2803 if ((found & 2) == 0) { 2804 css.addInternalCSSValue(attr, CSS.Attribute.FONT_VARIANT, 2805 "normal"); 2806 } 2807 if ((found & 4) == 0) { 2808 css.addInternalCSSValue(attr, CSS.Attribute.FONT_WEIGHT, 2809 "normal"); 2810 } 2811 2812 // string at index should be the font-size 2813 if (index < count) { 2814 String fontSize = strings[index]; 2815 int slashIndex = fontSize.indexOf('/'); 2816 2817 if (slashIndex != -1) { 2818 fontSize = fontSize.substring(0, slashIndex); 2819 strings[index] = strings[index].substring(slashIndex); 2820 } 2821 else { 2822 index++; 2823 } 2824 css.addInternalCSSValue(attr, CSS.Attribute.FONT_SIZE, 2825 fontSize); 2826 } 2827 else { 2828 css.addInternalCSSValue(attr, CSS.Attribute.FONT_SIZE, 2829 "medium"); 2830 } 2831 2832 // Check for line height 2833 if (index < count && strings[index].startsWith("/")) { 2834 String lineHeight = null; 2835 if (strings[index].equals("/")) { 2836 if (++index < count) { 2837 lineHeight = strings[index++]; 2838 } 2839 } 2840 else { 2841 lineHeight = strings[index++].substring(1); 2842 } 2843 // line height 2844 if (lineHeight != null) { 2845 css.addInternalCSSValue(attr, CSS.Attribute.LINE_HEIGHT, 2846 lineHeight); 2847 } 2848 else { 2849 css.addInternalCSSValue(attr, CSS.Attribute.LINE_HEIGHT, 2850 "normal"); 2851 } 2852 } 2853 else { 2854 css.addInternalCSSValue(attr, CSS.Attribute.LINE_HEIGHT, 2855 "normal"); 2856 } 2857 2858 // remainder of strings are font-family 2859 if (index < count) { 2860 String family = strings[index++]; 2861 2862 while (index < count) { 2863 family += " " + strings[index++]; 2864 } 2865 css.addInternalCSSValue(attr, CSS.Attribute.FONT_FAMILY, 2866 family); 2867 } 2868 else { 2869 css.addInternalCSSValue(attr, CSS.Attribute.FONT_FAMILY, 2870 Font.SANS_SERIF); 2871 } 2872 } 2873 2874 private static boolean isFontStyle(String string) { 2875 return (string.equals("italic") || 2876 string.equals("oblique")); 2877 } 2878 2879 private static boolean isFontVariant(String string) { 2880 return (string.equals("small-caps")); 2881 } 2882 2883 private static boolean isFontWeight(String string) { 2884 if (string.equals("bold") || string.equals("bolder") || 2885 string.equals("italic") || string.equals("lighter")) { 2886 return true; 2887 } 2888 // test for 100-900 2889 return (string.length() == 3 && 2890 string.charAt(0) >= '1' && string.charAt(0) <= '9' && 2891 string.charAt(1) == '0' && string.charAt(2) == '0'); 2892 } 2893 2894 } 2895 2896 2897 /** 2898 * Parses the background property into its intrinsic values. 2899 */ 2900 static class ShorthandBackgroundParser { 2901 /** 2902 * Parses the shorthand font string <code>value</code>, placing the 2903 * result in <code>attr</code>. 2904 */ 2905 static void parseShorthandBackground(CSS css, String value, 2906 MutableAttributeSet attr) { 2907 String[] strings = parseStrings(value); 2908 int count = strings.length; 2909 int index = 0; 2910 // bitmask: 0 for image, 1 repeat, 2 attachment, 3 position, 2911 // 4 color 2912 short found = 0; 2913 2914 while (index < count) { 2915 String string = strings[index++]; 2916 if ((found & 1) == 0 && isImage(string)) { 2917 css.addInternalCSSValue(attr, CSS.Attribute. 2918 BACKGROUND_IMAGE, string); 2919 found |= 1; 2920 } 2921 else if ((found & 2) == 0 && isRepeat(string)) { 2922 css.addInternalCSSValue(attr, CSS.Attribute. 2923 BACKGROUND_REPEAT, string); 2924 found |= 2; 2925 } 2926 else if ((found & 4) == 0 && isAttachment(string)) { 2927 css.addInternalCSSValue(attr, CSS.Attribute. 2928 BACKGROUND_ATTACHMENT, string); 2929 found |= 4; 2930 } 2931 else if ((found & 8) == 0 && isPosition(string)) { 2932 if (index < count && isPosition(strings[index])) { 2933 css.addInternalCSSValue(attr, CSS.Attribute. 2934 BACKGROUND_POSITION, 2935 string + " " + 2936 strings[index++]); 2937 } 2938 else { 2939 css.addInternalCSSValue(attr, CSS.Attribute. 2940 BACKGROUND_POSITION, string); 2941 } 2942 found |= 8; 2943 } 2944 else if ((found & 16) == 0 && isColor(string)) { 2945 css.addInternalCSSValue(attr, CSS.Attribute. 2946 BACKGROUND_COLOR, string); 2947 found |= 16; 2948 } 2949 } 2950 if ((found & 1) == 0) { 2951 css.addInternalCSSValue(attr, CSS.Attribute.BACKGROUND_IMAGE, 2952 null); 2953 } 2954 if ((found & 2) == 0) { 2955 css.addInternalCSSValue(attr, CSS.Attribute.BACKGROUND_REPEAT, 2956 "repeat"); 2957 } 2958 if ((found & 4) == 0) { 2959 css.addInternalCSSValue(attr, CSS.Attribute. 2960 BACKGROUND_ATTACHMENT, "scroll"); 2961 } 2962 if ((found & 8) == 0) { 2963 css.addInternalCSSValue(attr, CSS.Attribute. 2964 BACKGROUND_POSITION, null); 2965 } 2966 // Currently, there is no good way to express this. 2967 /* 2968 if ((found & 16) == 0) { 2969 css.addInternalCSSValue(attr, CSS.Attribute.BACKGROUND_COLOR, 2970 null); 2971 } 2972 */ 2973 } 2974 2975 static boolean isImage(String string) { 2976 return (string.startsWith("url(") && string.endsWith(")")); 2977 } 2978 2979 static boolean isRepeat(String string) { 2980 return (string.equals("repeat-x") || string.equals("repeat-y") || 2981 string.equals("repeat") || string.equals("no-repeat")); 2982 } 2983 2984 static boolean isAttachment(String string) { 2985 return (string.equals("fixed") || string.equals("scroll")); 2986 } 2987 2988 static boolean isPosition(String string) { 2989 return (string.equals("top") || string.equals("bottom") || 2990 string.equals("left") || string.equals("right") || 2991 string.equals("center") || 2992 (string.length() > 0 && 2993 Character.isDigit(string.charAt(0)))); 2994 } 2995 2996 static boolean isColor(String string) { 2997 return (CSS.stringToColor(string) != null); 2998 } 2999 } 3000 3001 3002 /** 3003 * Used to parser margin and padding. 3004 */ 3005 static class ShorthandMarginParser { 3006 /** 3007 * Parses the shorthand margin/padding/border string 3008 * <code>value</code>, placing the result in <code>attr</code>. 3009 * <code>names</code> give the 4 instrinsic property names. 3010 */ 3011 static void parseShorthandMargin(CSS css, String value, 3012 MutableAttributeSet attr, 3013 CSS.Attribute[] names) { 3014 String[] strings = parseStrings(value); 3015 int count = strings.length; 3016 int index = 0; 3017 switch (count) { 3018 case 0: 3019 // empty string 3020 return; 3021 case 1: 3022 // Identifies all values. 3023 for (int counter = 0; counter < 4; counter++) { 3024 css.addInternalCSSValue(attr, names[counter], strings[0]); 3025 } 3026 break; 3027 case 2: 3028 // 0 & 2 = strings[0], 1 & 3 = strings[1] 3029 css.addInternalCSSValue(attr, names[0], strings[0]); 3030 css.addInternalCSSValue(attr, names[2], strings[0]); 3031 css.addInternalCSSValue(attr, names[1], strings[1]); 3032 css.addInternalCSSValue(attr, names[3], strings[1]); 3033 break; 3034 case 3: 3035 css.addInternalCSSValue(attr, names[0], strings[0]); 3036 css.addInternalCSSValue(attr, names[1], strings[1]); 3037 css.addInternalCSSValue(attr, names[2], strings[2]); 3038 css.addInternalCSSValue(attr, names[3], strings[1]); 3039 break; 3040 default: 3041 for (int counter = 0; counter < 4; counter++) { 3042 css.addInternalCSSValue(attr, names[counter], 3043 strings[counter]); 3044 } 3045 break; 3046 } 3047 } 3048 } 3049 3050 static class ShorthandBorderParser { 3051 static Attribute[] keys = { 3052 Attribute.BORDER_TOP, Attribute.BORDER_RIGHT, 3053 Attribute.BORDER_BOTTOM, Attribute.BORDER_LEFT, 3054 }; 3055 3056 static void parseShorthandBorder(MutableAttributeSet attributes, 3057 CSS.Attribute key, String value) { 3058 Object[] parts = new Object[CSSBorder.PARSERS.length]; 3059 String[] strings = parseStrings(value); 3060 for (String s : strings) { 3061 boolean valid = false; 3062 for (int i = 0; i < parts.length; i++) { 3063 Object v = CSSBorder.PARSERS[i].parseCssValue(s); 3064 if (v != null) { 3065 if (parts[i] == null) { 3066 parts[i] = v; 3067 valid = true; 3068 } 3069 break; 3070 } 3071 } 3072 if (!valid) { 3073 // Part is non-parseable or occurred more than once. 3074 return; 3075 } 3076 } 3077 3078 // Unspecified parts get default values. 3079 for (int i = 0; i < parts.length; i++) { 3080 if (parts[i] == null) { 3081 parts[i] = CSSBorder.DEFAULTS[i]; 3082 } 3083 } 3084 3085 // Dispatch collected values to individual properties. 3086 for (int i = 0; i < keys.length; i++) { 3087 if ((key == Attribute.BORDER) || (key == keys[i])) { 3088 for (int k = 0; k < parts.length; k++) { 3089 attributes.addAttribute( 3090 CSSBorder.ATTRIBUTES[k][i], parts[k]); 3091 } 3092 } 3093 } 3094 } 3095 } 3096 3097 /** 3098 * Calculate the requirements needed to tile the requirements 3099 * given by the iterator that would be tiled. The calculation 3100 * takes into consideration margin and border spacing. 3101 */ 3102 static SizeRequirements calculateTiledRequirements(LayoutIterator iter, SizeRequirements r) { 3103 long minimum = 0; 3104 long maximum = 0; 3105 long preferred = 0; 3106 int lastMargin = 0; 3107 int totalSpacing = 0; 3108 int n = iter.getCount(); 3109 for (int i = 0; i < n; i++) { 3110 iter.setIndex(i); 3111 int margin0 = lastMargin; 3112 int margin1 = (int) iter.getLeadingCollapseSpan(); 3113 totalSpacing += Math.max(margin0, margin1); 3114 preferred += (int) iter.getPreferredSpan(0); 3115 minimum += iter.getMinimumSpan(0); 3116 maximum += iter.getMaximumSpan(0); 3117 3118 lastMargin = (int) iter.getTrailingCollapseSpan(); 3119 } 3120 totalSpacing += lastMargin; 3121 totalSpacing += 2 * iter.getBorderWidth(); 3122 3123 // adjust for the spacing area 3124 minimum += totalSpacing; 3125 preferred += totalSpacing; 3126 maximum += totalSpacing; 3127 3128 // set return value 3129 if (r == null) { 3130 r = new SizeRequirements(); 3131 } 3132 r.minimum = (minimum > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)minimum; 3133 r.preferred = (preferred > Integer.MAX_VALUE) ? Integer.MAX_VALUE :(int) preferred; 3134 r.maximum = (maximum > Integer.MAX_VALUE) ? Integer.MAX_VALUE :(int) maximum; 3135 return r; 3136 } 3137 3138 /** 3139 * Calculate a tiled layout for the given iterator. 3140 * This should be done collapsing the neighboring 3141 * margins to be a total of the maximum of the two 3142 * neighboring margin areas as described in the CSS spec. 3143 */ 3144 static void calculateTiledLayout(LayoutIterator iter, int targetSpan) { 3145 3146 /* 3147 * first pass, calculate the preferred sizes, adjustments needed because 3148 * of margin collapsing, and the flexibility to adjust the sizes. 3149 */ 3150 long preferred = 0; 3151 long currentPreferred; 3152 int lastMargin = 0; 3153 int totalSpacing = 0; 3154 int n = iter.getCount(); 3155 int adjustmentWeightsCount = LayoutIterator.WorstAdjustmentWeight + 1; 3156 //max gain we can get adjusting elements with adjustmentWeight <= i 3157 long gain[] = new long[adjustmentWeightsCount]; 3158 //max loss we can get adjusting elements with adjustmentWeight <= i 3159 long loss[] = new long[adjustmentWeightsCount]; 3160 3161 for (int i = 0; i < adjustmentWeightsCount; i++) { 3162 gain[i] = loss[i] = 0; 3163 } 3164 for (int i = 0; i < n; i++) { 3165 iter.setIndex(i); 3166 int margin0 = lastMargin; 3167 int margin1 = (int) iter.getLeadingCollapseSpan(); 3168 3169 iter.setOffset(Math.max(margin0, margin1)); 3170 totalSpacing += iter.getOffset(); 3171 3172 currentPreferred = (long)iter.getPreferredSpan(targetSpan); 3173 iter.setSpan((int) currentPreferred); 3174 preferred += currentPreferred; 3175 gain[iter.getAdjustmentWeight()] += 3176 (long)iter.getMaximumSpan(targetSpan) - currentPreferred; 3177 loss[iter.getAdjustmentWeight()] += 3178 currentPreferred - (long)iter.getMinimumSpan(targetSpan); 3179 lastMargin = (int) iter.getTrailingCollapseSpan(); 3180 } 3181 totalSpacing += lastMargin; 3182 totalSpacing += 2 * iter.getBorderWidth(); 3183 3184 for (int i = 1; i < adjustmentWeightsCount; i++) { 3185 gain[i] += gain[i - 1]; 3186 loss[i] += loss[i - 1]; 3187 } 3188 3189 /* 3190 * Second pass, expand or contract by as much as possible to reach 3191 * the target span. This takes the margin collapsing into account 3192 * prior to adjusting the span. 3193 */ 3194 3195 // determine the adjustment to be made 3196 int allocated = targetSpan - totalSpacing; 3197 long desiredAdjustment = allocated - preferred; 3198 long adjustmentsArray[] = (desiredAdjustment > 0) ? gain : loss; 3199 desiredAdjustment = Math.abs(desiredAdjustment); 3200 int adjustmentLevel = 0; 3201 for (;adjustmentLevel <= LayoutIterator.WorstAdjustmentWeight; 3202 adjustmentLevel++) { 3203 // adjustmentsArray[] is sorted. I do not bother about 3204 // binary search though 3205 if (adjustmentsArray[adjustmentLevel] >= desiredAdjustment) { 3206 break; 3207 } 3208 } 3209 float adjustmentFactor = 0.0f; 3210 if (adjustmentLevel <= LayoutIterator.WorstAdjustmentWeight) { 3211 desiredAdjustment -= (adjustmentLevel > 0) ? 3212 adjustmentsArray[adjustmentLevel - 1] : 0; 3213 if (desiredAdjustment != 0) { 3214 float maximumAdjustment = 3215 adjustmentsArray[adjustmentLevel] - 3216 ((adjustmentLevel > 0) ? 3217 adjustmentsArray[adjustmentLevel - 1] : 0 3218 ); 3219 adjustmentFactor = desiredAdjustment / maximumAdjustment; 3220 } 3221 } 3222 // make the adjustments 3223 int totalOffset = (int)iter.getBorderWidth(); 3224 for (int i = 0; i < n; i++) { 3225 iter.setIndex(i); 3226 iter.setOffset( iter.getOffset() + totalOffset); 3227 if (iter.getAdjustmentWeight() < adjustmentLevel) { 3228 iter.setSpan((int) 3229 ((allocated > preferred) ? 3230 Math.floor(iter.getMaximumSpan(targetSpan)) : 3231 Math.ceil(iter.getMinimumSpan(targetSpan)) 3232 ) 3233 ); 3234 } else if (iter.getAdjustmentWeight() == adjustmentLevel) { 3235 int availableSpan = (allocated > preferred) ? 3236 (int) iter.getMaximumSpan(targetSpan) - iter.getSpan() : 3237 iter.getSpan() - (int) iter.getMinimumSpan(targetSpan); 3238 int adj = (int)Math.floor(adjustmentFactor * availableSpan); 3239 iter.setSpan(iter.getSpan() + 3240 ((allocated > preferred) ? adj : -adj)); 3241 } 3242 totalOffset = (int) Math.min((long) iter.getOffset() + 3243 (long) iter.getSpan(), 3244 Integer.MAX_VALUE); 3245 } 3246 3247 // while rounding we could lose several pixels. 3248 int roundError = targetSpan - totalOffset - 3249 (int)iter.getTrailingCollapseSpan() - 3250 (int)iter.getBorderWidth(); 3251 int adj = (roundError > 0) ? 1 : -1; 3252 roundError *= adj; 3253 3254 boolean canAdjust = true; 3255 while (roundError > 0 && canAdjust) { 3256 // check for infinite loop 3257 canAdjust = false; 3258 int offsetAdjust = 0; 3259 // try to distribute roundError. one pixel per cell 3260 for (int i = 0; i < n; i++) { 3261 iter.setIndex(i); 3262 iter.setOffset(iter.getOffset() + offsetAdjust); 3263 int curSpan = iter.getSpan(); 3264 if (roundError > 0) { 3265 int boundGap = (adj > 0) ? 3266 (int)Math.floor(iter.getMaximumSpan(targetSpan)) - curSpan : 3267 curSpan - (int)Math.ceil(iter.getMinimumSpan(targetSpan)); 3268 if (boundGap >= 1) { 3269 canAdjust = true; 3270 iter.setSpan(curSpan + adj); 3271 offsetAdjust += adj; 3272 roundError--; 3273 } 3274 } 3275 } 3276 } 3277 } 3278 3279 /** 3280 * An iterator to express the requirements to use when computing 3281 * layout. 3282 */ 3283 interface LayoutIterator { 3284 3285 void setOffset(int offs); 3286 3287 int getOffset(); 3288 3289 void setSpan(int span); 3290 3291 int getSpan(); 3292 3293 int getCount(); 3294 3295 void setIndex(int i); 3296 3297 float getMinimumSpan(float parentSpan); 3298 3299 float getPreferredSpan(float parentSpan); 3300 3301 float getMaximumSpan(float parentSpan); 3302 3303 int getAdjustmentWeight(); //0 is the best weight WorstAdjustmentWeight is a worst one 3304 3305 //float getAlignment(); 3306 3307 float getBorderWidth(); 3308 3309 float getLeadingCollapseSpan(); 3310 3311 float getTrailingCollapseSpan(); 3312 public static final int WorstAdjustmentWeight = 2; 3313 } 3314 3315 // 3316 // Serialization support 3317 // 3318 3319 private void writeObject(java.io.ObjectOutputStream s) 3320 throws IOException 3321 { 3322 s.defaultWriteObject(); 3323 3324 // Determine what values in valueConvertor need to be written out. 3325 Enumeration keys = valueConvertor.keys(); 3326 s.writeInt(valueConvertor.size()); 3327 if (keys != null) { 3328 while (keys.hasMoreElements()) { 3329 Object key = keys.nextElement(); 3330 Object value = valueConvertor.get(key); 3331 if (!(key instanceof Serializable) && 3332 (key = StyleContext.getStaticAttributeKey(key)) == null) { 3333 // Should we throw an exception here? 3334 key = null; 3335 value = null; 3336 } 3337 else if (!(value instanceof Serializable) && 3338 (value = StyleContext.getStaticAttributeKey(value)) == null){ 3339 // Should we throw an exception here? 3340 key = null; 3341 value = null; 3342 } 3343 s.writeObject(key); 3344 s.writeObject(value); 3345 } 3346 } 3347 } 3348 3349 private void readObject(ObjectInputStream s) 3350 throws ClassNotFoundException, IOException 3351 { 3352 s.defaultReadObject(); 3353 // Reconstruct the hashtable. 3354 int numValues = s.readInt(); 3355 valueConvertor = new Hashtable<Object, Object>(Math.max(1, numValues)); 3356 while (numValues-- > 0) { 3357 Object key = s.readObject(); 3358 Object value = s.readObject(); 3359 Object staticKey = StyleContext.getStaticAttribute(key); 3360 if (staticKey != null) { 3361 key = staticKey; 3362 } 3363 Object staticValue = StyleContext.getStaticAttribute(value); 3364 if (staticValue != null) { 3365 value = staticValue; 3366 } 3367 if (key != null && value != null) { 3368 valueConvertor.put(key, value); 3369 } 3370 } 3371 } 3372 3373 3374 /* 3375 * we need StyleSheet for resolving lenght units. (see 3376 * isW3CLengthUnits) 3377 * we can not pass stylesheet for handling relative sizes. (do not 3378 * think changing public API is necessary) 3379 * CSS is not likely to be accessed from more then one thread. 3380 * Having local storage for StyleSheet for resolving relative 3381 * sizes is safe 3382 * 3383 * idk 08/30/2004 3384 */ 3385 private StyleSheet getStyleSheet(StyleSheet ss) { 3386 if (ss != null) { 3387 styleSheet = ss; 3388 } 3389 return styleSheet; 3390 } 3391 // 3392 // Instance variables 3393 // 3394 3395 /** Maps from CSS key to CssValue. */ 3396 private transient Hashtable<Object, Object> valueConvertor; 3397 3398 /** Size used for relative units. */ 3399 private int baseFontSize; 3400 3401 private transient StyleSheet styleSheet = null; 3402 3403 static int baseFontSizeIndex = 3; 3404 }