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