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