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