1. /*
  2. * @(#)CSS.java 1.30 00/02/02
  3. *
  4. * Copyright 1998-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing.text.html;
  11. import java.awt.Color;
  12. import java.awt.Font;
  13. import java.awt.Toolkit;
  14. import java.io.*;
  15. import java.lang.reflect.Method;
  16. import java.net.URL;
  17. import java.net.MalformedURLException;
  18. import java.util.Enumeration;
  19. import java.util.Hashtable;
  20. import java.util.Vector;
  21. import javax.swing.ImageIcon;
  22. import javax.swing.SizeRequirements;
  23. import javax.swing.text.*;
  24. /**
  25. * Defines a set of
  26. * <a href="http://www.w3.org/TR/REC-CSS1">CSS attributes</a>
  27. * as a typesafe enumeration. The HTML View implementations use
  28. * CSS attributes to determine how they will render. This also defines
  29. * methods to map between CSS/HTML/StyleConstants. Any shorthand
  30. * properties, such as font, are mapped to the intrinsic properties.
  31. * <p>The following describes the CSS properties that are suppored by the
  32. * rendering engine:
  33. * <ul><li>font-family
  34. * <li>font-style
  35. * <li>font-size (supports relative units)
  36. * <li>font-weight
  37. * <li>font
  38. * <li>color
  39. * <li>background-color (with the exception of transparent)
  40. * <li>background-image
  41. * <li>background-repeat
  42. * <li>background-position
  43. * <li>background
  44. * <li>background-repeat
  45. * <li>text-decoration (with the exception of blink and overline)
  46. * <li>vertical-align (only sup and super)
  47. * <li>text-align (justify is treated as center)
  48. * <li>margin-top
  49. * <li>margin-right
  50. * <li>margin-bottom
  51. * <li>margin-left
  52. * <li>margin
  53. * <li>padding-top
  54. * <li>padding-right
  55. * <li>padding-bottom
  56. * <li>padding-left
  57. * <li>border-style (only supports inset, outset and none)
  58. * <li>list-style-type
  59. * <li>list-style-position
  60. * </ul>
  61. * The following are modeled, but currently not rendered.
  62. * <ul><li>font-variant
  63. * <li>background-attachment (background always treated as scroll)
  64. * <li>word-spacing
  65. * <li>letter-spacing
  66. * <li>text-indent
  67. * <li>text-transform
  68. * <li>line-height
  69. * <li>border-top-width (this is used to indicate if a border should be used)
  70. * <li>border-right-width
  71. * <li>border-bottom-width
  72. * <li>border-left-width
  73. * <li>border-width
  74. * <li>border-top
  75. * <li>border-right
  76. * <li>border-bottom
  77. * <li>border-left
  78. * <li>border
  79. * <li>width
  80. * <li>height
  81. * <li>float
  82. * <li>clear
  83. * <li>display
  84. * <li>white-space
  85. * <li>list-style
  86. * </ul>
  87. * <p><b>Note: for the time being we do not fully support relative units,
  88. * unless noted, so that
  89. * p { margin-top: 10% } will be treated as if no margin-top was specified.
  90. *
  91. * @author Timothy Prinzing
  92. * @author Scott Violet
  93. * @version 1.30 02/02/00
  94. * @see StyleSheet
  95. */
  96. public class CSS implements Serializable {
  97. /**
  98. * Definitions to be used as a key on AttributeSet's
  99. * that might hold CSS attributes. Since this is a
  100. * closed set (i.e. defined exactly by the specification),
  101. * it is final and cannot be extended.
  102. */
  103. public static final class Attribute {
  104. private Attribute(String name, String defaultValue, boolean inherited) {
  105. this.name = name;
  106. this.defaultValue = defaultValue;
  107. this.inherited = inherited;
  108. }
  109. /**
  110. * The string representation of the attribute. This
  111. * should exactly match the string specified in the
  112. * CSS specification.
  113. */
  114. public String toString() {
  115. return name;
  116. }
  117. /**
  118. * Fetch the default value for the attribute.
  119. * If there is no default value (such as for
  120. * composite attributes), null will be returned.
  121. */
  122. public String getDefaultValue() {
  123. return defaultValue;
  124. }
  125. /**
  126. * Indicates if the attribute should be inherited
  127. * from the parent or not.
  128. */
  129. public boolean isInherited() {
  130. return inherited;
  131. }
  132. private String name;
  133. private String defaultValue;
  134. private boolean inherited;
  135. public static final Attribute BACKGROUND =
  136. new Attribute("background", null, false);
  137. public static final Attribute BACKGROUND_ATTACHMENT =
  138. new Attribute("background-attachment", "scroll", false);
  139. public static final Attribute BACKGROUND_COLOR =
  140. new Attribute("background-color", "transparent", false);
  141. public static final Attribute BACKGROUND_IMAGE =
  142. new Attribute("background-image", "none", false);
  143. public static final Attribute BACKGROUND_POSITION =
  144. new Attribute("background-position", null, false);
  145. public static final Attribute BACKGROUND_REPEAT =
  146. new Attribute("background-repeat", "repeat", false);
  147. public static final Attribute BORDER =
  148. new Attribute("border", null, false);
  149. public static final Attribute BORDER_BOTTOM =
  150. new Attribute("border-bottom", null, false);
  151. public static final Attribute BORDER_BOTTOM_WIDTH =
  152. new Attribute("border-bottom-width", "medium", false);
  153. public static final Attribute BORDER_COLOR =
  154. new Attribute("border-color", null, false);
  155. public static final Attribute BORDER_LEFT =
  156. new Attribute("border-left", null, false);
  157. public static final Attribute BORDER_LEFT_WIDTH =
  158. new Attribute("border-left-width", "medium", false);
  159. public static final Attribute BORDER_RIGHT =
  160. new Attribute("border-right", null, false);
  161. public static final Attribute BORDER_RIGHT_WIDTH =
  162. new Attribute("border-right-width", "medium", false);
  163. public static final Attribute BORDER_STYLE =
  164. new Attribute("border-style", "none", false);
  165. public static final Attribute BORDER_TOP =
  166. new Attribute("border-top", null, false);
  167. public static final Attribute BORDER_TOP_WIDTH =
  168. new Attribute("border-top-width", "medium", false);
  169. public static final Attribute BORDER_WIDTH =
  170. new Attribute("border-width", "medium", false);
  171. public static final Attribute CLEAR =
  172. new Attribute("clear", "none", false);
  173. public static final Attribute COLOR =
  174. new Attribute("color", null, true);
  175. public static final Attribute DISPLAY =
  176. new Attribute("display", "block", false);
  177. public static final Attribute FLOAT =
  178. new Attribute("float", "none", false);
  179. public static final Attribute FONT =
  180. new Attribute("font", null, true);
  181. public static final Attribute FONT_FAMILY =
  182. new Attribute("font-family", null, true);
  183. public static final Attribute FONT_SIZE =
  184. new Attribute("font-size", "medium", true);
  185. public static final Attribute FONT_STYLE =
  186. new Attribute("font-style", "normal", true);
  187. public static final Attribute FONT_VARIANT =
  188. new Attribute("font-variant", "normal", true);
  189. public static final Attribute FONT_WEIGHT =
  190. new Attribute("font-weight", "normal", true);
  191. public static final Attribute HEIGHT =
  192. new Attribute("height", "auto", false);
  193. public static final Attribute LETTER_SPACING =
  194. new Attribute("letter-spacing", "normal", true);
  195. public static final Attribute LINE_HEIGHT =
  196. new Attribute("line-height", "normal", true);
  197. public static final Attribute LIST_STYLE =
  198. new Attribute("list-style", null, true);
  199. public static final Attribute LIST_STYLE_IMAGE =
  200. new Attribute("list-style-image", "none", true);
  201. public static final Attribute LIST_STYLE_POSITION =
  202. new Attribute("list-style-position", "outside", true);
  203. public static final Attribute LIST_STYLE_TYPE =
  204. new Attribute("list-style-type", "disc", true);
  205. public static final Attribute MARGIN =
  206. new Attribute("margin", null, false);
  207. public static final Attribute MARGIN_BOTTOM =
  208. new Attribute("margin-bottom", "0", false);
  209. public static final Attribute MARGIN_LEFT =
  210. new Attribute("margin-left", "0", false);
  211. public static final Attribute MARGIN_RIGHT =
  212. new Attribute("margin-right", "0", false);
  213. public static final Attribute MARGIN_TOP =
  214. new Attribute("margin-top", "0", false);
  215. public static final Attribute PADDING =
  216. new Attribute("padding", null, false);
  217. public static final Attribute PADDING_BOTTOM =
  218. new Attribute("padding-bottom", "0", false);
  219. public static final Attribute PADDING_LEFT =
  220. new Attribute("padding-left", "0", false);
  221. public static final Attribute PADDING_RIGHT =
  222. new Attribute("padding-right", "0", false);
  223. public static final Attribute PADDING_TOP =
  224. new Attribute("padding-top", "0", false);
  225. public static final Attribute TEXT_ALIGN =
  226. new Attribute("text-align", null, true);
  227. public static final Attribute TEXT_DECORATION =
  228. new Attribute("text-decoration", "none", true);
  229. public static final Attribute TEXT_INDENT =
  230. new Attribute("text-indent", "0", true);
  231. public static final Attribute TEXT_TRANSFORM =
  232. new Attribute("text-transform", "none", true);
  233. public static final Attribute VERTICAL_ALIGN =
  234. new Attribute("vertical-align", "baseline", false);
  235. public static final Attribute WORD_SPACING =
  236. new Attribute("word-spacing", "normal", true);
  237. public static final Attribute WHITE_SPACE =
  238. new Attribute("white-space", "normal", true);
  239. public static final Attribute WIDTH =
  240. new Attribute("width", "auto", false);
  241. /*public*/ static final Attribute BORDER_SPACING =
  242. new Attribute("border-spacing", "0", true);
  243. /*public*/ static final Attribute CAPTION_SIDE =
  244. new Attribute("caption-side", "left", true);
  245. // All possible CSS attribute keys.
  246. static final Attribute[] allAttributes = {
  247. BACKGROUND, BACKGROUND_ATTACHMENT, BACKGROUND_COLOR,
  248. BACKGROUND_IMAGE, BACKGROUND_POSITION, BACKGROUND_REPEAT,
  249. BORDER, BORDER_BOTTOM, BORDER_BOTTOM_WIDTH, BORDER_COLOR,
  250. BORDER_LEFT, BORDER_LEFT_WIDTH, BORDER_RIGHT, BORDER_RIGHT_WIDTH,
  251. BORDER_STYLE, BORDER_TOP, BORDER_TOP_WIDTH, BORDER_WIDTH,
  252. CLEAR, COLOR, DISPLAY, FLOAT, FONT, FONT_FAMILY, FONT_SIZE,
  253. FONT_STYLE, FONT_VARIANT, FONT_WEIGHT, HEIGHT, LETTER_SPACING,
  254. LINE_HEIGHT, LIST_STYLE, LIST_STYLE_IMAGE, LIST_STYLE_POSITION,
  255. LIST_STYLE_TYPE, MARGIN, MARGIN_BOTTOM, MARGIN_LEFT, MARGIN_RIGHT,
  256. MARGIN_TOP, PADDING, PADDING_BOTTOM, PADDING_LEFT, PADDING_RIGHT,
  257. PADDING_TOP, TEXT_ALIGN, TEXT_DECORATION, TEXT_INDENT, TEXT_TRANSFORM,
  258. VERTICAL_ALIGN, WORD_SPACING, WHITE_SPACE, WIDTH,
  259. BORDER_SPACING, CAPTION_SIDE
  260. };
  261. private static final Attribute[] ALL_MARGINS =
  262. { MARGIN_TOP, MARGIN_RIGHT, MARGIN_BOTTOM, MARGIN_LEFT };
  263. private static final Attribute[] ALL_PADDING =
  264. { PADDING_TOP, PADDING_RIGHT, PADDING_BOTTOM, PADDING_LEFT };
  265. private static final Attribute[] ALL_BORDER_WIDTHS =
  266. { BORDER_TOP_WIDTH, BORDER_RIGHT_WIDTH, BORDER_BOTTOM_WIDTH,
  267. BORDER_LEFT_WIDTH };
  268. }
  269. static final class Value {
  270. private Value(String name) {
  271. this.name = name;
  272. }
  273. /**
  274. * The string representation of the attribute. This
  275. * should exactly match the string specified in the
  276. * CSS specification.
  277. */
  278. public String toString() {
  279. return name;
  280. }
  281. static final Value INHERITED = new Value("inherited");
  282. static final Value NONE = new Value("none");
  283. static final Value DOTTED = new Value("dotted");
  284. static final Value DASHED = new Value("dashed");
  285. static final Value SOLID = new Value("solid");
  286. static final Value DOUBLE = new Value("double");
  287. static final Value GROOVE = new Value("groove");
  288. static final Value RIDGE = new Value("ridge");
  289. static final Value INSET = new Value("inset");
  290. static final Value OUTSET = new Value("outset");
  291. // Lists.
  292. static final Value BLANK_LIST_ITEM = new Value("none");
  293. static final Value DISC = new Value("disc");
  294. static final Value CIRCLE = new Value("circle");
  295. static final Value SQUARE = new Value("square");
  296. static final Value DECIMAL = new Value("decimal");
  297. static final Value LOWER_ROMAN = new Value("lower-roman");
  298. static final Value UPPER_ROMAN = new Value("upper-roman");
  299. static final Value LOWER_ALPHA = new Value("lower-alpha");
  300. static final Value UPPER_ALPHA = new Value("upper-alpha");
  301. // background-repeat
  302. static final Value BACKGROUND_NO_REPEAT = new Value("no-repeat");
  303. static final Value BACKGROUND_REPEAT = new Value("repeat");
  304. static final Value BACKGROUND_REPEAT_X = new Value("repeat-x");
  305. static final Value BACKGROUND_REPEAT_Y = new Value("repeat-y");
  306. // background-attachment
  307. static final Value BACKGROUND_SCROLL = new Value("scroll");
  308. static final Value BACKGROUND_FIXED = new Value("fixed");
  309. private String name;
  310. static final Value[] allValues = {
  311. INHERITED, NONE, DOTTED, DASHED, SOLID, DOUBLE, GROOVE,
  312. RIDGE, INSET, OUTSET, DISC, CIRCLE, SQUARE, DECIMAL,
  313. LOWER_ROMAN, UPPER_ROMAN, LOWER_ALPHA, UPPER_ALPHA,
  314. BLANK_LIST_ITEM, BACKGROUND_NO_REPEAT, BACKGROUND_REPEAT,
  315. BACKGROUND_REPEAT_X, BACKGROUND_REPEAT_Y,
  316. BACKGROUND_FIXED, BACKGROUND_FIXED
  317. };
  318. }
  319. public CSS() {
  320. baseFontSize = 3;
  321. // setup the css conversion table
  322. valueConvertor = new Hashtable();
  323. valueConvertor.put(CSS.Attribute.FONT_SIZE, new FontSize());
  324. valueConvertor.put(CSS.Attribute.FONT_FAMILY, new FontFamily());
  325. valueConvertor.put(CSS.Attribute.FONT_WEIGHT, new FontWeight());
  326. valueConvertor.put(CSS.Attribute.BORDER_STYLE, new BorderStyle());
  327. Object cv = new ColorValue();
  328. valueConvertor.put(CSS.Attribute.COLOR, cv);
  329. valueConvertor.put(CSS.Attribute.BACKGROUND_COLOR, cv);
  330. valueConvertor.put(CSS.Attribute.BORDER_COLOR, cv);
  331. Object lv = new LengthValue();
  332. valueConvertor.put(CSS.Attribute.MARGIN_TOP, lv);
  333. valueConvertor.put(CSS.Attribute.MARGIN_BOTTOM, lv);
  334. valueConvertor.put(CSS.Attribute.MARGIN_LEFT, lv);
  335. valueConvertor.put(CSS.Attribute.MARGIN_RIGHT, lv);
  336. valueConvertor.put(CSS.Attribute.PADDING_TOP, lv);
  337. valueConvertor.put(CSS.Attribute.PADDING_BOTTOM, lv);
  338. valueConvertor.put(CSS.Attribute.PADDING_LEFT, lv);
  339. valueConvertor.put(CSS.Attribute.PADDING_RIGHT, lv);
  340. Object bv = new BorderWidthValue(null, 0);
  341. valueConvertor.put(CSS.Attribute.BORDER_WIDTH, lv);
  342. valueConvertor.put(CSS.Attribute.BORDER_TOP_WIDTH, bv);
  343. valueConvertor.put(CSS.Attribute.BORDER_BOTTOM_WIDTH, bv);
  344. valueConvertor.put(CSS.Attribute.BORDER_LEFT_WIDTH, bv);
  345. valueConvertor.put(CSS.Attribute.BORDER_RIGHT_WIDTH, bv);
  346. valueConvertor.put(CSS.Attribute.TEXT_INDENT, lv);
  347. valueConvertor.put(CSS.Attribute.WIDTH, lv);
  348. valueConvertor.put(CSS.Attribute.HEIGHT, lv);
  349. valueConvertor.put(CSS.Attribute.BORDER_SPACING, lv);
  350. Object sv = new StringValue();
  351. valueConvertor.put(CSS.Attribute.FONT_STYLE, sv);
  352. valueConvertor.put(CSS.Attribute.TEXT_DECORATION, sv);
  353. valueConvertor.put(CSS.Attribute.TEXT_ALIGN, sv);
  354. valueConvertor.put(CSS.Attribute.VERTICAL_ALIGN, sv);
  355. Object valueMapper = new CssValueMapper();
  356. valueConvertor.put(CSS.Attribute.LIST_STYLE_TYPE,
  357. valueMapper);
  358. valueConvertor.put(CSS.Attribute.BACKGROUND_IMAGE,
  359. new BackgroundImage());
  360. valueConvertor.put(CSS.Attribute.BACKGROUND_POSITION,
  361. new BackgroundPosition());
  362. valueConvertor.put(CSS.Attribute.BACKGROUND_REPEAT,
  363. valueMapper);
  364. valueConvertor.put(CSS.Attribute.BACKGROUND_ATTACHMENT,
  365. valueMapper);
  366. Object generic = new CssValue();
  367. int n = CSS.Attribute.allAttributes.length;
  368. for (int i = 0; i < n; i++) {
  369. CSS.Attribute key = CSS.Attribute.allAttributes[i];
  370. if (valueConvertor.get(key) == null) {
  371. valueConvertor.put(key, generic);
  372. }
  373. }
  374. }
  375. /**
  376. * Sets the base font size. <code>sz</code> is a CSS value, and is
  377. * not necessarily the point size. Use getPointSize to determine the
  378. * point size corresponding to <code>sz</code>.
  379. */
  380. void setBaseFontSize(int sz) {
  381. if (sz < 1)
  382. baseFontSize = 0;
  383. else if (sz > 7)
  384. baseFontSize = 7;
  385. else
  386. baseFontSize = sz;
  387. }
  388. /**
  389. * Sets the base font size from the passed in string.
  390. */
  391. void setBaseFontSize(String size) {
  392. int relSize, absSize, diff;
  393. if (size != null) {
  394. if (size.startsWith("+")) {
  395. relSize = Integer.valueOf(size.substring(1)).intValue();
  396. setBaseFontSize(baseFontSize + relSize);
  397. } else if (size.startsWith("-")) {
  398. relSize = -Integer.valueOf(size.substring(1)).intValue();
  399. setBaseFontSize(baseFontSize + relSize);
  400. } else {
  401. setBaseFontSize(Integer.valueOf(size).intValue());
  402. }
  403. }
  404. }
  405. /**
  406. * Returns the base font size.
  407. */
  408. int getBaseFontSize() {
  409. return baseFontSize;
  410. }
  411. /**
  412. * Parses the CSS property <code>key</code> with value
  413. * <code>value</code> placing the result in <code>att</code>.
  414. */
  415. void addInternalCSSValue(MutableAttributeSet attr,
  416. CSS.Attribute key, String value) {
  417. if (key == CSS.Attribute.FONT) {
  418. ShorthandFontParser.parseShorthandFont(this, value, attr);
  419. }
  420. else if (key == CSS.Attribute.BACKGROUND) {
  421. ShorthandBackgroundParser.parseShorthandBackground
  422. (this, value, attr);
  423. }
  424. else if (key == CSS.Attribute.MARGIN) {
  425. ShorthandMarginParser.parseShorthandMargin(this, value, attr,
  426. CSS.Attribute.ALL_MARGINS);
  427. }
  428. else if (key == CSS.Attribute.PADDING) {
  429. ShorthandMarginParser.parseShorthandMargin(this, value, attr,
  430. CSS.Attribute.ALL_PADDING);
  431. }
  432. else if (key == CSS.Attribute.BORDER_WIDTH) {
  433. ShorthandMarginParser.parseShorthandMargin(this, value, attr,
  434. CSS.Attribute.ALL_BORDER_WIDTHS);
  435. }
  436. else {
  437. Object iValue = getInternalCSSValue(key, value);
  438. if (iValue != null) {
  439. attr.addAttribute(key, iValue);
  440. }
  441. }
  442. }
  443. /**
  444. * Gets the internal CSS representation of <code>value</code> which is
  445. * a CSS value of the CSS attribute named <code>key</code>. The receiver
  446. * should not modify <code>value</code>, and the first <code>count</code>
  447. * strings are valid.
  448. */
  449. Object getInternalCSSValue(CSS.Attribute key, String value) {
  450. CssValue conv = (CssValue) valueConvertor.get(key);
  451. return conv.parseCssValue(value);
  452. }
  453. /**
  454. * Maps from a StyleConstants to a CSS Attribute.
  455. */
  456. Attribute styleConstantsKeyToCSSKey(StyleConstants sc) {
  457. return (Attribute)styleConstantToCssMap.get(sc);
  458. }
  459. /**
  460. * Maps from a StyleConstants value to a CSS value.
  461. */
  462. Object styleConstantsValueToCSSValue(StyleConstants sc,
  463. Object styleValue) {
  464. Object cssKey = styleConstantsKeyToCSSKey(sc);
  465. if (cssKey != null) {
  466. CssValue conv = (CssValue)valueConvertor.get(cssKey);
  467. return conv.fromStyleConstants(sc, styleValue);
  468. }
  469. return null;
  470. }
  471. /**
  472. * Converts the passed in CSS value to a StyleConstants value.
  473. * <code>key</code> identifies the CSS attribute being mapped.
  474. */
  475. Object cssValueToStyleConstantsValue(StyleConstants key, Object value) {
  476. if (value instanceof CssValue) {
  477. return ((CssValue)value).toStyleConstants((StyleConstants)key,
  478. null);
  479. }
  480. return null;
  481. }
  482. /**
  483. * Returns the font for the values in the passed in AttributeSet.
  484. * It is assumed the keys will be CSS.Attribute keys.
  485. * <code>sc</code> is the StyleContext that will be messaged to get
  486. * the font once the size, name and style have been determined.
  487. */
  488. Font getFont(StyleContext sc, AttributeSet a, int defaultSize) {
  489. int size = getFontSize(a, defaultSize);
  490. /*
  491. * If the vertical alignment is set to either superscirpt or
  492. * subscript we reduce the font size by 2 points.
  493. */
  494. StringValue vAlignV = (StringValue)a.getAttribute
  495. (CSS.Attribute.VERTICAL_ALIGN);
  496. if ((vAlignV != null)) {
  497. String vAlign = vAlignV.toString();
  498. if ((vAlign.indexOf("sup") >= 0) ||
  499. (vAlign.indexOf("sub") >= 0)) {
  500. size -= 2;
  501. }
  502. }
  503. FontFamily familyValue = (FontFamily)a.getAttribute
  504. (CSS.Attribute.FONT_FAMILY);
  505. String family = (familyValue != null) ? familyValue.getValue() :
  506. "SansSerif";
  507. int style = Font.PLAIN;
  508. FontWeight weightValue = (FontWeight) a.getAttribute
  509. (CSS.Attribute.FONT_WEIGHT);
  510. if ((weightValue != null) && (weightValue.getValue() > 400)) {
  511. style |= Font.BOLD;
  512. }
  513. Object fs = a.getAttribute(CSS.Attribute.FONT_STYLE);
  514. if ((fs != null) && (fs.toString().indexOf("italic") >= 0)) {
  515. style |= Font.ITALIC;
  516. }
  517. Font f = sc.getFont(family, style, size);
  518. return f;
  519. }
  520. static int getFontSize(AttributeSet attr, int defaultSize) {
  521. // PENDING(prinz) this is a 1.1 based implementation, need to also
  522. // have a 1.2 version.
  523. FontSize sizeValue = (FontSize)attr.getAttribute(CSS.Attribute.
  524. FONT_SIZE);
  525. return (sizeValue != null) ? (int)sizeValue.getValue(attr) :
  526. defaultSize;
  527. }
  528. /**
  529. * Takes a set of attributes and turn it into a color
  530. * specification. This might be used to specify things
  531. * like brighter, more hue, etc.
  532. * This will return null if there is no value for <code>key</code>.
  533. *
  534. * @param key CSS.Attribute identifying where color is stored.
  535. * @param a the set of attributes
  536. * @return the color
  537. */
  538. Color getColor(AttributeSet a, CSS.Attribute key) {
  539. ColorValue cv = (ColorValue) a.getAttribute(key);
  540. if (cv != null) {
  541. return cv.getValue();
  542. }
  543. return null;
  544. }
  545. /**
  546. * Returns the size of a font from the passed in string.
  547. *
  548. * @param size CSS string describing font size
  549. * @param baseFontSize size to use for relative units.
  550. */
  551. float getPointSize(String size) {
  552. int relSize, absSize, diff, index;
  553. if (size != null) {
  554. if (size.startsWith("+")) {
  555. relSize = Integer.valueOf(size.substring(1)).intValue();
  556. return getPointSize(baseFontSize + relSize);
  557. } else if (size.startsWith("-")) {
  558. relSize = -Integer.valueOf(size.substring(1)).intValue();
  559. return getPointSize(baseFontSize + relSize);
  560. } else {
  561. absSize = Integer.valueOf(size).intValue();
  562. return getPointSize(absSize);
  563. }
  564. }
  565. return 0;
  566. }
  567. /**
  568. * Returns the length of the attribute in <code>a</code> with
  569. * key <code>key</code>.
  570. */
  571. float getLength(AttributeSet a, CSS.Attribute key) {
  572. LengthValue lv = (LengthValue) a.getAttribute(key);
  573. float len = (lv != null) ? lv.getValue() : 0;
  574. return len;
  575. }
  576. /**
  577. * Convert a set of HTML attributes to an equivalent
  578. * set of CSS attributes.
  579. *
  580. * @param AttributeSet containing the HTML attributes.
  581. * @return AttributeSet containing the corresponding CSS attributes.
  582. * The AttributeSet will be empty if there are no mapping
  583. * CSS attributes.
  584. */
  585. AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet) {
  586. MutableAttributeSet cssAttrSet = new SimpleAttributeSet();
  587. Element elem = (Element)htmlAttrSet;
  588. HTML.Tag tag = getHTMLTag(htmlAttrSet);
  589. if ((tag == HTML.Tag.TD) || (tag == HTML.Tag.TH)) {
  590. // translate border width into the cells
  591. AttributeSet tableAttr = elem.getParentElement().
  592. getParentElement().getAttributes();
  593. translateAttribute(HTML.Attribute.BORDER, tableAttr, cssAttrSet);
  594. String pad = (String)tableAttr.getAttribute(HTML.Attribute.CELLPADDING);
  595. if (pad != null) {
  596. Object v = getInternalCSSValue(CSS.Attribute.PADDING_TOP, pad);
  597. cssAttrSet.addAttribute(CSS.Attribute.PADDING_TOP, v);
  598. cssAttrSet.addAttribute(CSS.Attribute.PADDING_BOTTOM, v);
  599. cssAttrSet.addAttribute(CSS.Attribute.PADDING_LEFT, v);
  600. cssAttrSet.addAttribute(CSS.Attribute.PADDING_RIGHT, v);
  601. }
  602. }
  603. if (elem.isLeaf()) {
  604. translateEmbeddedAttributes(htmlAttrSet, cssAttrSet);
  605. } else {
  606. translateAttributes(tag, htmlAttrSet, cssAttrSet);
  607. }
  608. if (tag == HTML.Tag.CAPTION) {
  609. /*
  610. * Navigator uses ALIGN for caption placement and IE uses VALIGN.
  611. */
  612. Object v = htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
  613. if ((v != null) && (v.equals("top") || v.equals("bottom"))) {
  614. cssAttrSet.addAttribute(CSS.Attribute.CAPTION_SIDE, v);
  615. cssAttrSet.removeAttribute(CSS.Attribute.TEXT_ALIGN);
  616. } else {
  617. v = htmlAttrSet.getAttribute(HTML.Attribute.VALIGN);
  618. if (v != null) {
  619. cssAttrSet.addAttribute(CSS.Attribute.CAPTION_SIDE, v);
  620. }
  621. }
  622. }
  623. return cssAttrSet;
  624. }
  625. private static final Hashtable attributeMap = new Hashtable();
  626. private static final Hashtable valueMap = new Hashtable();
  627. /**
  628. * The HTML/CSS size model has seven slots
  629. * that one can assign sizes to.
  630. */
  631. static int sizeMap[] = { 8, 10, 12, 14, 18, 24, 36 };
  632. /**
  633. * The hashtable and the static initalization block below,
  634. * set up a mapping from well-known HTML attributes to
  635. * CSS attributes. For the most part, there is a 1-1 mapping
  636. * between the two. However in the case of certain HTML
  637. * attributes for example HTML.Attribute.VSPACE or
  638. * HTML.Attribute.HSPACE, end up mapping to two CSS.Attribute's.
  639. * Therefore, the value associated with each HTML.Attribute.
  640. * key ends up being an array of CSS.Attribute.* objects.
  641. */
  642. private static final Hashtable htmlAttrToCssAttrMap = new Hashtable(20);
  643. /**
  644. * The hashtable and static initialization that follows sets
  645. * up a translation from StyleConstants (i.e. the <em>well known</em>
  646. * attributes) to the associated CSS attributes.
  647. */
  648. private static final Hashtable styleConstantToCssMap = new Hashtable(17);
  649. /** Maps from HTML value to a CSS value. Used in internal mapping. */
  650. private static final Hashtable htmlValueToCssValueMap = new Hashtable(8);
  651. /** Maps from CSS value (string) to internal value. */
  652. private static final Hashtable cssValueToInternalValueMap = new Hashtable(13);
  653. /** Used to indicate if a font family name is valid. */
  654. private static Hashtable fontMapping;
  655. private static final Object fontMappingLock = new Object();
  656. static {
  657. // load the attribute map
  658. for (int i = 0; i < Attribute.allAttributes.length; i++ ) {
  659. attributeMap.put(Attribute.allAttributes[i].toString(),
  660. Attribute.allAttributes[i]);
  661. }
  662. // load the value map
  663. for (int i = 0; i < Value.allValues.length; i++ ) {
  664. valueMap.put(Value.allValues[i].toString(),
  665. Value.allValues[i]);
  666. }
  667. htmlAttrToCssAttrMap.put(HTML.Attribute.COLOR,
  668. new CSS.Attribute[]{CSS.Attribute.COLOR});
  669. htmlAttrToCssAttrMap.put(HTML.Attribute.TEXT,
  670. new CSS.Attribute[]{CSS.Attribute.COLOR});
  671. htmlAttrToCssAttrMap.put(HTML.Attribute.CLEAR,
  672. new CSS.Attribute[]{CSS.Attribute.CLEAR});
  673. htmlAttrToCssAttrMap.put(HTML.Attribute.BACKGROUND,
  674. new CSS.Attribute[]{CSS.Attribute.BACKGROUND_IMAGE});
  675. htmlAttrToCssAttrMap.put(HTML.Attribute.BGCOLOR,
  676. new CSS.Attribute[]{CSS.Attribute.BACKGROUND_COLOR});
  677. htmlAttrToCssAttrMap.put(HTML.Attribute.WIDTH,
  678. new CSS.Attribute[]{CSS.Attribute.WIDTH});
  679. htmlAttrToCssAttrMap.put(HTML.Attribute.HEIGHT,
  680. new CSS.Attribute[]{CSS.Attribute.HEIGHT});
  681. htmlAttrToCssAttrMap.put(HTML.Attribute.BORDER,
  682. new CSS.Attribute[]{CSS.Attribute.BORDER_TOP_WIDTH, CSS.Attribute.BORDER_RIGHT_WIDTH, CSS.Attribute.BORDER_BOTTOM_WIDTH, CSS.Attribute.BORDER_LEFT_WIDTH});
  683. htmlAttrToCssAttrMap.put(HTML.Attribute.CELLPADDING,
  684. new CSS.Attribute[]{CSS.Attribute.PADDING});
  685. htmlAttrToCssAttrMap.put(HTML.Attribute.CELLSPACING,
  686. new CSS.Attribute[]{CSS.Attribute.BORDER_SPACING});
  687. htmlAttrToCssAttrMap.put(HTML.Attribute.MARGINWIDTH,
  688. new CSS.Attribute[]{CSS.Attribute.MARGIN_LEFT,
  689. CSS.Attribute.MARGIN_RIGHT});
  690. htmlAttrToCssAttrMap.put(HTML.Attribute.MARGINHEIGHT,
  691. new CSS.Attribute[]{CSS.Attribute.MARGIN_TOP,
  692. CSS.Attribute.MARGIN_BOTTOM});
  693. htmlAttrToCssAttrMap.put(HTML.Attribute.HSPACE,
  694. new CSS.Attribute[]{CSS.Attribute.PADDING_LEFT,
  695. CSS.Attribute.PADDING_RIGHT});
  696. htmlAttrToCssAttrMap.put(HTML.Attribute.VSPACE,
  697. new CSS.Attribute[]{CSS.Attribute.PADDING_BOTTOM,
  698. CSS.Attribute.PADDING_TOP});
  699. htmlAttrToCssAttrMap.put(HTML.Attribute.FACE,
  700. new CSS.Attribute[]{CSS.Attribute.FONT_FAMILY});
  701. htmlAttrToCssAttrMap.put(HTML.Attribute.SIZE,
  702. new CSS.Attribute[]{CSS.Attribute.FONT_SIZE});
  703. htmlAttrToCssAttrMap.put(HTML.Attribute.VALIGN,
  704. new CSS.Attribute[]{CSS.Attribute.VERTICAL_ALIGN});
  705. htmlAttrToCssAttrMap.put(HTML.Attribute.ALIGN,
  706. new CSS.Attribute[]{CSS.Attribute.VERTICAL_ALIGN,
  707. CSS.Attribute.TEXT_ALIGN,
  708. CSS.Attribute.FLOAT});
  709. htmlAttrToCssAttrMap.put(HTML.Attribute.TYPE,
  710. new CSS.Attribute[]{CSS.Attribute.LIST_STYLE_TYPE});
  711. htmlAttrToCssAttrMap.put(HTML.Attribute.NOWRAP,
  712. new CSS.Attribute[]{CSS.Attribute.WHITE_SPACE});
  713. // initialize StyleConstants mapping
  714. styleConstantToCssMap.put(StyleConstants.FontFamily,
  715. CSS.Attribute.FONT_FAMILY);
  716. styleConstantToCssMap.put(StyleConstants.FontSize,
  717. CSS.Attribute.FONT_SIZE);
  718. styleConstantToCssMap.put(StyleConstants.Bold,
  719. CSS.Attribute.FONT_WEIGHT);
  720. styleConstantToCssMap.put(StyleConstants.Italic,
  721. CSS.Attribute.FONT_STYLE);
  722. styleConstantToCssMap.put(StyleConstants.Underline,
  723. CSS.Attribute.TEXT_DECORATION);
  724. styleConstantToCssMap.put(StyleConstants.StrikeThrough,
  725. CSS.Attribute.TEXT_DECORATION);
  726. styleConstantToCssMap.put(StyleConstants.Superscript,
  727. CSS.Attribute.VERTICAL_ALIGN);
  728. styleConstantToCssMap.put(StyleConstants.Subscript,
  729. CSS.Attribute.VERTICAL_ALIGN);
  730. styleConstantToCssMap.put(StyleConstants.Foreground,
  731. CSS.Attribute.COLOR);
  732. styleConstantToCssMap.put(StyleConstants.Background,
  733. CSS.Attribute.BACKGROUND_COLOR);
  734. styleConstantToCssMap.put(StyleConstants.FirstLineIndent,
  735. CSS.Attribute.TEXT_INDENT);
  736. styleConstantToCssMap.put(StyleConstants.LeftIndent,
  737. CSS.Attribute.MARGIN_LEFT);
  738. styleConstantToCssMap.put(StyleConstants.RightIndent,
  739. CSS.Attribute.MARGIN_RIGHT);
  740. styleConstantToCssMap.put(StyleConstants.SpaceAbove,
  741. CSS.Attribute.MARGIN_TOP);
  742. styleConstantToCssMap.put(StyleConstants.SpaceBelow,
  743. CSS.Attribute.MARGIN_BOTTOM);
  744. styleConstantToCssMap.put(StyleConstants.Alignment,
  745. CSS.Attribute.TEXT_ALIGN);
  746. // HTML->CSS
  747. htmlValueToCssValueMap.put("disc", CSS.Value.DISC);
  748. htmlValueToCssValueMap.put("square", CSS.Value.SQUARE);
  749. htmlValueToCssValueMap.put("circle", CSS.Value.CIRCLE);
  750. htmlValueToCssValueMap.put("1", CSS.Value.DECIMAL);
  751. htmlValueToCssValueMap.put("a", CSS.Value.LOWER_ALPHA);
  752. htmlValueToCssValueMap.put("A", CSS.Value.UPPER_ALPHA);
  753. htmlValueToCssValueMap.put("i", CSS.Value.LOWER_ROMAN);
  754. htmlValueToCssValueMap.put("I", CSS.Value.UPPER_ROMAN);
  755. // CSS-> internal CSS
  756. cssValueToInternalValueMap.put("disc", CSS.Value.DISC);
  757. cssValueToInternalValueMap.put("square", CSS.Value.SQUARE);
  758. cssValueToInternalValueMap.put("circle", CSS.Value.CIRCLE);
  759. cssValueToInternalValueMap.put("decimal", CSS.Value.DECIMAL);
  760. cssValueToInternalValueMap.put("lower-roman", CSS.Value.LOWER_ROMAN);
  761. cssValueToInternalValueMap.put("upper-roman", CSS.Value.UPPER_ROMAN);
  762. cssValueToInternalValueMap.put("lower-alpha", CSS.Value.LOWER_ALPHA);
  763. cssValueToInternalValueMap.put("upper-alpha", CSS.Value.UPPER_ALPHA);
  764. cssValueToInternalValueMap.put("repeat", CSS.Value.BACKGROUND_REPEAT);
  765. cssValueToInternalValueMap.put("no-repeat",
  766. CSS.Value.BACKGROUND_NO_REPEAT);
  767. cssValueToInternalValueMap.put("repeat-x",
  768. CSS.Value.BACKGROUND_REPEAT_X);
  769. cssValueToInternalValueMap.put("repeat-y",
  770. CSS.Value.BACKGROUND_REPEAT_Y);
  771. cssValueToInternalValueMap.put("scroll",
  772. CSS.Value.BACKGROUND_SCROLL);
  773. cssValueToInternalValueMap.put("fixed",
  774. CSS.Value.BACKGROUND_FIXED);
  775. // Register all the CSS attribute keys for archival/unarchival
  776. Object[] keys = CSS.Attribute.allAttributes;
  777. try {
  778. for (int i = 0; i < keys.length; i++) {
  779. StyleContext.registerStaticAttributeKey(keys[i]);
  780. }
  781. } catch (Throwable e) {
  782. e.printStackTrace();
  783. }
  784. // Register all the CSS Values for archival/unarchival
  785. keys = CSS.Value.allValues;
  786. try {
  787. for (int i = 0; i < keys.length; i++) {
  788. StyleContext.registerStaticAttributeKey(keys[i]);
  789. }
  790. } catch (Throwable e) {
  791. e.printStackTrace();
  792. }
  793. }
  794. /**
  795. * Returns a hashtable whose contents are used to indicate the valid
  796. * fonts.
  797. */
  798. static Hashtable getValidFontNameMapping() {
  799. if (fontMapping == null) {
  800. // This can get called from multiple threads, lock before setting
  801. synchronized(fontMappingLock) {
  802. if (fontMapping == null) {
  803. String[] names = null;
  804. // Use the 1.2 GraphicsEnvironment for getting font names,
  805. // if we can.
  806. try {
  807. Class ge = Class.forName("java.awt.GraphicsEnvironment");
  808. if (ge != null) {
  809. Method method = ge.getDeclaredMethod
  810. ("getLocalGraphicsEnvironment", null);
  811. if (method != null) {
  812. Object instance = method.invoke(ge, null);
  813. if (instance != null) {
  814. method = ge.getMethod
  815. ("getAvailableFontFamilyNames", null);
  816. if (method != null) {
  817. names = (String[])method.invoke
  818. (instance, null);
  819. }
  820. }
  821. }
  822. }
  823. }
  824. catch (Throwable th) { }
  825. if (names == null) {
  826. // We can't, use the Toolkit method.
  827. names = Toolkit.getDefaultToolkit().getFontList();
  828. }
  829. if (names != null) {
  830. fontMapping = new Hashtable(names.length * 2);
  831. for (int counter = names.length - 1; counter >= 0;
  832. counter--) {
  833. // Put both lowercase and case value in table.
  834. fontMapping.put(names[counter].toLowerCase(),
  835. names[counter]);
  836. fontMapping.put(names[counter], names[counter]);
  837. }
  838. }
  839. else {
  840. fontMapping = new Hashtable(1);
  841. }
  842. }
  843. }
  844. }
  845. return fontMapping;
  846. }
  847. /**
  848. * Return the set of all possible CSS attribute keys.
  849. */
  850. public static Attribute[] getAllAttributeKeys() {
  851. Attribute[] keys = new Attribute[Attribute.allAttributes.length];
  852. System.arraycopy(Attribute.allAttributes, 0, keys, 0, Attribute.allAttributes.length);
  853. return keys;
  854. }
  855. /**
  856. * Translate a string to a CSS.Attribute object. This
  857. * will return null if there is no attribute by the given
  858. * name.
  859. * @param name the name of the CSS attribute to fetch the
  860. * typesafe enumeration for.
  861. * @returns the CSS.Attribute object, or null if the string
  862. * doesn't represent a valid attribute key.
  863. */
  864. public static final Attribute getAttribute(String name) {
  865. return (Attribute) attributeMap.get(name);
  866. }
  867. /**
  868. * Translate a string to a CSS.Value object. This
  869. * will return null if there is no value by the given
  870. * name.
  871. * @param name the name of the CSS value to fetch the
  872. * typesafe enumeration for.
  873. * @returns the CSS.Value object, or null if the string
  874. * doesn't represent a valid CSS value name. This does
  875. * not mean it doesn't represent a valid CSS value.
  876. */
  877. static final Value getValue(String name) {
  878. return (Value) valueMap.get(name);
  879. }
  880. //
  881. // Conversion related methods/classes
  882. //
  883. /**
  884. * Returns a URL for the given CSS url string. If relative,
  885. * <code>base</code> is used as the parent. If a valid URL can not
  886. * be found, this will not throw a MalformedURLException, instead
  887. * null will be returned.
  888. */
  889. static URL getURL(URL base, String cssString) {
  890. if (cssString == null) {
  891. return null;
  892. }
  893. if (cssString.startsWith("url(") &&
  894. cssString.endsWith(")")) {
  895. cssString = cssString.substring(4, cssString.length() - 1);
  896. }
  897. // Absolute first
  898. try {
  899. URL url = new URL(cssString);
  900. if (url != null) {
  901. return url;
  902. }
  903. } catch (MalformedURLException mue) {
  904. }
  905. // Then relative
  906. if (base != null) {
  907. // Relative URL, try from base
  908. try {
  909. URL url = new URL(base, cssString);
  910. return url;
  911. }
  912. catch (MalformedURLException muee) {
  913. }
  914. }
  915. return null;
  916. }
  917. /**
  918. * Converts a type Color to a hex string
  919. * in the format "#RRGGBB"
  920. */
  921. static String colorToHex(Color color) {
  922. String colorstr = new String("#");
  923. // Red
  924. String str = Integer.toHexString(color.getRed());
  925. if (str.length() > 2)
  926. str = str.substring(0, 2);
  927. else if (str.length() < 2)
  928. colorstr += "0" + str;
  929. else
  930. colorstr += str;
  931. // Green
  932. str = Integer.toHexString(color.getGreen());
  933. if (str.length() > 2)
  934. str = str.substring(0, 2);
  935. else if (str.length() < 2)
  936. colorstr += "0" + str;
  937. else
  938. colorstr += str;
  939. // Blue
  940. str = Integer.toHexString(color.getBlue());
  941. if (str.length() > 2)
  942. str = str.substring(0, 2);
  943. else if (str.length() < 2)
  944. colorstr += "0" + str;
  945. else
  946. colorstr += str;
  947. return colorstr;
  948. }
  949. /**
  950. * Convert a "#FFFFFF" hex string to a Color.
  951. * If the color specification is bad, an attempt
  952. * will be made to fix it up.
  953. */
  954. static final Color hexToColor(String value) {
  955. String digits;
  956. int n = value.length();
  957. if (value.startsWith("#")) {
  958. digits = value.substring(1, Math.min(value.length(), 7));
  959. } else {
  960. digits = value;
  961. }
  962. String hstr = "0x" + digits;
  963. Color c;
  964. try {
  965. c = Color.decode(hstr);
  966. } catch (NumberFormatException nfe) {
  967. c = null;
  968. }
  969. return c;
  970. }
  971. /**
  972. * Convert a color string such as "RED" or "#NNNNNN" to a Color.
  973. * Note: This will only convert the HTML3.2 color strings
  974. * or a string of length 7;
  975. * otherwise, it will return null.
  976. */
  977. static Color stringToColor(String str) {
  978. Color color = null;
  979. if (str.charAt(0) == '#')
  980. color = hexToColor(str);
  981. else if (str.equalsIgnoreCase("Black"))
  982. color = hexToColor("#000000");
  983. else if(str.equalsIgnoreCase("Silver"))
  984. color = hexToColor("#C0C0C0");
  985. else if(str.equalsIgnoreCase("Gray"))
  986. color = hexToColor("#808080");
  987. else if(str.equalsIgnoreCase("White"))
  988. color = hexToColor("#FFFFFF");
  989. else if(str.equalsIgnoreCase("Maroon"))
  990. color = hexToColor("#800000");
  991. else if(str.equalsIgnoreCase("Red"))
  992. color = hexToColor("#FF0000");
  993. else if(str.equalsIgnoreCase("Purple"))
  994. color = hexToColor("#800080");
  995. else if(str.equalsIgnoreCase("Fuchsia"))
  996. color = hexToColor("#FF00FF");
  997. else if(str.equalsIgnoreCase("Green"))
  998. color = hexToColor("#008000");
  999. else if(str.equalsIgnoreCase("Lime"))
  1000. color = hexToColor("#00FF00");
  1001. else if(str.equalsIgnoreCase("Olive"))
  1002. color = hexToColor("#808000");
  1003. else if(str.equalsIgnoreCase("Yellow"))
  1004. color = hexToColor("#FFFF00");
  1005. else if(str.equalsIgnoreCase("Navy"))
  1006. color = hexToColor("#000080");
  1007. else if(str.equalsIgnoreCase("Blue"))
  1008. color = hexToColor("#0000FF");
  1009. else if(str.equalsIgnoreCase("Teal"))
  1010. color = hexToColor("#008080");
  1011. else if(str.equalsIgnoreCase("Aqua"))
  1012. color = hexToColor("#00FFFF");
  1013. else
  1014. color = hexToColor(str); // sometimes get specified without leading #
  1015. return color;
  1016. }
  1017. static int getIndexOfSize(float pt) {
  1018. for (int i = 0; i < sizeMap.length; i ++ )
  1019. if (pt <= sizeMap[i])
  1020. return i;
  1021. return sizeMap.length - 1;
  1022. }
  1023. /**
  1024. * @return an array of all the strings in <code>value</code>
  1025. * that are separated by whitespace.
  1026. */
  1027. static String[] parseStrings(String value) {
  1028. int current, last;
  1029. int length = (value == null) ? 0 : value.length();
  1030. Vector temp = new Vector(4);
  1031. current = 0;
  1032. while (current < length) {
  1033. // Skip ws
  1034. while (current < length && Character.isWhitespace
  1035. (value.charAt(current))) {
  1036. current++;
  1037. }
  1038. last = current;
  1039. while (current < length && !Character.isWhitespace
  1040. (value.charAt(current))) {
  1041. current++;
  1042. }
  1043. if (last != current) {
  1044. temp.addElement(value.substring(last, current));
  1045. }
  1046. current++;
  1047. }
  1048. String[] retValue = new String[temp.size()];
  1049. temp.copyInto(retValue);
  1050. return retValue;
  1051. }
  1052. /**
  1053. * Return the point size, given a size index.
  1054. */
  1055. float getPointSize(int index) {
  1056. if (index < 0)
  1057. return sizeMap[0];
  1058. else if (index > sizeMap.length - 1)
  1059. return sizeMap[sizeMap.length - 1];
  1060. else
  1061. return sizeMap[index];
  1062. }
  1063. private void translateEmbeddedAttributes(AttributeSet htmlAttrSet,
  1064. MutableAttributeSet cssAttrSet) {
  1065. Enumeration keys = htmlAttrSet.getAttributeNames();
  1066. if (htmlAttrSet.getAttribute(StyleConstants.NameAttribute) ==
  1067. HTML.Tag.HR) {
  1068. // HR needs special handling due to us treating it as a leaf.
  1069. translateAttributes(HTML.Tag.HR, htmlAttrSet, cssAttrSet);
  1070. }
  1071. while (keys.hasMoreElements()) {
  1072. Object key = keys.nextElement();
  1073. if (key instanceof HTML.Tag) {
  1074. HTML.Tag tag = (HTML.Tag)key;
  1075. Object o = htmlAttrSet.getAttribute(tag);
  1076. if (o != null && o instanceof AttributeSet) {
  1077. translateAttributes(tag, (AttributeSet)o, cssAttrSet);
  1078. }
  1079. } else if (key instanceof CSS.Attribute) {
  1080. cssAttrSet.addAttribute(key, htmlAttrSet.getAttribute(key));
  1081. }
  1082. }
  1083. }
  1084. private void translateAttributes(HTML.Tag tag,
  1085. AttributeSet htmlAttrSet,
  1086. MutableAttributeSet cssAttrSet) {
  1087. Enumeration names = htmlAttrSet.getAttributeNames();
  1088. while (names.hasMoreElements()) {
  1089. Object name = names.nextElement();
  1090. if (name instanceof HTML.Attribute) {
  1091. HTML.Attribute key = (HTML.Attribute)name;
  1092. /*
  1093. * HTML.Attribute.ALIGN needs special processing.
  1094. * It can map to to 1 of many(3) possible CSS attributes
  1095. * depending on the nature of the tag the attribute is
  1096. * part off and depending on the value of the attribute.
  1097. */
  1098. if (key == HTML.Attribute.ALIGN) {
  1099. String htmlAttrValue = (String)htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
  1100. if (htmlAttrValue != null) {
  1101. CSS.Attribute cssAttr = getCssAlignAttribute(tag, htmlAttrSet);
  1102. if (cssAttr != null) {
  1103. Object o = getCssValue(cssAttr, htmlAttrValue);
  1104. if (o != null) {
  1105. cssAttrSet.addAttribute(cssAttr, o);
  1106. }
  1107. }
  1108. }
  1109. } else {
  1110. /*
  1111. * The html size attribute has a mapping in the CSS world only
  1112. * if it is par of a font or base font tag.
  1113. */
  1114. if (key == HTML.Attribute.SIZE && !isHTMLFontTag(tag)) {
  1115. continue;
  1116. }
  1117. translateAttribute(key, htmlAttrSet, cssAttrSet);
  1118. }
  1119. } else if (name instanceof CSS.Attribute) {
  1120. cssAttrSet.addAttribute(name, htmlAttrSet.getAttribute(name));
  1121. }
  1122. }
  1123. }
  1124. private void translateAttribute(HTML.Attribute key,
  1125. AttributeSet htmlAttrSet,
  1126. MutableAttributeSet cssAttrSet) {
  1127. /*
  1128. * In the case of all remaining HTML.Attribute's they
  1129. * map to 1 or more CCS.Attribute.
  1130. */
  1131. CSS.Attribute[] cssAttrList = getCssAttribute(key);
  1132. String htmlAttrValue = (String)htmlAttrSet.getAttribute(key);
  1133. if (cssAttrList == null || htmlAttrValue == null) {
  1134. return;
  1135. }
  1136. for (int i = 0; i < cssAttrList.length; i++) {
  1137. Object o = getCssValue(cssAttrList[i], htmlAttrValue);
  1138. if (o != null) {
  1139. cssAttrSet.addAttribute(cssAttrList[i], o);
  1140. }
  1141. }
  1142. }
  1143. /**
  1144. * Given a CSS.Attribute object and its corresponding HTML.Attribute's
  1145. * value, this method returns a CssValue object to associate with the
  1146. * CSS attribute.
  1147. *
  1148. * @param the CSS.Attribute
  1149. * @param a String containing the value associated HTML.Attribtue.
  1150. */
  1151. Object getCssValue(CSS.Attribute cssAttr, String htmlAttrValue) {
  1152. CssValue value = (CssValue)valueConvertor.get(cssAttr);
  1153. Object o = value.parseHtmlValue(htmlAttrValue);
  1154. return o;
  1155. }
  1156. /**
  1157. * Maps an HTML.Attribute object to its appropriate CSS.Attributes.
  1158. *
  1159. * @param HTML.Attribute
  1160. * @return CSS.Attribute[]
  1161. */
  1162. private CSS.Attribute[] getCssAttribute(HTML.Attribute hAttr) {
  1163. return (CSS.Attribute[])htmlAttrToCssAttrMap.get(hAttr);
  1164. }
  1165. /**
  1166. * Maps HTML.Attribute.ALIGN to either:
  1167. * CSS.Attribute.TEXT_ALIGN
  1168. * CSS.Attribute.FLOAT
  1169. * CSS.Attribute.VERTICAL_ALIGN
  1170. * based on the tag associated with the attribute and the
  1171. * value of the attribute.
  1172. *
  1173. * @param AttributeSet containing HTML attributes.
  1174. * @return CSS.Attribute mapping for HTML.Attribute.ALIGN.
  1175. */
  1176. private CSS.Attribute getCssAlignAttribute(HTML.Tag tag,
  1177. AttributeSet htmlAttrSet) {
  1178. return CSS.Attribute.TEXT_ALIGN;
  1179. /*
  1180. String htmlAttrValue = (String)htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
  1181. CSS.Attribute cssAttr = CSS.Attribute.TEXT_ALIGN;
  1182. if (htmlAttrValue != null && htmlAttrSet instanceof Element) {
  1183. Element elem = (Element)htmlAttrSet;
  1184. if (!elem.isLeaf() && tag.isBlock() && validTextAlignValue(htmlAttrValue)) {
  1185. return CSS.Attribute.TEXT_ALIGN;
  1186. } else if (isFloater(htmlAttrValue)) {
  1187. return CSS.Attribute.FLOAT;
  1188. } else if (elem.isLeaf()) {
  1189. return CSS.Attribute.VERTICAL_ALIGN;
  1190. }
  1191. }
  1192. return null;
  1193. */
  1194. }
  1195. /**
  1196. * Fetches the tag associated with the HTML AttributeSet.
  1197. *
  1198. * @param AttributeSet containing the HTML attributes.
  1199. * @return HTML.Tag
  1200. */
  1201. private HTML.Tag getHTMLTag(AttributeSet htmlAttrSet) {
  1202. Object o = htmlAttrSet.getAttribute(StyleConstants.NameAttribute);
  1203. if (o instanceof HTML.Tag) {
  1204. HTML.Tag tag = (HTML.Tag) o;
  1205. return tag;
  1206. }
  1207. return null;
  1208. }
  1209. private boolean isHTMLFontTag(HTML.Tag tag) {
  1210. return (tag != null && ((tag == HTML.Tag.FONT) || (tag == HTML.Tag.BASEFONT)));
  1211. }
  1212. private boolean isFloater(String alignValue) {
  1213. return (alignValue.equals("left") || alignValue.equals("right"));
  1214. }
  1215. private boolean validTextAlignValue(String alignValue) {
  1216. return (isFloater(alignValue) || alignValue.equals("center"));
  1217. }
  1218. /**
  1219. * Base class to CSS values in the attribute sets. This
  1220. * is intended to act as a convertor to/from other attribute
  1221. * formats.
  1222. * <p>
  1223. * The CSS parser uses the parseCssValue method to convert
  1224. * a string to whatever format is appropriate a given key
  1225. * (i.e. these convertors are stored in a map using the
  1226. * CSS.Attribute as a key and the CssValue as the value).
  1227. * <p>
  1228. * The HTML to CSS conversion process first converts the
  1229. * HTML.Attribute to a CSS.Attribute, and then calls
  1230. * the parseHtmlValue method on the value of the HTML
  1231. * attribute to produce the corresponding CSS value.
  1232. * <p>
  1233. * The StyleConstants to CSS conversion process first
  1234. * converts the StyleConstants attribute to a
  1235. * CSS.Attribute, and then calls the fromStyleConstants
  1236. * method to convert the StyleConstants value to a
  1237. * CSS value.
  1238. * <p>
  1239. * The CSS to StyleConstants conversion process first
  1240. * converts the StyleConstants attribute to a
  1241. * CSS.Attribute, and then calls the toStyleConstants
  1242. * method to convert the CSS value to a StyleConstants
  1243. * value.
  1244. */
  1245. static class CssValue implements Serializable {
  1246. /**
  1247. * Convert a CSS value string to the internal format
  1248. * (for fast processing) used in the attribute sets.
  1249. * The fallback storage for any value that we don't
  1250. * have a special binary format for is a String.
  1251. */
  1252. Object parseCssValue(String value) {
  1253. return value;
  1254. }
  1255. /**
  1256. * Convert an HTML attribute value to a CSS attribute
  1257. * value. If there is no conversion, return null.
  1258. * This is implemented to simply forward to the CSS
  1259. * parsing by default (since some of the attribute
  1260. * values are the same). If the attribute value
  1261. * isn't recognized as a CSS value it is generally
  1262. * returned as null.
  1263. */
  1264. Object parseHtmlValue(String value) {
  1265. return parseCssValue(value);
  1266. }
  1267. /**
  1268. * Convert a StyleConstants attribute value to
  1269. * a CSS attribute value. If there is no conversion
  1270. * return null. By default, there is no conversion.
  1271. *
  1272. * @param key the StyleConstants attribute.
  1273. * @param value the value of a StyleConstants attribute,
  1274. * to be converted.
  1275. * @returns the CSS value that represents the StyleConstants
  1276. * value.
  1277. */
  1278. Object fromStyleConstants(StyleConstants key, Object value) {
  1279. return null;
  1280. }
  1281. /**
  1282. * Convert a CSS attribute value to a StyleConstants
  1283. * value. If there is no conversion, return null.
  1284. * By default, there is no conversion.
  1285. *
  1286. * @param key the StyleConstants attribute.
  1287. * @param v View containing AttributeSet
  1288. * @returns the StyleConstants attribute value that
  1289. * represents the CSS attribute value.
  1290. */
  1291. Object toStyleConstants(StyleConstants key, View v) {
  1292. return null;
  1293. }
  1294. /**
  1295. * Return the CSS format of the value
  1296. */
  1297. public String toString() {
  1298. return svalue;
  1299. }
  1300. /**
  1301. * The value as a string... before conversion to a
  1302. * binary format.
  1303. */
  1304. String svalue;
  1305. }
  1306. /**
  1307. * By default CSS attributes are represented as simple
  1308. * strings. They also have no conversion to/from
  1309. * StyleConstants by default. This class represents the
  1310. * value as a string (via the superclass), but
  1311. * provides StyleConstants conversion support for the
  1312. * CSS attributes that are held as strings.
  1313. */
  1314. static class StringValue extends CssValue {
  1315. /**
  1316. * Convert a CSS value string to the internal format
  1317. * (for fast processing) used in the attribute sets.
  1318. * This produces a StringValue, so that it can be
  1319. * used to convert from CSS to StyleConstants values.
  1320. */
  1321. Object parseCssValue(String value) {
  1322. StringValue sv = new StringValue();
  1323. sv.svalue = value;
  1324. return sv;
  1325. }
  1326. /**
  1327. * Convert a StyleConstants attribute value to
  1328. * a CSS attribute value. If there is no conversion
  1329. * return null.
  1330. *
  1331. * @param key the StyleConstants attribute.
  1332. * @param value the value of a StyleConstants attribute,
  1333. * to be converted.
  1334. * @returns the CSS value that represents the StyleConstants
  1335. * value.
  1336. */
  1337. Object fromStyleConstants(StyleConstants key, Object value) {
  1338. if (key == StyleConstants.Italic) {
  1339. if (value.equals(Boolean.TRUE)) {
  1340. return parseCssValue("italic");
  1341. }
  1342. return parseCssValue("");
  1343. } else if (key == StyleConstants.Underline) {
  1344. if (value.equals(Boolean.TRUE)) {
  1345. return parseCssValue("underline");
  1346. }
  1347. return parseCssValue("");
  1348. } else if (key == StyleConstants.Alignment) {
  1349. int align = ((Integer)value).intValue();
  1350. String ta;
  1351. switch(align) {
  1352. case StyleConstants.ALIGN_LEFT:
  1353. ta = "left";
  1354. break;
  1355. case StyleConstants.ALIGN_RIGHT:
  1356. ta = "right";
  1357. break;
  1358. case StyleConstants.ALIGN_CENTER:
  1359. ta = "center";
  1360. break;
  1361. case StyleConstants.ALIGN_JUSTIFIED:
  1362. ta = "justify";
  1363. break;
  1364. default:
  1365. ta = "left";
  1366. }
  1367. return parseCssValue(ta);
  1368. } else if (key == StyleConstants.StrikeThrough) {
  1369. if (value.equals(Boolean.TRUE)) {
  1370. return parseCssValue("line-through");
  1371. }
  1372. return parseCssValue("");
  1373. } else if (key == StyleConstants.Superscript) {
  1374. if (value.equals(Boolean.TRUE)) {
  1375. return parseCssValue("super");
  1376. }
  1377. return parseCssValue("");
  1378. } else if (key == StyleConstants.Subscript) {
  1379. if (value.equals(Boolean.TRUE)) {
  1380. return parseCssValue("sub");
  1381. }
  1382. return parseCssValue("");
  1383. }
  1384. return null;
  1385. }
  1386. /**
  1387. * Convert a CSS attribute value to a StyleConstants
  1388. * value. If there is no conversion, return null.
  1389. * By default, there is no conversion.
  1390. *
  1391. * @param key the StyleConstants attribute.
  1392. * @returns the StyleConstants attribute value that
  1393. * represents the CSS attribute value.
  1394. */
  1395. Object toStyleConstants(StyleConstants key, View v) {
  1396. if (key == StyleConstants.Italic) {
  1397. if (svalue.indexOf("italic") >= 0) {
  1398. return Boolean.TRUE;
  1399. }
  1400. return Boolean.FALSE;
  1401. } else if (key == StyleConstants.Underline) {
  1402. if (svalue.indexOf("underline") >= 0) {
  1403. return Boolean.TRUE;
  1404. }
  1405. return Boolean.FALSE;
  1406. } else if (key == StyleConstants.Alignment) {
  1407. if (svalue.equals("right")) {
  1408. return new Integer(StyleConstants.ALIGN_RIGHT);
  1409. } else if (svalue.equals("center")) {
  1410. return new Integer(StyleConstants.ALIGN_CENTER);
  1411. } else if (svalue.equals("justify")) {
  1412. return new Integer(StyleConstants.ALIGN_JUSTIFIED);
  1413. }
  1414. return new Integer(StyleConstants.ALIGN_LEFT);
  1415. } else if (key == StyleConstants.StrikeThrough) {
  1416. if (svalue.indexOf("line-through") >= 0) {
  1417. return Boolean.TRUE;
  1418. }
  1419. return Boolean.FALSE;
  1420. } else if (key == StyleConstants.Superscript) {
  1421. if (svalue.indexOf("super") >= 0) {
  1422. return Boolean.TRUE;
  1423. }
  1424. return Boolean.FALSE;
  1425. } else if (key == StyleConstants.Subscript) {
  1426. if (svalue.indexOf("sub") >= 0) {
  1427. return Boolean.TRUE;
  1428. }
  1429. return Boolean.FALSE;
  1430. }
  1431. return null;
  1432. }
  1433. // Used by ViewAttributeSet
  1434. boolean isItalic() {
  1435. return (svalue.indexOf("italic") != -1);
  1436. }
  1437. boolean isStrike() {
  1438. return (svalue.indexOf("line-through") != -1);
  1439. }
  1440. boolean isUnderline() {
  1441. return (svalue.indexOf("underline") != -1);
  1442. }
  1443. boolean isSub() {
  1444. return (svalue.indexOf("sub") != -1);
  1445. }
  1446. boolean isSup() {
  1447. return (svalue.indexOf("sup") != -1);
  1448. }
  1449. }
  1450. /**
  1451. * Represents a value for the CSS.FONT_SIZE attribute.
  1452. * The binary format of the value can be one of several
  1453. * types. If the type is Float,
  1454. * the value is specified in terms of point or
  1455. * percentage, depending upon the ending of the
  1456. * associated string.
  1457. * If the type is Integer, the value is specified
  1458. * in terms of a size index.
  1459. */
  1460. class FontSize extends CssValue {
  1461. /**
  1462. * Returns the size in points. This is ultimately
  1463. * what we need for the purpose of creating/fetching
  1464. * a Font object.
  1465. *
  1466. * @param a the attribute set the value is being
  1467. * requested from. We may need to walk up the
  1468. * resolve hierarchy if it's relative.
  1469. */
  1470. float getValue(AttributeSet a) {
  1471. if (index) {
  1472. // it's an index, translate from size table
  1473. return getPointSize((int) value);
  1474. }
  1475. else if (lu == null) {
  1476. return value;
  1477. }
  1478. else {
  1479. if (lu.type == 0) {
  1480. return lu.value;
  1481. }
  1482. if (a != null) {
  1483. AttributeSet resolveParent = a.getResolveParent();
  1484. if (resolveParent != null) {
  1485. int pValue = StyleConstants.getFontSize(resolveParent);
  1486. float retValue;
  1487. if (lu.type == 1 || lu.type == 3) {
  1488. retValue = lu.value * (float)pValue;
  1489. }
  1490. else {
  1491. retValue = lu.value + (float)pValue;
  1492. }
  1493. return retValue;
  1494. }
  1495. }
  1496. // a is null, or no resolve parent.
  1497. return 12;
  1498. }
  1499. }
  1500. Object parseCssValue(String value) {
  1501. FontSize fs = new FontSize();
  1502. fs.svalue = value;
  1503. try {
  1504. if (value.equals("xx-small")) {
  1505. fs.value = 0;
  1506. fs.index = true;
  1507. } else if (value.equals("x-small")) {
  1508. fs.value = 1;
  1509. fs.index = true;
  1510. } else if (value.equals("small")) {
  1511. fs.value = 2;
  1512. fs.index = true;
  1513. } else if (value.equals("medium")) {
  1514. fs.value = 3;
  1515. fs.index = true;
  1516. } else if (value.equals("large")) {
  1517. fs.value = 4;
  1518. fs.index = true;
  1519. } else if (value.equals("x-large")) {
  1520. fs.value = 5;
  1521. fs.index = true;
  1522. } else if (value.equals("xx-large")) {
  1523. fs.value = 6;
  1524. fs.index = true;
  1525. } else {
  1526. fs.lu = new LengthUnit(value, (short)1, 1f);
  1527. }
  1528. // relative sizes, larger | smaller (adjust from parent by
  1529. // 1.5 pixels)
  1530. // em, ex refer to parent sizes
  1531. // lengths: pt, mm, cm, pc, in, px
  1532. // em (font height 3em would be 3 times font height)
  1533. // ex (height of X)
  1534. // lengths are (+/-) followed by a number and two letter
  1535. // unit identifier
  1536. } catch (NumberFormatException nfe) {
  1537. fs = null;
  1538. }
  1539. return fs;
  1540. }
  1541. Object parseHtmlValue(String value) {
  1542. FontSize fs = new FontSize();
  1543. fs.svalue = value;
  1544. try {
  1545. /*
  1546. * relative sizes in the size attribute are relative
  1547. * to the <basefont>'s size.
  1548. */
  1549. int baseFontSize = getBaseFontSize();
  1550. if ((value != null) && (value.charAt(0) == '+')) {
  1551. int relSize = Integer.valueOf(value.substring(1)).intValue();
  1552. fs.value = baseFontSize + relSize;
  1553. fs.index = true;
  1554. } else if ((value != null) && (value.charAt(0) == '-')) {
  1555. int relSize = -Integer.valueOf(value.substring(1)).intValue();
  1556. fs.value = baseFontSize + relSize;
  1557. fs.index = true;
  1558. } else {
  1559. fs.value = Integer.parseInt(value);
  1560. if (fs.value > 6) {
  1561. fs.value = 6;
  1562. } else if (fs.value < 0) {
  1563. fs.value = 0;
  1564. }
  1565. fs.index = true;
  1566. }
  1567. } catch (NumberFormatException nfe) {
  1568. fs = null;
  1569. }
  1570. return fs;
  1571. }
  1572. /**
  1573. * Convert a StyleConstants attribute value to
  1574. * a CSS attribute value. If there is no conversion
  1575. * return null. By default, there is no conversion.
  1576. *
  1577. * @param key the StyleConstants attribute.
  1578. * @param value the value of a StyleConstants attribute,
  1579. * to be converted.
  1580. * @returns the CSS value that represents the StyleConstants
  1581. * value.
  1582. */
  1583. Object fromStyleConstants(StyleConstants key, Object value) {
  1584. return parseCssValue(value.toString());
  1585. }
  1586. /**
  1587. * Convert a CSS attribute value to a StyleConstants
  1588. * value. If there is no conversion, return null.
  1589. * By default, there is no conversion.
  1590. *
  1591. * @param key the StyleConstants attribute.
  1592. * @returns the StyleConstants attribute value that
  1593. * represents the CSS attribute value.
  1594. */
  1595. Object toStyleConstants(StyleConstants key, View v) {
  1596. if (v != null) {
  1597. return new Integer((int) getValue(v.getAttributes()));
  1598. }
  1599. return new Integer((int) getValue(null));
  1600. }
  1601. float value;
  1602. boolean index;
  1603. LengthUnit lu;
  1604. }
  1605. static class FontFamily extends CssValue {
  1606. /**
  1607. * Returns the font family to use. This is expected
  1608. * to be a legal font for this platform.
  1609. */
  1610. String getValue() {
  1611. return family;
  1612. }
  1613. Object parseCssValue(String value) {
  1614. int cIndex = value.indexOf(',');
  1615. Hashtable validNames = getValidFontNameMapping();
  1616. FontFamily ff = new FontFamily();
  1617. ff.svalue = value;
  1618. ff.family = null;
  1619. if (cIndex == -1) {
  1620. setFontName(validNames, ff, value);
  1621. }
  1622. else {
  1623. boolean done = false;
  1624. int lastIndex;
  1625. int length = value.length();
  1626. cIndex = 0;
  1627. while (!done) {
  1628. // skip ws.
  1629. while (cIndex < length &&
  1630. Character.isWhitespace(value.charAt(cIndex)))
  1631. cIndex++;
  1632. // Find next ','
  1633. lastIndex = cIndex;
  1634. cIndex = value.indexOf(',', cIndex);
  1635. if (cIndex == -1) {
  1636. cIndex = length;
  1637. }
  1638. if (lastIndex < length) {
  1639. if (lastIndex != cIndex) {
  1640. int lastCharIndex = cIndex;
  1641. if (cIndex > 0 && value.charAt(cIndex - 1) == ' '){
  1642. lastCharIndex--;
  1643. }
  1644. setFontName(validNames, ff, value.substring
  1645. (lastIndex, lastCharIndex));
  1646. done = (ff.family != null);
  1647. }
  1648. cIndex++;
  1649. }
  1650. else {
  1651. done = true;
  1652. }
  1653. }
  1654. }
  1655. if (ff.family == null) {
  1656. ff.family = "SansSerif";
  1657. }
  1658. return ff;
  1659. }
  1660. private void setFontName(Hashtable validNames, FontFamily ff,
  1661. String fontName) {
  1662. ff.family = (String)validNames.get(mapFontName(fontName));
  1663. if (ff.family == null) {
  1664. ff.family = (String)validNames.get(fontName.toLowerCase());
  1665. }
  1666. }
  1667. private String mapFontName(String name) {
  1668. if (name.equals("monospace")) {
  1669. return "monospaced";
  1670. }
  1671. return name;
  1672. }
  1673. Object parseHtmlValue(String value) {
  1674. // TBD
  1675. return parseCssValue(value);
  1676. }
  1677. /**
  1678. * Convert a StyleConstants attribute value to
  1679. * a CSS attribute value. If there is no conversion
  1680. * return null. By default, there is no conversion.
  1681. *
  1682. * @param key the StyleConstants attribute.
  1683. * @param value the value of a StyleConstants attribute,
  1684. * to be converted.
  1685. * @returns the CSS value that represents the StyleConstants
  1686. * value.
  1687. */
  1688. Object fromStyleConstants(StyleConstants key, Object value) {
  1689. return parseCssValue(value.toString());
  1690. }
  1691. /**
  1692. * Convert a CSS attribute value to a StyleConstants
  1693. * value. If there is no conversion, return null.
  1694. * By default, there is no conversion.
  1695. *
  1696. * @param key the StyleConstants attribute.
  1697. * @returns the StyleConstants attribute value that
  1698. * represents the CSS attribute value.
  1699. */
  1700. Object toStyleConstants(StyleConstants key, View v) {
  1701. return family;
  1702. }
  1703. String family;
  1704. }
  1705. static class FontWeight extends CssValue {
  1706. int getValue() {
  1707. return weight;
  1708. }
  1709. Object parseCssValue(String value) {
  1710. FontWeight fw = new FontWeight();
  1711. fw.svalue = value;
  1712. if (value.equals("bold")) {
  1713. fw.weight = 700;
  1714. } else if (value.equals("normal")) {
  1715. fw.weight = 400;
  1716. } else {
  1717. // PENDING(prinz) add support for relative values
  1718. try {
  1719. fw.weight = Integer.parseInt(value);
  1720. } catch (NumberFormatException nfe) {
  1721. fw = null;
  1722. }
  1723. }
  1724. return fw;
  1725. }
  1726. /**
  1727. * Convert a StyleConstants attribute value to
  1728. * a CSS attribute value. If there is no conversion
  1729. * return null. By default, there is no conversion.
  1730. *
  1731. * @param key the StyleConstants attribute.
  1732. * @param value the value of a StyleConstants attribute,
  1733. * to be converted.
  1734. * @returns the CSS value that represents the StyleConstants
  1735. * value.
  1736. */
  1737. Object fromStyleConstants(StyleConstants key, Object value) {
  1738. if (value.equals(Boolean.TRUE)) {
  1739. return parseCssValue("bold");
  1740. }
  1741. return parseCssValue("normal");
  1742. }
  1743. /**
  1744. * Convert a CSS attribute value to a StyleConstants
  1745. * value. If there is no conversion, return null.
  1746. * By default, there is no conversion.
  1747. *
  1748. * @param key the StyleConstants attribute.
  1749. * @returns the StyleConstants attribute value that
  1750. * represents the CSS attribute value.
  1751. */
  1752. Object toStyleConstants(StyleConstants key, View v) {
  1753. return (weight > 500) ? Boolean.TRUE : Boolean.FALSE;
  1754. }
  1755. boolean isBold() {
  1756. return (weight > 500);
  1757. }
  1758. int weight;
  1759. }
  1760. static class ColorValue extends CssValue {
  1761. /**
  1762. * Returns the color to use.
  1763. */
  1764. Color getValue() {
  1765. return c;
  1766. }
  1767. Object parseCssValue(String value) {
  1768. Color c = stringToColor(value);
  1769. if (c != null) {
  1770. ColorValue cv = new ColorValue();
  1771. cv.svalue = value;
  1772. cv.c = c;
  1773. return cv;
  1774. }
  1775. return null;
  1776. }
  1777. Object parseHtmlValue(String value) {
  1778. return parseCssValue(value);
  1779. }
  1780. /**
  1781. * Convert a StyleConstants attribute value to
  1782. * a CSS attribute value. If there is no conversion
  1783. * return null. By default, there is no conversion.
  1784. *
  1785. * @param key the StyleConstants attribute.
  1786. * @param value the value of a StyleConstants attribute,
  1787. * to be converted.
  1788. * @returns the CSS value that represents the StyleConstants
  1789. * value.
  1790. */
  1791. Object fromStyleConstants(StyleConstants key, Object value) {
  1792. ColorValue colorValue = new ColorValue();
  1793. colorValue.c = (Color)value;
  1794. colorValue.svalue = colorToHex(colorValue.c);
  1795. return colorValue;
  1796. }
  1797. /**
  1798. * Convert a CSS attribute value to a StyleConstants
  1799. * value. If there is no conversion, return null.
  1800. * By default, there is no conversion.
  1801. *
  1802. * @param key the StyleConstants attribute.
  1803. * @returns the StyleConstants attribute value that
  1804. * represents the CSS attribute value.
  1805. */
  1806. Object toStyleConstants(StyleConstants key, View v) {
  1807. return c;
  1808. }
  1809. Color c;
  1810. }
  1811. static class BorderStyle extends CssValue {
  1812. CSS.Value getValue() {
  1813. return style;
  1814. }
  1815. Object parseCssValue(String value) {
  1816. CSS.Value cssv = CSS.getValue(value);
  1817. if (cssv != null) {
  1818. if ((cssv == CSS.Value.INSET) ||
  1819. (cssv == CSS.Value.OUTSET) ||
  1820. (cssv == CSS.Value.NONE) ||
  1821. (cssv == CSS.Value.DOTTED) ||
  1822. (cssv == CSS.Value.DASHED) ||
  1823. (cssv == CSS.Value.SOLID) ||
  1824. (cssv == CSS.Value.DOUBLE) ||
  1825. (cssv == CSS.Value.GROOVE) ||
  1826. (cssv == CSS.Value.RIDGE)) {
  1827. BorderStyle bs = new BorderStyle();
  1828. bs.svalue = value;
  1829. bs.style = cssv;
  1830. return bs;
  1831. }
  1832. }
  1833. return null;
  1834. }
  1835. private void writeObject(java.io.ObjectOutputStream s)
  1836. throws IOException {
  1837. s.defaultWriteObject();
  1838. if (style == null) {
  1839. s.writeObject(null);
  1840. }
  1841. else {
  1842. s.writeObject(style.toString());
  1843. }
  1844. }
  1845. private void readObject(ObjectInputStream s)
  1846. throws ClassNotFoundException, IOException {
  1847. s.defaultReadObject();
  1848. Object value = s.readObject();
  1849. if (value != null) {
  1850. style = CSS.getValue((String)value);
  1851. }
  1852. }
  1853. // CSS.Values are static, don't archive it.
  1854. transient private CSS.Value style;
  1855. }
  1856. static class LengthValue extends CssValue {
  1857. /**
  1858. * Returns the length (span) to use.
  1859. */
  1860. float getValue() {
  1861. return getValue(0);
  1862. }
  1863. /**
  1864. * Returns the length (span) to use. If the value represents
  1865. * a percentage, it is scaled based on <code>currentValue</code>.
  1866. */
  1867. float getValue(float currentValue) {
  1868. if (percentage) {
  1869. return span * currentValue;
  1870. }
  1871. return span;
  1872. }
  1873. /**
  1874. * Returns true if the length represents a percentage of the
  1875. * containing box.
  1876. */
  1877. boolean isPercentage() {
  1878. return percentage;
  1879. }
  1880. Object parseCssValue(String value) {
  1881. LengthValue lv;
  1882. try {
  1883. // Assume pixels
  1884. float absolute = Float.valueOf(value).floatValue();
  1885. lv = new LengthValue();
  1886. lv.span = absolute;
  1887. } catch (NumberFormatException nfe) {
  1888. // Not pixels, use LengthUnit
  1889. LengthUnit lu = new LengthUnit(value,
  1890. LengthUnit.UNINITALIZED_LENGTH,
  1891. 0);
  1892. // PENDING: currently, we only support absolute values and
  1893. // percentages.
  1894. switch (lu.type) {
  1895. case 0:
  1896. // Absolute
  1897. lv = new LengthValue();
  1898. lv.span = Math.max(0, lu.value);
  1899. break;
  1900. case 1:
  1901. // %
  1902. lv = new LengthValue();
  1903. lv.span = Math.max(0, Math.min(1, lu.value));
  1904. lv.percentage = true;
  1905. break;
  1906. default:
  1907. return null;
  1908. }
  1909. }
  1910. lv.svalue = value;
  1911. return lv;
  1912. }
  1913. Object parseHtmlValue(String value) {
  1914. if (value.equals(HTML.NULL_ATTRIBUTE_VALUE)) {
  1915. value = "1";
  1916. }
  1917. return parseCssValue(value);
  1918. }
  1919. /**
  1920. * Convert a StyleConstants attribute value to
  1921. * a CSS attribute value. If there is no conversion
  1922. * return null. By default, there is no conversion.
  1923. *
  1924. * @param key the StyleConstants attribute.
  1925. * @param value the value of a StyleConstants attribute,
  1926. * to be converted.
  1927. * @returns the CSS value that represents the StyleConstants
  1928. * value.
  1929. */
  1930. Object fromStyleConstants(StyleConstants key, Object value) {
  1931. LengthValue v = new LengthValue();
  1932. v.svalue = value.toString();
  1933. v.span = ((Float)value).floatValue();
  1934. return v;
  1935. }
  1936. /**
  1937. * Convert a CSS attribute value to a StyleConstants
  1938. * value. If there is no conversion, return null.
  1939. * By default, there is no conversion.
  1940. *
  1941. * @param key the StyleConstants attribute.
  1942. * @returns the StyleConstants attribute value that
  1943. * represents the CSS attribute value.
  1944. */
  1945. Object toStyleConstants(StyleConstants key, View v) {
  1946. return new Float(getValue());
  1947. }
  1948. /** If true, span is a percentage value, and that to determine
  1949. * the length another value needs to be passed in. */
  1950. boolean percentage;
  1951. /** Either the absolute value (percentage == false) or
  1952. * a percentage value. */
  1953. float span;
  1954. }
  1955. /**
  1956. * BorderWidthValue is used to model BORDER_XXX_WIDTH and adds support
  1957. * for the thin/medium/thick values.
  1958. */
  1959. static class BorderWidthValue extends LengthValue {
  1960. BorderWidthValue(String svalue, int index) {
  1961. this.svalue = svalue;
  1962. this.index = index;
  1963. }
  1964. float getValue() {
  1965. return values[index];
  1966. }
  1967. float getValue(float currentValue) {
  1968. return values[index];
  1969. }
  1970. Object parseCssValue(String value) {
  1971. if (value != null) {
  1972. if (value.equals("thick")) {
  1973. return new BorderWidthValue(value, 2);
  1974. }
  1975. else if (value.equals("medium")) {
  1976. return new BorderWidthValue(value, 1);
  1977. }
  1978. else if (value.equals("thin")) {
  1979. return new BorderWidthValue(value, 0);
  1980. }
  1981. }
  1982. // Assume its a length.
  1983. return super.parseCssValue(value);
  1984. }
  1985. Object parseHtmlValue(String value) {
  1986. if (value == HTML.NULL_ATTRIBUTE_VALUE) {
  1987. return parseCssValue("medium");
  1988. }
  1989. return parseCssValue(value);
  1990. }
  1991. /** Index into values. */
  1992. private int index;
  1993. /** Values used to represent border width. */
  1994. private static final float[] values = { 1, 2, 4 };
  1995. }
  1996. /**
  1997. * Handles uniquing of CSS values, like lists, and background image
  1998. * repeating.
  1999. */
  2000. static class CssValueMapper extends CssValue {
  2001. Object parseCssValue(String value) {
  2002. Object retValue = cssValueToInternalValueMap.get(value);
  2003. if (retValue == null) {
  2004. retValue = cssValueToInternalValueMap.get(value.toLowerCase());
  2005. }
  2006. return retValue;
  2007. }
  2008. Object parseHtmlValue(String value) {
  2009. Object retValue = htmlValueToCssValueMap.get(value);
  2010. if (retValue == null) {
  2011. retValue = htmlValueToCssValueMap.get(value.toLowerCase());
  2012. }
  2013. return retValue;
  2014. }
  2015. }
  2016. /**
  2017. * Used for background images, to represent the position.
  2018. */
  2019. static class BackgroundPosition extends CssValue {
  2020. float horizontalPosition;
  2021. float verticalPosition;
  2022. // bitmask: bit 0, horizontal relative, bit 1 horizontal relative to
  2023. // font size, 2 vertical relative to size, 3 vertical relative to
  2024. // font size.
  2025. //
  2026. short relative;
  2027. Object parseCssValue(String value) {
  2028. // 'top left' and 'left top' both mean the same as '0% 0%'.
  2029. // 'top', 'top center' and 'center top' mean the same as '50% 0%'.
  2030. // 'right top' and 'top right' mean the same as '100% 0%'.
  2031. // 'left', 'left center' and 'center left' mean the same as
  2032. // '0% 50%'.
  2033. // 'center' and 'center center' mean the same as '50% 50%'.
  2034. // 'right', 'right center' and 'center right' mean the same as
  2035. // '100% 50%'.
  2036. // 'bottom left' and 'left bottom' mean the same as '0% 100%'.
  2037. // 'bottom', 'bottom center' and 'center bottom' mean the same as
  2038. // '50% 100%'.
  2039. // 'bottom right' and 'right bottom' mean the same as '100% 100%'.
  2040. String[] strings = CSS.parseStrings(value);
  2041. int count = strings.length;
  2042. BackgroundPosition bp = new BackgroundPosition();
  2043. bp.relative = 5;
  2044. bp.svalue = value;
  2045. if (count > 0) {
  2046. // bit 0 for vert, 1 hor, 2 for center
  2047. short found = 0;
  2048. int index = 0;
  2049. while (index < count) {
  2050. // First, check for keywords
  2051. String string = strings[index++];
  2052. if (string.equals("center")) {
  2053. found |= 4;
  2054. continue;
  2055. }
  2056. else {
  2057. if ((found & 1) == 0) {
  2058. if (string.equals("top")) {
  2059. found |= 1;
  2060. }
  2061. else if (string.equals("bottom")) {
  2062. found |= 1;
  2063. bp.verticalPosition = 1;
  2064. continue;
  2065. }
  2066. }
  2067. if ((found & 2) == 0) {
  2068. if (string.equals("left")) {
  2069. found |= 2;
  2070. bp.horizontalPosition = 0;
  2071. }
  2072. else if (string.equals("right")) {
  2073. found |= 2;
  2074. bp.horizontalPosition = 1;
  2075. }
  2076. }
  2077. }
  2078. }
  2079. if (found != 0) {
  2080. if ((found & 1) == 1) {
  2081. if ((found & 2) == 0) {
  2082. // vert and no horiz.
  2083. bp.horizontalPosition = .5f;
  2084. }
  2085. }
  2086. else if ((found & 2) == 2) {
  2087. // horiz and no vert.
  2088. bp.verticalPosition = .5f;
  2089. }
  2090. else {
  2091. // no horiz, no vert, but center
  2092. bp.horizontalPosition = bp.verticalPosition = .5f;
  2093. }
  2094. }
  2095. else {
  2096. // Assume lengths
  2097. LengthUnit lu = new LengthUnit(strings[0], (short)0, 0f);
  2098. if (lu.type == 0) {
  2099. bp.horizontalPosition = lu.value;
  2100. bp.relative = (short)(1 ^ bp.relative);
  2101. }
  2102. else if (lu.type == 1) {
  2103. bp.horizontalPosition = lu.value;
  2104. }
  2105. else if (lu.type == 3) {
  2106. bp.horizontalPosition = lu.value;
  2107. bp.relative = (short)((1 ^ bp.relative) | 2);
  2108. }
  2109. if (count > 1) {
  2110. lu = new LengthUnit(strings[1], (short)0, 0f);
  2111. if (lu.type == 0) {
  2112. bp.verticalPosition = lu.value;
  2113. bp.relative = (short)(4 ^ bp.relative);
  2114. }
  2115. else if (lu.type == 1) {
  2116. bp.verticalPosition = lu.value;
  2117. }
  2118. else if (lu.type == 3) {
  2119. bp.verticalPosition = lu.value;
  2120. bp.relative = (short)((4 ^ bp.relative) | 8);
  2121. }
  2122. }
  2123. else {
  2124. bp.verticalPosition = .5f;
  2125. }
  2126. }
  2127. }
  2128. return bp;
  2129. }
  2130. boolean isHorizontalPositionRelativeToSize() {
  2131. return ((relative & 1) == 1);
  2132. }
  2133. boolean isHorizontalPositionRelativeToFontSize() {
  2134. return ((relative & 2) == 2);
  2135. }
  2136. float getHorizontalPosition() {
  2137. return horizontalPosition;
  2138. }
  2139. boolean isVerticalPositionRelativeToSize() {
  2140. return ((relative & 4) == 4);
  2141. }
  2142. boolean isVerticalPositionRelativeToFontSize() {
  2143. return ((relative & 8) == 8);
  2144. }
  2145. float getVerticalPosition() {
  2146. return verticalPosition;
  2147. }
  2148. }
  2149. /**
  2150. * Used for BackgroundImages.
  2151. */
  2152. static class BackgroundImage extends CssValue {
  2153. private boolean loadedImage;
  2154. private ImageIcon image;
  2155. Object parseCssValue(String value) {
  2156. BackgroundImage retValue = new BackgroundImage();
  2157. retValue.svalue = value;
  2158. return retValue;
  2159. }
  2160. Object parseHtmlValue(String value) {
  2161. return parseCssValue(value);
  2162. }
  2163. // PENDING: this base is wrong for linked style sheets.
  2164. ImageIcon getImage(URL base) {
  2165. if (!loadedImage) {
  2166. synchronized(this) {
  2167. if (!loadedImage) {
  2168. URL url = CSS.getURL(base, svalue);
  2169. loadedImage = true;
  2170. if (url != null) {
  2171. image = new ImageIcon(url);
  2172. }
  2173. }
  2174. }
  2175. }
  2176. return image;
  2177. }
  2178. }
  2179. /**
  2180. * Parses a length value, this is used internally, and never added
  2181. * to an AttributeSet or returned to the developer.
  2182. */
  2183. static class LengthUnit implements Serializable {
  2184. static Hashtable lengthMapping = new Hashtable(6);
  2185. static {
  2186. lengthMapping.put("pt", new Float(1f));
  2187. // Not sure about 1.3, determined by experiementation.
  2188. lengthMapping.put("px", new Float(1.3f));
  2189. lengthMapping.put("mm", new Float(2.83464f));
  2190. lengthMapping.put("cm", new Float(28.3464f));
  2191. lengthMapping.put("pc", new Float(12f));
  2192. lengthMapping.put("in", new Float(72f));
  2193. }
  2194. LengthUnit(String value, short defaultType, float defaultValue) {
  2195. parse(value, defaultType, defaultValue);
  2196. }
  2197. void parse(String value, short defaultType, float defaultValue) {
  2198. type = defaultType;
  2199. this.value = defaultValue;
  2200. int length = value.length();
  2201. if (length > 0 && value.charAt(length - 1) == '%') {
  2202. try {
  2203. this.value = Float.valueOf(value.substring(0, length - 1)).
  2204. floatValue() / 100.0f;
  2205. type = 1;
  2206. }
  2207. catch (NumberFormatException nfe) { }
  2208. }
  2209. if (length >= 2) {
  2210. String units = value.substring(length - 2, length);
  2211. Float scale = (Float)lengthMapping.get(units);
  2212. if (scale != null) {
  2213. try {
  2214. this.value = Float.valueOf(value.substring(0,
  2215. length - 2)).floatValue() * scale.floatValue();
  2216. type = 0;
  2217. }
  2218. catch (NumberFormatException nfe) { }
  2219. }
  2220. else if (units.equals("em") ||
  2221. units.equals("ex")) {
  2222. try {
  2223. this.value = Float.valueOf(value.substring(0,
  2224. length - 2)).floatValue();
  2225. type = 3;
  2226. }
  2227. catch (NumberFormatException nfe) { }
  2228. }
  2229. else if (value.equals("larger")) {
  2230. this.value = 2f;
  2231. type = 2;
  2232. }
  2233. else if (value.equals("smaller")) {
  2234. this.value = -2;
  2235. type = 2;
  2236. }
  2237. else {
  2238. // treat like points.
  2239. try {
  2240. this.value = Float.valueOf(value).floatValue();
  2241. type = 0;
  2242. } catch (NumberFormatException nfe) {}
  2243. }
  2244. }
  2245. }
  2246. public String toString() {
  2247. return type + " " + value;
  2248. }
  2249. // 0 - value indicates real value
  2250. // 1 - % value, value relative to depends upon key.
  2251. // 50% will have a value = .5
  2252. // 2 - add value to parent value.
  2253. // 3 - em/ex relative to font size of element (except for
  2254. // font-size, which is relative to parent).
  2255. short type;
  2256. float value;
  2257. static final short UNINITALIZED_LENGTH = (short)10;
  2258. }
  2259. /**
  2260. * Class used to parse font property. The font property is shorthand
  2261. * for the other font properties. This expands the properties, placing
  2262. * them in the attributeset.
  2263. */
  2264. static class ShorthandFontParser {
  2265. /**
  2266. * Parses the shorthand font string <code>value</code>, placing the
  2267. * result in <code>attr</code>.
  2268. */
  2269. static void parseShorthandFont(CSS css, String value,
  2270. MutableAttributeSet attr) {
  2271. // font is of the form:
  2272. // [ <font-style> || <font-variant> || <font-weight> ]? <font-size>
  2273. // [ / <line-height> ]? <font-family>
  2274. String[] strings = CSS.parseStrings(value);
  2275. int count = strings.length;
  2276. int index = 0;
  2277. // bitmask, 1 for style, 2 for variant, 3 for weight
  2278. short found = 0;
  2279. int maxC = Math.min(3, count);
  2280. // Check for font-style font-variant font-weight
  2281. while (index < maxC) {
  2282. if ((found & 1) == 0 && isFontStyle(strings[index])) {
  2283. css.addInternalCSSValue(attr, CSS.Attribute.FONT_STYLE,
  2284. strings[index++]);
  2285. found |= 1;
  2286. }
  2287. else if ((found & 2) == 0 && isFontVariant(strings[index])) {
  2288. css.addInternalCSSValue(attr, CSS.Attribute.FONT_VARIANT,
  2289. strings[index++]);
  2290. found |= 2;
  2291. }
  2292. else if ((found & 4) == 0 && isFontWeight(strings[index])) {
  2293. css.addInternalCSSValue(attr, CSS.Attribute.FONT_WEIGHT,
  2294. strings[index++]);
  2295. found |= 4;
  2296. }
  2297. else if (strings[index].equals("normal")) {
  2298. index++;
  2299. }
  2300. else {
  2301. break;
  2302. }
  2303. }
  2304. if ((found & 1) == 0) {
  2305. css.addInternalCSSValue(attr, CSS.Attribute.FONT_STYLE,
  2306. "normal");
  2307. }
  2308. if ((found & 2) == 0) {
  2309. css.addInternalCSSValue(attr, CSS.Attribute.FONT_VARIANT,
  2310. "normal");
  2311. }
  2312. if ((found & 4) == 0) {
  2313. css.addInternalCSSValue(attr, CSS.Attribute.FONT_WEIGHT,
  2314. "normal");
  2315. }
  2316. // string at index should be the font-size
  2317. if (index < count) {
  2318. String fontSize = strings[index];
  2319. int slashIndex = fontSize.indexOf('/');
  2320. if (slashIndex != -1) {
  2321. fontSize = fontSize.substring(0, slashIndex);
  2322. strings[index] = strings[index].substring(slashIndex);
  2323. }
  2324. else {
  2325. index++;
  2326. }
  2327. css.addInternalCSSValue(attr, CSS.Attribute.FONT_SIZE,
  2328. fontSize);
  2329. }
  2330. else {
  2331. css.addInternalCSSValue(attr, CSS.Attribute.FONT_SIZE,
  2332. "medium");
  2333. }
  2334. // Check for line height
  2335. if (index < count && strings[index].startsWith("/")) {
  2336. String lineHeight = null;
  2337. if (strings[index].equals("/")) {
  2338. if (++index < count) {
  2339. lineHeight = strings[index++];
  2340. }
  2341. }
  2342. else {
  2343. lineHeight = strings[index++].substring(1);
  2344. }
  2345. // line height
  2346. if (lineHeight != null) {
  2347. css.addInternalCSSValue(attr, CSS.Attribute.LINE_HEIGHT,
  2348. lineHeight);
  2349. }
  2350. else {
  2351. css.addInternalCSSValue(attr, CSS.Attribute.LINE_HEIGHT,
  2352. "normal");
  2353. }
  2354. }
  2355. else {
  2356. css.addInternalCSSValue(attr, CSS.Attribute.LINE_HEIGHT,
  2357. "normal");
  2358. }
  2359. // remainder of strings are font-family
  2360. if (index < count) {
  2361. String family = strings[index++];
  2362. while (index < count) {
  2363. family += " " + strings[index++];
  2364. }
  2365. css.addInternalCSSValue(attr, CSS.Attribute.FONT_FAMILY,
  2366. family);
  2367. }
  2368. else {
  2369. css.addInternalCSSValue(attr, CSS.Attribute.FONT_FAMILY,
  2370. "SansSerif");
  2371. }
  2372. }
  2373. private static boolean isFontStyle(String string) {
  2374. return (string.equals("italic") ||
  2375. string.equals("oblique"));
  2376. }
  2377. private static boolean isFontVariant(String string) {
  2378. return (string.equals("small-caps"));
  2379. }
  2380. private static boolean isFontWeight(String string) {
  2381. if (string.equals("bold") || string.equals("bolder") ||
  2382. string.equals("italic") || string.equals("lighter")) {
  2383. return true;
  2384. }
  2385. // test for 100-900
  2386. return (string.length() == 3 &&
  2387. string.charAt(0) >= '1' && string.charAt(0) <= '9' &&
  2388. string.charAt(1) == '0' && string.charAt(2) == '0');
  2389. }
  2390. }
  2391. /**
  2392. * Parses the background property into its intrinsic values.
  2393. */
  2394. static class ShorthandBackgroundParser {
  2395. /**
  2396. * Parses the shorthand font string <code>value</code>, placing the
  2397. * result in <code>attr</code>.
  2398. */
  2399. static void parseShorthandBackground(CSS css, String value,
  2400. MutableAttributeSet attr) {
  2401. String[] strings = parseStrings(value);
  2402. int count = strings.length;
  2403. int index = 0;
  2404. // bitmask: 0 for image, 1 repeat, 2 attachment, 3 position,
  2405. // 4 color
  2406. short found = 0;
  2407. while (index < count) {
  2408. String string = strings[index++];
  2409. if ((found & 1) == 0 && isImage(string)) {
  2410. css.addInternalCSSValue(attr, CSS.Attribute.
  2411. BACKGROUND_IMAGE, string);
  2412. found |= 1;
  2413. }
  2414. else if ((found & 2) == 0 && isRepeat(string)) {
  2415. css.addInternalCSSValue(attr, CSS.Attribute.
  2416. BACKGROUND_REPEAT, string);
  2417. found |= 2;
  2418. }
  2419. else if ((found & 4) == 0 && isAttachment(string)) {
  2420. css.addInternalCSSValue(attr, CSS.Attribute.
  2421. BACKGROUND_ATTACHMENT, string);
  2422. found |= 4;
  2423. }
  2424. else if ((found & 8) == 0 && isPosition(string)) {
  2425. if (index < count && isPosition(strings[index])) {
  2426. css.addInternalCSSValue(attr, CSS.Attribute.
  2427. BACKGROUND_POSITION,
  2428. string + " " +
  2429. strings[index++]);
  2430. }
  2431. else {
  2432. css.addInternalCSSValue(attr, CSS.Attribute.
  2433. BACKGROUND_POSITION, string);
  2434. }
  2435. found |= 8;
  2436. }
  2437. else if ((found & 16) == 0 && isColor(string)) {
  2438. css.addInternalCSSValue(attr, CSS.Attribute.
  2439. BACKGROUND_COLOR, string);
  2440. found |= 16;
  2441. }
  2442. }
  2443. if ((found & 1) == 0) {
  2444. css.addInternalCSSValue(attr, CSS.Attribute.BACKGROUND_IMAGE,
  2445. null);
  2446. }
  2447. if ((found & 2) == 0) {
  2448. css.addInternalCSSValue(attr, CSS.Attribute.BACKGROUND_REPEAT,
  2449. "repeat");
  2450. }
  2451. if ((found & 4) == 0) {
  2452. css.addInternalCSSValue(attr, CSS.Attribute.
  2453. BACKGROUND_ATTACHMENT, "scroll");
  2454. }
  2455. if ((found & 8) == 0) {
  2456. css.addInternalCSSValue(attr, CSS.Attribute.
  2457. BACKGROUND_POSITION, null);
  2458. }
  2459. // Currently, there is no good way to express this.
  2460. /*
  2461. if ((found & 16) == 0) {
  2462. css.addInternalCSSValue(attr, CSS.Attribute.BACKGROUND_COLOR,
  2463. null);
  2464. }
  2465. */
  2466. }
  2467. static boolean isImage(String string) {
  2468. return (string.startsWith("url(") && string.endsWith(")"));
  2469. }
  2470. static boolean isRepeat(String string) {
  2471. return (string.equals("repeat-x") || string.equals("repeat-y") ||
  2472. string.equals("repeat") || string.equals("no-repeat"));
  2473. }
  2474. static boolean isAttachment(String string) {
  2475. return (string.equals("fixed") || string.equals("scroll"));
  2476. }
  2477. static boolean isPosition(String string) {
  2478. return (string.equals("top") || string.equals("bottom") ||
  2479. string.equals("left") || string.equals("right") ||
  2480. string.equals("center") ||
  2481. (string.length() > 0 &&
  2482. Character.isDigit(string.charAt(0))));
  2483. }
  2484. static boolean isColor(String string) {
  2485. return (CSS.stringToColor(string) != null);
  2486. }
  2487. }
  2488. /**
  2489. * Used to parser margin and padding.
  2490. */
  2491. static class ShorthandMarginParser {
  2492. /**
  2493. * Parses the shorthand margin/padding/border string
  2494. * <code>value</code>, placing the result in <code>attr</code>.
  2495. * <code>names</code> give the 4 instrinsic property names.
  2496. */
  2497. static void parseShorthandMargin(CSS css, String value,
  2498. MutableAttributeSet attr,
  2499. CSS.Attribute[] names) {
  2500. String[] strings = parseStrings(value);
  2501. int count = strings.length;
  2502. int index = 0;
  2503. switch (count) {
  2504. case 0:
  2505. // empty string
  2506. return;
  2507. case 1:
  2508. // Identifies all values.
  2509. for (int counter = 0; counter < 4; counter++) {
  2510. css.addInternalCSSValue(attr, names[counter], strings[0]);
  2511. }
  2512. break;
  2513. case 2:
  2514. // 0 & 2 = strings[0], 1 & 3 = strings[1]
  2515. css.addInternalCSSValue(attr, names[0], strings[0]);
  2516. css.addInternalCSSValue(attr, names[2], strings[0]);
  2517. css.addInternalCSSValue(attr, names[1], strings[1]);
  2518. css.addInternalCSSValue(attr, names[3], strings[1]);
  2519. break;
  2520. case 3:
  2521. css.addInternalCSSValue(attr, names[0], strings[0]);
  2522. css.addInternalCSSValue(attr, names[1], strings[1]);
  2523. css.addInternalCSSValue(attr, names[2], strings[2]);
  2524. css.addInternalCSSValue(attr, names[3], strings[1]);
  2525. break;
  2526. default:
  2527. for (int counter = 0; counter < 4; counter++) {
  2528. css.addInternalCSSValue(attr, names[counter],
  2529. strings[counter]);
  2530. }
  2531. break;
  2532. }
  2533. }
  2534. }
  2535. /**
  2536. * Calculate the requirements needed to tile the requirements
  2537. * given by the iterator that would be tiled. The calculation
  2538. * takes into consideration margin collapsing.
  2539. */
  2540. static SizeRequirements calculateTiledRequirements(LayoutIterator iter, SizeRequirements r) {
  2541. long minimum = 0;
  2542. long maximum = 0;
  2543. long preferred = 0;
  2544. int lastMargin = 0;
  2545. int collapsed = 0;
  2546. int n = iter.getCount();
  2547. for (int i = 0; i < n; i++) {
  2548. iter.setIndex(i);
  2549. int margin0 = lastMargin;
  2550. int margin1 = (int) iter.getLeadingCollapseSpan();
  2551. int offset = Math.max(margin0, margin1);
  2552. if (offset != 0) {
  2553. // There is a collapse area
  2554. collapsed += offset - margin0;
  2555. }
  2556. preferred += (int) iter.getPreferredSpan(0);
  2557. minimum += iter.getMinimumSpan(0);
  2558. maximum += iter.getMaximumSpan(0);
  2559. lastMargin = (int) iter.getTrailingCollapseSpan();
  2560. }
  2561. // adjust for the collapsed area
  2562. minimum -= collapsed;
  2563. preferred -= collapsed;
  2564. maximum -= collapsed;
  2565. // set return value
  2566. if (r == null) {
  2567. r = new SizeRequirements();
  2568. }
  2569. r.minimum = (minimum > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)minimum;
  2570. r.preferred = (preferred > Integer.MAX_VALUE) ? Integer.MAX_VALUE :(int) preferred;
  2571. r.maximum = (maximum > Integer.MAX_VALUE) ? Integer.MAX_VALUE :(int) maximum;
  2572. return r;
  2573. }
  2574. /**
  2575. * Calculate a tiled layout for the given iterator.
  2576. * This should be done collapsing the neighboring
  2577. * margins to be a total of the maximum of the two
  2578. * neighboring margin areas as described in the CSS spec.
  2579. */
  2580. static void calculateTiledLayout(LayoutIterator iter, int targetSpan) {
  2581. /*
  2582. * first pass, calculate the preferred sizes, adjustments needed because
  2583. * of margin collapsing, and the flexibility to adjust the sizes.
  2584. */
  2585. long minimum = 0;
  2586. long maximum = 0;
  2587. long preferred = 0;
  2588. int lastMargin = 0;
  2589. int collapsed = 0;
  2590. int n = iter.getCount();
  2591. for (int i = 0; i < n; i++) {
  2592. iter.setIndex(i);
  2593. int margin0 = lastMargin;
  2594. int margin1 = (int) iter.getLeadingCollapseSpan();
  2595. iter.setOffset(Math.max(margin0, margin1));
  2596. if (iter.getOffset() != 0) {
  2597. // There is a collapse area
  2598. iter.setOffset(iter.getOffset() - margin0);
  2599. collapsed += iter.getOffset();
  2600. }
  2601. iter.setSpan( (int) iter.getPreferredSpan(targetSpan));
  2602. preferred += iter.getSpan();
  2603. minimum += iter.getMinimumSpan(targetSpan);
  2604. maximum += iter.getMaximumSpan(targetSpan);
  2605. lastMargin = (int) iter.getTrailingCollapseSpan();
  2606. }
  2607. /*
  2608. * Second pass, expand or contract by as much as possible to reach
  2609. * the target span. This takes the margin collapsing into account
  2610. * prior to adjusting the span.
  2611. */
  2612. // determine the adjustment to be made
  2613. int allocated = targetSpan - collapsed;
  2614. long desiredAdjustment = allocated - preferred;
  2615. float adjustmentFactor = 0.0f;
  2616. if (desiredAdjustment != 0) {
  2617. float maximumAdjustment = (desiredAdjustment > 0) ?
  2618. maximum - preferred : preferred - minimum;
  2619. adjustmentFactor = desiredAdjustment / maximumAdjustment;
  2620. adjustmentFactor = Math.min(adjustmentFactor, 1.0f);
  2621. adjustmentFactor = Math.max(adjustmentFactor, -1.0f);
  2622. }
  2623. // make the adjustments
  2624. int totalOffset = 0;
  2625. for (int i = 0; i < n; i++) {
  2626. iter.setIndex(i);
  2627. iter.setOffset( iter.getOffset() + totalOffset);
  2628. int availableSpan = (adjustmentFactor > 0.0f) ?
  2629. (int) iter.getMaximumSpan(targetSpan) - iter.getSpan() :
  2630. iter.getSpan() - (int) iter.getMinimumSpan(targetSpan);
  2631. int adj = (int) (adjustmentFactor * availableSpan + 0.5f);
  2632. iter.setSpan( iter.getSpan() + adj);
  2633. totalOffset = (int) Math.min((long) totalOffset + (long) iter.getSpan(), Integer.MAX_VALUE);
  2634. }
  2635. }
  2636. /**
  2637. * An iterator to express the requirements to use when computing
  2638. * layout.
  2639. */
  2640. interface LayoutIterator {
  2641. void setOffset(int offs);
  2642. int getOffset();
  2643. void setSpan(int span);
  2644. int getSpan();
  2645. int getCount();
  2646. void setIndex(int i);
  2647. float getMinimumSpan(float parentSpan);
  2648. float getPreferredSpan(float parentSpan);
  2649. float getMaximumSpan(float parentSpan);
  2650. //float getAlignment();
  2651. float getLeadingCollapseSpan();
  2652. float getTrailingCollapseSpan();
  2653. }
  2654. //
  2655. // Serialization support
  2656. //
  2657. private void writeObject(java.io.ObjectOutputStream s)
  2658. throws IOException
  2659. {
  2660. s.defaultWriteObject();
  2661. // Determine what values in valueConvertor need to be written out.
  2662. Enumeration keys = valueConvertor.keys();
  2663. s.writeInt(valueConvertor.size());
  2664. if (keys != null) {
  2665. while (keys.hasMoreElements()) {
  2666. Object key = keys.nextElement();
  2667. Object value = valueConvertor.get(key);
  2668. if (!(key instanceof Serializable) &&
  2669. (key = StyleContext.getStaticAttributeKey(key)) == null) {
  2670. // Should we throw an exception here?
  2671. key = null;
  2672. value = null;
  2673. }
  2674. else if (!(value instanceof Serializable) &&
  2675. (value = StyleContext.getStaticAttributeKey(value)) == null){
  2676. // Should we throw an exception here?
  2677. key = null;
  2678. value = null;
  2679. }
  2680. s.writeObject(key);
  2681. s.writeObject(value);
  2682. }
  2683. }
  2684. }
  2685. private void readObject(ObjectInputStream s)
  2686. throws ClassNotFoundException, IOException
  2687. {
  2688. s.defaultReadObject();
  2689. // Reconstruct the hashtable.
  2690. int numValues = s.readInt();
  2691. valueConvertor = new Hashtable(Math.max(1, numValues));
  2692. while (numValues-- > 0) {
  2693. Object key = s.readObject();
  2694. Object value = s.readObject();
  2695. Object staticKey = StyleContext.getStaticAttribute(key);
  2696. if (staticKey != null) {
  2697. key = staticKey;
  2698. }
  2699. Object staticValue = StyleContext.getStaticAttribute(value);
  2700. if (staticValue != null) {
  2701. value = staticValue;
  2702. }
  2703. if (key != null && value != null) {
  2704. valueConvertor.put(key, value);
  2705. }
  2706. }
  2707. }
  2708. //
  2709. // Instance variables
  2710. //
  2711. /** Maps from CSS key to CssValue. */
  2712. private transient Hashtable valueConvertor;
  2713. /** Size used for relative units. */
  2714. private int baseFontSize;
  2715. }