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