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