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