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