/*
* Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.awt.Color;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.awt.HeadlessException;
import java.awt.Image;
import java.io.*;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Locale;
import javax.swing.ImageIcon;
import javax.swing.SizeRequirements;
import javax.swing.text.*;
Defines a set of
CSS attributes
as a typesafe enumeration. The HTML View implementations use
CSS attributes to determine how they will render. This also defines
methods to map between CSS/HTML/StyleConstants. Any shorthand
properties, such as font, are mapped to the intrinsic properties.
The following describes the CSS properties that are supported by the
rendering engine:
- font-family
- font-style
- font-size (supports relative units)
- font-weight
- font
- color
- background-color (with the exception of transparent)
- background-image
- background-repeat
- background-position
- background
- text-decoration (with the exception of blink and overline)
- vertical-align (only sup and super)
- text-align (justify is treated as center)
- margin-top
- margin-right
- margin-bottom
- margin-left
- margin
- padding-top
- padding-right
- padding-bottom
- padding-left
- padding
- border-top-style
- border-right-style
- border-bottom-style
- border-left-style
- border-style (only supports inset, outset and none)
- border-top-color
- border-right-color
- border-bottom-color
- border-left-color
- border-color
- list-style-image
- list-style-type
- list-style-position
The following are modeled, but currently not rendered.
- font-variant
- background-attachment (background always treated as scroll)
- word-spacing
- letter-spacing
- text-indent
- text-transform
- line-height
- border-top-width (this is used to indicate if a border should be used)
- border-right-width
- border-bottom-width
- border-left-width
- border-width
- border-top
- border-right
- border-bottom
- border-left
- border
- width
- height
- float
- clear
- display
- white-space
- list-style
Note: for the time being we do not fully support relative units,
unless noted, so that
p { margin-top: 10% } will be treated as if no margin-top was specified.
Author: Timothy Prinzing, Scott Violet See Also:
/**
* Defines a set of
* <a href="http://www.w3.org/TR/REC-CSS1">CSS attributes</a>
* as a typesafe enumeration. The HTML View implementations use
* CSS attributes to determine how they will render. This also defines
* methods to map between CSS/HTML/StyleConstants. Any shorthand
* properties, such as font, are mapped to the intrinsic properties.
* <p>The following describes the CSS properties that are supported by the
* rendering engine:
* <ul><li>font-family
* <li>font-style
* <li>font-size (supports relative units)
* <li>font-weight
* <li>font
* <li>color
* <li>background-color (with the exception of transparent)
* <li>background-image
* <li>background-repeat
* <li>background-position
* <li>background
* <li>text-decoration (with the exception of blink and overline)
* <li>vertical-align (only sup and super)
* <li>text-align (justify is treated as center)
* <li>margin-top
* <li>margin-right
* <li>margin-bottom
* <li>margin-left
* <li>margin
* <li>padding-top
* <li>padding-right
* <li>padding-bottom
* <li>padding-left
* <li>padding
* <li>border-top-style
* <li>border-right-style
* <li>border-bottom-style
* <li>border-left-style
* <li>border-style (only supports inset, outset and none)
* <li>border-top-color
* <li>border-right-color
* <li>border-bottom-color
* <li>border-left-color
* <li>border-color
* <li>list-style-image
* <li>list-style-type
* <li>list-style-position
* </ul>
* The following are modeled, but currently not rendered.
* <ul><li>font-variant
* <li>background-attachment (background always treated as scroll)
* <li>word-spacing
* <li>letter-spacing
* <li>text-indent
* <li>text-transform
* <li>line-height
* <li>border-top-width (this is used to indicate if a border should be used)
* <li>border-right-width
* <li>border-bottom-width
* <li>border-left-width
* <li>border-width
* <li>border-top
* <li>border-right
* <li>border-bottom
* <li>border-left
* <li>border
* <li>width
* <li>height
* <li>float
* <li>clear
* <li>display
* <li>white-space
* <li>list-style
* </ul>
* <p><b>Note: for the time being we do not fully support relative units,
* unless noted, so that
* p { margin-top: 10% } will be treated as if no margin-top was specified.
*
* @author Timothy Prinzing
* @author Scott Violet
* @see StyleSheet
*/
public class CSS implements Serializable {
Definitions to be used as a key on AttributeSet's
that might hold CSS attributes. Since this is a
closed set (i.e. defined exactly by the specification),
it is final and cannot be extended.
/**
* Definitions to be used as a key on AttributeSet's
* that might hold CSS attributes. Since this is a
* closed set (i.e. defined exactly by the specification),
* it is final and cannot be extended.
*/
public static final class Attribute {
private Attribute(String name, String defaultValue, boolean inherited) {
this.name = name;
this.defaultValue = defaultValue;
this.inherited = inherited;
}
The string representation of the attribute. This
should exactly match the string specified in the
CSS specification.
/**
* The string representation of the attribute. This
* should exactly match the string specified in the
* CSS specification.
*/
public String toString() {
return name;
}
Fetch the default value for the attribute.
If there is no default value (such as for
composite attributes), null will be returned.
/**
* Fetch the default value for the attribute.
* If there is no default value (such as for
* composite attributes), null will be returned.
*/
public String getDefaultValue() {
return defaultValue;
}
Indicates if the attribute should be inherited
from the parent or not.
/**
* Indicates if the attribute should be inherited
* from the parent or not.
*/
public boolean isInherited() {
return inherited;
}
private String name;
private String defaultValue;
private boolean inherited;
public static final Attribute BACKGROUND =
new Attribute("background", null, false);
public static final Attribute BACKGROUND_ATTACHMENT =
new Attribute("background-attachment", "scroll", false);
public static final Attribute BACKGROUND_COLOR =
new Attribute("background-color", "transparent", false);
public static final Attribute BACKGROUND_IMAGE =
new Attribute("background-image", "none", false);
public static final Attribute BACKGROUND_POSITION =
new Attribute("background-position", null, false);
public static final Attribute BACKGROUND_REPEAT =
new Attribute("background-repeat", "repeat", false);
public static final Attribute BORDER =
new Attribute("border", null, false);
public static final Attribute BORDER_BOTTOM =
new Attribute("border-bottom", null, false);
public static final Attribute BORDER_BOTTOM_COLOR =
new Attribute("border-bottom-color", null, false);
public static final Attribute BORDER_BOTTOM_STYLE =
new Attribute("border-bottom-style", "none", false);
public static final Attribute BORDER_BOTTOM_WIDTH =
new Attribute("border-bottom-width", "medium", false);
public static final Attribute BORDER_COLOR =
new Attribute("border-color", null, false);
public static final Attribute BORDER_LEFT =
new Attribute("border-left", null, false);
public static final Attribute BORDER_LEFT_COLOR =
new Attribute("border-left-color", null, false);
public static final Attribute BORDER_LEFT_STYLE =
new Attribute("border-left-style", "none", false);
public static final Attribute BORDER_LEFT_WIDTH =
new Attribute("border-left-width", "medium", false);
public static final Attribute BORDER_RIGHT =
new Attribute("border-right", null, false);
public static final Attribute BORDER_RIGHT_COLOR =
new Attribute("border-right-color", null, false);
public static final Attribute BORDER_RIGHT_STYLE =
new Attribute("border-right-style", "none", false);
public static final Attribute BORDER_RIGHT_WIDTH =
new Attribute("border-right-width", "medium", false);
public static final Attribute BORDER_STYLE =
new Attribute("border-style", "none", false);
public static final Attribute BORDER_TOP =
new Attribute("border-top", null, false);
public static final Attribute BORDER_TOP_COLOR =
new Attribute("border-top-color", null, false);
public static final Attribute BORDER_TOP_STYLE =
new Attribute("border-top-style", "none", false);
public static final Attribute BORDER_TOP_WIDTH =
new Attribute("border-top-width", "medium", false);
public static final Attribute BORDER_WIDTH =
new Attribute("border-width", "medium", false);
public static final Attribute CLEAR =
new Attribute("clear", "none", false);
public static final Attribute COLOR =
new Attribute("color", "black", true);
public static final Attribute DISPLAY =
new Attribute("display", "block", false);
public static final Attribute FLOAT =
new Attribute("float", "none", false);
public static final Attribute FONT =
new Attribute("font", null, true);
public static final Attribute FONT_FAMILY =
new Attribute("font-family", null, true);
public static final Attribute FONT_SIZE =
new Attribute("font-size", "medium", true);
public static final Attribute FONT_STYLE =
new Attribute("font-style", "normal", true);
public static final Attribute FONT_VARIANT =
new Attribute("font-variant", "normal", true);
public static final Attribute FONT_WEIGHT =
new Attribute("font-weight", "normal", true);
public static final Attribute HEIGHT =
new Attribute("height", "auto", false);
public static final Attribute LETTER_SPACING =
new Attribute("letter-spacing", "normal", true);
public static final Attribute LINE_HEIGHT =
new Attribute("line-height", "normal", true);
public static final Attribute LIST_STYLE =
new Attribute("list-style", null, true);
public static final Attribute LIST_STYLE_IMAGE =
new Attribute("list-style-image", "none", true);
public static final Attribute LIST_STYLE_POSITION =
new Attribute("list-style-position", "outside", true);
public static final Attribute LIST_STYLE_TYPE =
new Attribute("list-style-type", "disc", true);
public static final Attribute MARGIN =
new Attribute("margin", null, false);
public static final Attribute MARGIN_BOTTOM =
new Attribute("margin-bottom", "0", false);
public static final Attribute MARGIN_LEFT =
new Attribute("margin-left", "0", false);
public static final Attribute MARGIN_RIGHT =
new Attribute("margin-right", "0", false);
/*
* made up css attributes to describe orientation depended
* margins. used for <dir>, <menu>, <ul> etc. see
* 5088268 for more details
*/
static final Attribute MARGIN_LEFT_LTR =
new Attribute("margin-left-ltr",
Integer.toString(Integer.MIN_VALUE), false);
static final Attribute MARGIN_LEFT_RTL =
new Attribute("margin-left-rtl",
Integer.toString(Integer.MIN_VALUE), false);
static final Attribute MARGIN_RIGHT_LTR =
new Attribute("margin-right-ltr",
Integer.toString(Integer.MIN_VALUE), false);
static final Attribute MARGIN_RIGHT_RTL =
new Attribute("margin-right-rtl",
Integer.toString(Integer.MIN_VALUE), false);
public static final Attribute MARGIN_TOP =
new Attribute("margin-top", "0", false);
public static final Attribute PADDING =
new Attribute("padding", null, false);
public static final Attribute PADDING_BOTTOM =
new Attribute("padding-bottom", "0", false);
public static final Attribute PADDING_LEFT =
new Attribute("padding-left", "0", false);
public static final Attribute PADDING_RIGHT =
new Attribute("padding-right", "0", false);
public static final Attribute PADDING_TOP =
new Attribute("padding-top", "0", false);
public static final Attribute TEXT_ALIGN =
new Attribute("text-align", null, true);
public static final Attribute TEXT_DECORATION =
new Attribute("text-decoration", "none", true);
public static final Attribute TEXT_INDENT =
new Attribute("text-indent", "0", true);
public static final Attribute TEXT_TRANSFORM =
new Attribute("text-transform", "none", true);
public static final Attribute VERTICAL_ALIGN =
new Attribute("vertical-align", "baseline", false);
public static final Attribute WORD_SPACING =
new Attribute("word-spacing", "normal", true);
public static final Attribute WHITE_SPACE =
new Attribute("white-space", "normal", true);
public static final Attribute WIDTH =
new Attribute("width", "auto", false);
/*public*/ static final Attribute BORDER_SPACING =
new Attribute("border-spacing", "0", true);
/*public*/ static final Attribute CAPTION_SIDE =
new Attribute("caption-side", "left", true);
// All possible CSS attribute keys.
static final Attribute[] allAttributes = {
BACKGROUND, BACKGROUND_ATTACHMENT, BACKGROUND_COLOR,
BACKGROUND_IMAGE, BACKGROUND_POSITION, BACKGROUND_REPEAT,
BORDER, BORDER_BOTTOM, BORDER_BOTTOM_WIDTH, BORDER_COLOR,
BORDER_LEFT, BORDER_LEFT_WIDTH, BORDER_RIGHT, BORDER_RIGHT_WIDTH,
BORDER_STYLE, BORDER_TOP, BORDER_TOP_WIDTH, BORDER_WIDTH,
BORDER_TOP_STYLE, BORDER_RIGHT_STYLE, BORDER_BOTTOM_STYLE,
BORDER_LEFT_STYLE,
BORDER_TOP_COLOR, BORDER_RIGHT_COLOR, BORDER_BOTTOM_COLOR,
BORDER_LEFT_COLOR,
CLEAR, COLOR, DISPLAY, FLOAT, FONT, FONT_FAMILY, FONT_SIZE,
FONT_STYLE, FONT_VARIANT, FONT_WEIGHT, HEIGHT, LETTER_SPACING,
LINE_HEIGHT, LIST_STYLE, LIST_STYLE_IMAGE, LIST_STYLE_POSITION,
LIST_STYLE_TYPE, MARGIN, MARGIN_BOTTOM, MARGIN_LEFT, MARGIN_RIGHT,
MARGIN_TOP, PADDING, PADDING_BOTTOM, PADDING_LEFT, PADDING_RIGHT,
PADDING_TOP, TEXT_ALIGN, TEXT_DECORATION, TEXT_INDENT, TEXT_TRANSFORM,
VERTICAL_ALIGN, WORD_SPACING, WHITE_SPACE, WIDTH,
BORDER_SPACING, CAPTION_SIDE,
MARGIN_LEFT_LTR, MARGIN_LEFT_RTL, MARGIN_RIGHT_LTR, MARGIN_RIGHT_RTL
};
private static final Attribute[] ALL_MARGINS =
{ MARGIN_TOP, MARGIN_RIGHT, MARGIN_BOTTOM, MARGIN_LEFT };
private static final Attribute[] ALL_PADDING =
{ PADDING_TOP, PADDING_RIGHT, PADDING_BOTTOM, PADDING_LEFT };
private static final Attribute[] ALL_BORDER_WIDTHS =
{ BORDER_TOP_WIDTH, BORDER_RIGHT_WIDTH, BORDER_BOTTOM_WIDTH,
BORDER_LEFT_WIDTH };
private static final Attribute[] ALL_BORDER_STYLES =
{ BORDER_TOP_STYLE, BORDER_RIGHT_STYLE, BORDER_BOTTOM_STYLE,
BORDER_LEFT_STYLE };
private static final Attribute[] ALL_BORDER_COLORS =
{ BORDER_TOP_COLOR, BORDER_RIGHT_COLOR, BORDER_BOTTOM_COLOR,
BORDER_LEFT_COLOR };
}
static final class Value {
private Value(String name) {
this.name = name;
}
The string representation of the attribute. This
should exactly match the string specified in the
CSS specification.
/**
* The string representation of the attribute. This
* should exactly match the string specified in the
* CSS specification.
*/
public String toString() {
return name;
}
static final Value INHERITED = new Value("inherited");
static final Value NONE = new Value("none");
static final Value HIDDEN = new Value("hidden");
static final Value DOTTED = new Value("dotted");
static final Value DASHED = new Value("dashed");
static final Value SOLID = new Value("solid");
static final Value DOUBLE = new Value("double");
static final Value GROOVE = new Value("groove");
static final Value RIDGE = new Value("ridge");
static final Value INSET = new Value("inset");
static final Value OUTSET = new Value("outset");
// Lists.
static final Value DISC = new Value("disc");
static final Value CIRCLE = new Value("circle");
static final Value SQUARE = new Value("square");
static final Value DECIMAL = new Value("decimal");
static final Value LOWER_ROMAN = new Value("lower-roman");
static final Value UPPER_ROMAN = new Value("upper-roman");
static final Value LOWER_ALPHA = new Value("lower-alpha");
static final Value UPPER_ALPHA = new Value("upper-alpha");
// background-repeat
static final Value BACKGROUND_NO_REPEAT = new Value("no-repeat");
static final Value BACKGROUND_REPEAT = new Value("repeat");
static final Value BACKGROUND_REPEAT_X = new Value("repeat-x");
static final Value BACKGROUND_REPEAT_Y = new Value("repeat-y");
// background-attachment
static final Value BACKGROUND_SCROLL = new Value("scroll");
static final Value BACKGROUND_FIXED = new Value("fixed");
private String name;
static final Value[] allValues = {
INHERITED, NONE, DOTTED, DASHED, SOLID, DOUBLE, GROOVE,
RIDGE, INSET, OUTSET, DISC, CIRCLE, SQUARE, DECIMAL,
LOWER_ROMAN, UPPER_ROMAN, LOWER_ALPHA, UPPER_ALPHA,
BACKGROUND_NO_REPEAT, BACKGROUND_REPEAT,
BACKGROUND_REPEAT_X, BACKGROUND_REPEAT_Y,
BACKGROUND_FIXED, BACKGROUND_FIXED
};
}
public CSS() {
baseFontSize = baseFontSizeIndex + 1;
// setup the css conversion table
valueConvertor = new Hashtable<Object, Object>();
valueConvertor.put(CSS.Attribute.FONT_SIZE, new FontSize());
valueConvertor.put(CSS.Attribute.FONT_FAMILY, new FontFamily());
valueConvertor.put(CSS.Attribute.FONT_WEIGHT, new FontWeight());
Object bs = new BorderStyle();
valueConvertor.put(CSS.Attribute.BORDER_TOP_STYLE, bs);
valueConvertor.put(CSS.Attribute.BORDER_RIGHT_STYLE, bs);
valueConvertor.put(CSS.Attribute.BORDER_BOTTOM_STYLE, bs);
valueConvertor.put(CSS.Attribute.BORDER_LEFT_STYLE, bs);
Object cv = new ColorValue();
valueConvertor.put(CSS.Attribute.COLOR, cv);
valueConvertor.put(CSS.Attribute.BACKGROUND_COLOR, cv);
valueConvertor.put(CSS.Attribute.BORDER_TOP_COLOR, cv);
valueConvertor.put(CSS.Attribute.BORDER_RIGHT_COLOR, cv);
valueConvertor.put(CSS.Attribute.BORDER_BOTTOM_COLOR, cv);
valueConvertor.put(CSS.Attribute.BORDER_LEFT_COLOR, cv);
Object lv = new LengthValue();
valueConvertor.put(CSS.Attribute.MARGIN_TOP, lv);
valueConvertor.put(CSS.Attribute.MARGIN_BOTTOM, lv);
valueConvertor.put(CSS.Attribute.MARGIN_LEFT, lv);
valueConvertor.put(CSS.Attribute.MARGIN_LEFT_LTR, lv);
valueConvertor.put(CSS.Attribute.MARGIN_LEFT_RTL, lv);
valueConvertor.put(CSS.Attribute.MARGIN_RIGHT, lv);
valueConvertor.put(CSS.Attribute.MARGIN_RIGHT_LTR, lv);
valueConvertor.put(CSS.Attribute.MARGIN_RIGHT_RTL, lv);
valueConvertor.put(CSS.Attribute.PADDING_TOP, lv);
valueConvertor.put(CSS.Attribute.PADDING_BOTTOM, lv);
valueConvertor.put(CSS.Attribute.PADDING_LEFT, lv);
valueConvertor.put(CSS.Attribute.PADDING_RIGHT, lv);
Object bv = new BorderWidthValue(null, 0);
valueConvertor.put(CSS.Attribute.BORDER_TOP_WIDTH, bv);
valueConvertor.put(CSS.Attribute.BORDER_BOTTOM_WIDTH, bv);
valueConvertor.put(CSS.Attribute.BORDER_LEFT_WIDTH, bv);
valueConvertor.put(CSS.Attribute.BORDER_RIGHT_WIDTH, bv);
Object nlv = new LengthValue(true);
valueConvertor.put(CSS.Attribute.TEXT_INDENT, nlv);
valueConvertor.put(CSS.Attribute.WIDTH, lv);
valueConvertor.put(CSS.Attribute.HEIGHT, lv);
valueConvertor.put(CSS.Attribute.BORDER_SPACING, lv);
Object sv = new StringValue();
valueConvertor.put(CSS.Attribute.FONT_STYLE, sv);
valueConvertor.put(CSS.Attribute.TEXT_DECORATION, sv);
valueConvertor.put(CSS.Attribute.TEXT_ALIGN, sv);
valueConvertor.put(CSS.Attribute.VERTICAL_ALIGN, sv);
Object valueMapper = new CssValueMapper();
valueConvertor.put(CSS.Attribute.LIST_STYLE_TYPE,
valueMapper);
valueConvertor.put(CSS.Attribute.BACKGROUND_IMAGE,
new BackgroundImage());
valueConvertor.put(CSS.Attribute.BACKGROUND_POSITION,
new BackgroundPosition());
valueConvertor.put(CSS.Attribute.BACKGROUND_REPEAT,
valueMapper);
valueConvertor.put(CSS.Attribute.BACKGROUND_ATTACHMENT,
valueMapper);
Object generic = new CssValue();
int n = CSS.Attribute.allAttributes.length;
for (int i = 0; i < n; i++) {
CSS.Attribute key = CSS.Attribute.allAttributes[i];
if (valueConvertor.get(key) == null) {
valueConvertor.put(key, generic);
}
}
}
Sets the base font size. sz
is a CSS value, and is
not necessarily the point size. Use getPointSize to determine the
point size corresponding to sz
.
/**
* Sets the base font size. <code>sz</code> is a CSS value, and is
* not necessarily the point size. Use getPointSize to determine the
* point size corresponding to <code>sz</code>.
*/
void setBaseFontSize(int sz) {
if (sz < 1)
baseFontSize = 0;
else if (sz > 7)
baseFontSize = 7;
else
baseFontSize = sz;
}
Sets the base font size from the passed in string.
/**
* Sets the base font size from the passed in string.
*/
void setBaseFontSize(String size) {
int relSize, absSize, diff;
if (size != null) {
if (size.startsWith("+")) {
relSize = Integer.valueOf(size.substring(1)).intValue();
setBaseFontSize(baseFontSize + relSize);
} else if (size.startsWith("-")) {
relSize = -Integer.valueOf(size.substring(1)).intValue();
setBaseFontSize(baseFontSize + relSize);
} else {
setBaseFontSize(Integer.valueOf(size).intValue());
}
}
}
Returns the base font size.
/**
* Returns the base font size.
*/
int getBaseFontSize() {
return baseFontSize;
}
Parses the CSS property key
with value
value
placing the result in att
.
/**
* Parses the CSS property <code>key</code> with value
* <code>value</code> placing the result in <code>att</code>.
*/
void addInternalCSSValue(MutableAttributeSet attr,
CSS.Attribute key, String value) {
if (key == CSS.Attribute.FONT) {
ShorthandFontParser.parseShorthandFont(this, value, attr);
}
else if (key == CSS.Attribute.BACKGROUND) {
ShorthandBackgroundParser.parseShorthandBackground
(this, value, attr);
}
else if (key == CSS.Attribute.MARGIN) {
ShorthandMarginParser.parseShorthandMargin(this, value, attr,
CSS.Attribute.ALL_MARGINS);
}
else if (key == CSS.Attribute.PADDING) {
ShorthandMarginParser.parseShorthandMargin(this, value, attr,
CSS.Attribute.ALL_PADDING);
}
else if (key == CSS.Attribute.BORDER_WIDTH) {
ShorthandMarginParser.parseShorthandMargin(this, value, attr,
CSS.Attribute.ALL_BORDER_WIDTHS);
}
else if (key == CSS.Attribute.BORDER_COLOR) {
ShorthandMarginParser.parseShorthandMargin(this, value, attr,
CSS.Attribute.ALL_BORDER_COLORS);
}
else if (key == CSS.Attribute.BORDER_STYLE) {
ShorthandMarginParser.parseShorthandMargin(this, value, attr,
CSS.Attribute.ALL_BORDER_STYLES);
}
else if ((key == CSS.Attribute.BORDER) ||
(key == CSS.Attribute.BORDER_TOP) ||
(key == CSS.Attribute.BORDER_RIGHT) ||
(key == CSS.Attribute.BORDER_BOTTOM) ||
(key == CSS.Attribute.BORDER_LEFT)) {
ShorthandBorderParser.parseShorthandBorder(attr, key, value);
}
else {
Object iValue = getInternalCSSValue(key, value);
if (iValue != null) {
attr.addAttribute(key, iValue);
}
}
}
Gets the internal CSS representation of value
which is
a CSS value of the CSS attribute named key
. The receiver
should not modify value
, and the first count
strings are valid.
/**
* Gets the internal CSS representation of <code>value</code> which is
* a CSS value of the CSS attribute named <code>key</code>. The receiver
* should not modify <code>value</code>, and the first <code>count</code>
* strings are valid.
*/
Object getInternalCSSValue(CSS.Attribute key, String value) {
CssValue conv = (CssValue) valueConvertor.get(key);
Object r = conv.parseCssValue(value);
return r != null ? r : conv.parseCssValue(key.getDefaultValue());
}
Maps from a StyleConstants to a CSS Attribute.
/**
* Maps from a StyleConstants to a CSS Attribute.
*/
Attribute styleConstantsKeyToCSSKey(StyleConstants sc) {
return styleConstantToCssMap.get(sc);
}
Maps from a StyleConstants value to a CSS value.
/**
* Maps from a StyleConstants value to a CSS value.
*/
Object styleConstantsValueToCSSValue(StyleConstants sc,
Object styleValue) {
Attribute cssKey = styleConstantsKeyToCSSKey(sc);
if (cssKey != null) {
CssValue conv = (CssValue)valueConvertor.get(cssKey);
return conv.fromStyleConstants(sc, styleValue);
}
return null;
}
Converts the passed in CSS value to a StyleConstants value.
key
identifies the CSS attribute being mapped.
/**
* Converts the passed in CSS value to a StyleConstants value.
* <code>key</code> identifies the CSS attribute being mapped.
*/
Object cssValueToStyleConstantsValue(StyleConstants key, Object value) {
if (value instanceof CssValue) {
return ((CssValue)value).toStyleConstants(key, null);
}
return null;
}
Returns the font for the values in the passed in AttributeSet.
It is assumed the keys will be CSS.Attribute keys.
sc
is the StyleContext that will be messaged to get
the font once the size, name and style have been determined.
/**
* Returns the font for the values in the passed in AttributeSet.
* It is assumed the keys will be CSS.Attribute keys.
* <code>sc</code> is the StyleContext that will be messaged to get
* the font once the size, name and style have been determined.
*/
Font getFont(StyleContext sc, AttributeSet a, int defaultSize, StyleSheet ss) {
ss = getStyleSheet(ss);
int size = getFontSize(a, defaultSize, ss);
/*
* If the vertical alignment is set to either superscirpt or
* subscript we reduce the font size by 2 points.
*/
StringValue vAlignV = (StringValue)a.getAttribute
(CSS.Attribute.VERTICAL_ALIGN);
if ((vAlignV != null)) {
String vAlign = vAlignV.toString();
if ((vAlign.indexOf("sup") >= 0) ||
(vAlign.indexOf("sub") >= 0)) {
size -= 2;
}
}
FontFamily familyValue = (FontFamily)a.getAttribute
(CSS.Attribute.FONT_FAMILY);
String family = (familyValue != null) ? familyValue.getValue() :
Font.SANS_SERIF;
int style = Font.PLAIN;
FontWeight weightValue = (FontWeight) a.getAttribute
(CSS.Attribute.FONT_WEIGHT);
if ((weightValue != null) && (weightValue.getValue() > 400)) {
style |= Font.BOLD;
}
Object fs = a.getAttribute(CSS.Attribute.FONT_STYLE);
if ((fs != null) && (fs.toString().indexOf("italic") >= 0)) {
style |= Font.ITALIC;
}
if (family.equalsIgnoreCase("monospace")) {
family = Font.MONOSPACED;
}
Font f = sc.getFont(family, style, size);
if (f == null
|| (f.getFamily().equals(Font.DIALOG)
&& ! family.equalsIgnoreCase(Font.DIALOG))) {
family = Font.SANS_SERIF;
f = sc.getFont(family, style, size);
}
return f;
}
static int getFontSize(AttributeSet attr, int defaultSize, StyleSheet ss) {
// PENDING(prinz) this is a 1.1 based implementation, need to also
// have a 1.2 version.
FontSize sizeValue = (FontSize)attr.getAttribute(CSS.Attribute.
FONT_SIZE);
return (sizeValue != null) ? sizeValue.getValue(attr, ss)
: defaultSize;
}
Takes a set of attributes and turn it into a color
specification. This might be used to specify things
like brighter, more hue, etc.
This will return null if there is no value for key
.
Params: - key – CSS.Attribute identifying where color is stored.
- a – the set of attributes
Returns: the color
/**
* Takes a set of attributes and turn it into a color
* specification. This might be used to specify things
* like brighter, more hue, etc.
* This will return null if there is no value for <code>key</code>.
*
* @param key CSS.Attribute identifying where color is stored.
* @param a the set of attributes
* @return the color
*/
Color getColor(AttributeSet a, CSS.Attribute key) {
ColorValue cv = (ColorValue) a.getAttribute(key);
if (cv != null) {
return cv.getValue();
}
return null;
}
Returns the size of a font from the passed in string.
Params: - size – CSS string describing font size
- baseFontSize – size to use for relative units.
/**
* Returns the size of a font from the passed in string.
*
* @param size CSS string describing font size
* @param baseFontSize size to use for relative units.
*/
float getPointSize(String size, StyleSheet ss) {
int relSize, absSize, diff, index;
ss = getStyleSheet(ss);
if (size != null) {
if (size.startsWith("+")) {
relSize = Integer.valueOf(size.substring(1)).intValue();
return getPointSize(baseFontSize + relSize, ss);
} else if (size.startsWith("-")) {
relSize = -Integer.valueOf(size.substring(1)).intValue();
return getPointSize(baseFontSize + relSize, ss);
} else {
absSize = Integer.valueOf(size).intValue();
return getPointSize(absSize, ss);
}
}
return 0;
}
Returns the length of the attribute in a
with
key key
.
/**
* Returns the length of the attribute in <code>a</code> with
* key <code>key</code>.
*/
float getLength(AttributeSet a, CSS.Attribute key, StyleSheet ss) {
ss = getStyleSheet(ss);
LengthValue lv = (LengthValue) a.getAttribute(key);
boolean isW3CLengthUnits = (ss == null) ? false : ss.isW3CLengthUnits();
float len = (lv != null) ? lv.getValue(isW3CLengthUnits) : 0;
return len;
}
Convert a set of HTML attributes to an equivalent
set of CSS attributes.
Params: - htmlAttrSet – AttributeSet containing the HTML attributes.
Returns: AttributeSet containing the corresponding CSS attributes.
The AttributeSet will be empty if there are no mapping
CSS attributes.
/**
* Convert a set of HTML attributes to an equivalent
* set of CSS attributes.
*
* @param htmlAttrSet AttributeSet containing the HTML attributes.
* @return AttributeSet containing the corresponding CSS attributes.
* The AttributeSet will be empty if there are no mapping
* CSS attributes.
*/
AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet) {
MutableAttributeSet cssAttrSet = new SimpleAttributeSet();
Element elem = (Element)htmlAttrSet;
HTML.Tag tag = getHTMLTag(htmlAttrSet);
if ((tag == HTML.Tag.TD) || (tag == HTML.Tag.TH)) {
// translate border width into the cells, if it has non-zero value.
AttributeSet tableAttr = elem.getParentElement().
getParentElement().getAttributes();
int borderWidth = getTableBorder(tableAttr);
if (borderWidth > 0) {
// If table contains the BORDER attribute cells should have border width equals 1
translateAttribute(HTML.Attribute.BORDER, "1", cssAttrSet);
}
String pad = (String)tableAttr.getAttribute(HTML.Attribute.CELLPADDING);
if (pad != null) {
LengthValue v =
(LengthValue)getInternalCSSValue(CSS.Attribute.PADDING_TOP, pad);
v.span = (v.span < 0) ? 0 : v.span;
cssAttrSet.addAttribute(CSS.Attribute.PADDING_TOP, v);
cssAttrSet.addAttribute(CSS.Attribute.PADDING_BOTTOM, v);
cssAttrSet.addAttribute(CSS.Attribute.PADDING_LEFT, v);
cssAttrSet.addAttribute(CSS.Attribute.PADDING_RIGHT, v);
}
}
if (elem.isLeaf()) {
translateEmbeddedAttributes(htmlAttrSet, cssAttrSet);
} else {
translateAttributes(tag, htmlAttrSet, cssAttrSet);
}
if (tag == HTML.Tag.CAPTION) {
/*
* Navigator uses ALIGN for caption placement and IE uses VALIGN.
*/
Object v = htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
if ((v != null) && (v.equals("top") || v.equals("bottom"))) {
cssAttrSet.addAttribute(CSS.Attribute.CAPTION_SIDE, v);
cssAttrSet.removeAttribute(CSS.Attribute.TEXT_ALIGN);
} else {
v = htmlAttrSet.getAttribute(HTML.Attribute.VALIGN);
if (v != null) {
cssAttrSet.addAttribute(CSS.Attribute.CAPTION_SIDE, v);
}
}
}
return cssAttrSet;
}
private static int getTableBorder(AttributeSet tableAttr) {
String borderValue = (String) tableAttr.getAttribute(HTML.Attribute.BORDER);
if (borderValue == HTML.NULL_ATTRIBUTE_VALUE || "".equals(borderValue)) {
// Some browsers accept <TABLE BORDER> and <TABLE BORDER=""> with the same semantics as BORDER=1
return 1;
}
try {
return Integer.parseInt(borderValue);
} catch (NumberFormatException e) {
return 0;
}
}
private static final Hashtable<String, Attribute> attributeMap = new Hashtable<String, Attribute>();
private static final Hashtable<String, Value> valueMap = new Hashtable<String, Value>();
The hashtable and the static initalization block below,
set up a mapping from well-known HTML attributes to
CSS attributes. For the most part, there is a 1-1 mapping
between the two. However in the case of certain HTML
attributes for example HTML.Attribute.VSPACE or
HTML.Attribute.HSPACE, end up mapping to two CSS.Attribute's.
Therefore, the value associated with each HTML.Attribute.
key ends up being an array of CSS.Attribute.* objects.
/**
* The hashtable and the static initalization block below,
* set up a mapping from well-known HTML attributes to
* CSS attributes. For the most part, there is a 1-1 mapping
* between the two. However in the case of certain HTML
* attributes for example HTML.Attribute.VSPACE or
* HTML.Attribute.HSPACE, end up mapping to two CSS.Attribute's.
* Therefore, the value associated with each HTML.Attribute.
* key ends up being an array of CSS.Attribute.* objects.
*/
private static final Hashtable<HTML.Attribute, CSS.Attribute[]> htmlAttrToCssAttrMap = new Hashtable<HTML.Attribute, CSS.Attribute[]>(20);
The hashtable and static initialization that follows sets
up a translation from StyleConstants (i.e. the well known
attributes) to the associated CSS attributes.
/**
* The hashtable and static initialization that follows sets
* up a translation from StyleConstants (i.e. the <em>well known</em>
* attributes) to the associated CSS attributes.
*/
private static final Hashtable<Object, Attribute> styleConstantToCssMap = new Hashtable<Object, Attribute>(17);
Maps from HTML value to a CSS value. Used in internal mapping. /** Maps from HTML value to a CSS value. Used in internal mapping. */
private static final Hashtable<String, CSS.Value> htmlValueToCssValueMap = new Hashtable<String, CSS.Value>(8);
Maps from CSS value (string) to internal value. /** Maps from CSS value (string) to internal value. */
private static final Hashtable<String, CSS.Value> cssValueToInternalValueMap = new Hashtable<String, CSS.Value>(13);
static {
// load the attribute map
for (int i = 0; i < Attribute.allAttributes.length; i++ ) {
attributeMap.put(Attribute.allAttributes[i].toString(),
Attribute.allAttributes[i]);
}
// load the value map
for (int i = 0; i < Value.allValues.length; i++ ) {
valueMap.put(Value.allValues[i].toString(),
Value.allValues[i]);
}
htmlAttrToCssAttrMap.put(HTML.Attribute.COLOR,
new CSS.Attribute[]{CSS.Attribute.COLOR});
htmlAttrToCssAttrMap.put(HTML.Attribute.TEXT,
new CSS.Attribute[]{CSS.Attribute.COLOR});
htmlAttrToCssAttrMap.put(HTML.Attribute.CLEAR,
new CSS.Attribute[]{CSS.Attribute.CLEAR});
htmlAttrToCssAttrMap.put(HTML.Attribute.BACKGROUND,
new CSS.Attribute[]{CSS.Attribute.BACKGROUND_IMAGE});
htmlAttrToCssAttrMap.put(HTML.Attribute.BGCOLOR,
new CSS.Attribute[]{CSS.Attribute.BACKGROUND_COLOR});
htmlAttrToCssAttrMap.put(HTML.Attribute.WIDTH,
new CSS.Attribute[]{CSS.Attribute.WIDTH});
htmlAttrToCssAttrMap.put(HTML.Attribute.HEIGHT,
new CSS.Attribute[]{CSS.Attribute.HEIGHT});
htmlAttrToCssAttrMap.put(HTML.Attribute.BORDER,
new CSS.Attribute[]{CSS.Attribute.BORDER_TOP_WIDTH, CSS.Attribute.BORDER_RIGHT_WIDTH, CSS.Attribute.BORDER_BOTTOM_WIDTH, CSS.Attribute.BORDER_LEFT_WIDTH});
htmlAttrToCssAttrMap.put(HTML.Attribute.CELLPADDING,
new CSS.Attribute[]{CSS.Attribute.PADDING});
htmlAttrToCssAttrMap.put(HTML.Attribute.CELLSPACING,
new CSS.Attribute[]{CSS.Attribute.BORDER_SPACING});
htmlAttrToCssAttrMap.put(HTML.Attribute.MARGINWIDTH,
new CSS.Attribute[]{CSS.Attribute.MARGIN_LEFT,
CSS.Attribute.MARGIN_RIGHT});
htmlAttrToCssAttrMap.put(HTML.Attribute.MARGINHEIGHT,
new CSS.Attribute[]{CSS.Attribute.MARGIN_TOP,
CSS.Attribute.MARGIN_BOTTOM});
htmlAttrToCssAttrMap.put(HTML.Attribute.HSPACE,
new CSS.Attribute[]{CSS.Attribute.PADDING_LEFT,
CSS.Attribute.PADDING_RIGHT});
htmlAttrToCssAttrMap.put(HTML.Attribute.VSPACE,
new CSS.Attribute[]{CSS.Attribute.PADDING_BOTTOM,
CSS.Attribute.PADDING_TOP});
htmlAttrToCssAttrMap.put(HTML.Attribute.FACE,
new CSS.Attribute[]{CSS.Attribute.FONT_FAMILY});
htmlAttrToCssAttrMap.put(HTML.Attribute.SIZE,
new CSS.Attribute[]{CSS.Attribute.FONT_SIZE});
htmlAttrToCssAttrMap.put(HTML.Attribute.VALIGN,
new CSS.Attribute[]{CSS.Attribute.VERTICAL_ALIGN});
htmlAttrToCssAttrMap.put(HTML.Attribute.ALIGN,
new CSS.Attribute[]{CSS.Attribute.VERTICAL_ALIGN,
CSS.Attribute.TEXT_ALIGN,
CSS.Attribute.FLOAT});
htmlAttrToCssAttrMap.put(HTML.Attribute.TYPE,
new CSS.Attribute[]{CSS.Attribute.LIST_STYLE_TYPE});
htmlAttrToCssAttrMap.put(HTML.Attribute.NOWRAP,
new CSS.Attribute[]{CSS.Attribute.WHITE_SPACE});
// initialize StyleConstants mapping
styleConstantToCssMap.put(StyleConstants.FontFamily,
CSS.Attribute.FONT_FAMILY);
styleConstantToCssMap.put(StyleConstants.FontSize,
CSS.Attribute.FONT_SIZE);
styleConstantToCssMap.put(StyleConstants.Bold,
CSS.Attribute.FONT_WEIGHT);
styleConstantToCssMap.put(StyleConstants.Italic,
CSS.Attribute.FONT_STYLE);
styleConstantToCssMap.put(StyleConstants.Underline,
CSS.Attribute.TEXT_DECORATION);
styleConstantToCssMap.put(StyleConstants.StrikeThrough,
CSS.Attribute.TEXT_DECORATION);
styleConstantToCssMap.put(StyleConstants.Superscript,
CSS.Attribute.VERTICAL_ALIGN);
styleConstantToCssMap.put(StyleConstants.Subscript,
CSS.Attribute.VERTICAL_ALIGN);
styleConstantToCssMap.put(StyleConstants.Foreground,
CSS.Attribute.COLOR);
styleConstantToCssMap.put(StyleConstants.Background,
CSS.Attribute.BACKGROUND_COLOR);
styleConstantToCssMap.put(StyleConstants.FirstLineIndent,
CSS.Attribute.TEXT_INDENT);
styleConstantToCssMap.put(StyleConstants.LeftIndent,
CSS.Attribute.MARGIN_LEFT);
styleConstantToCssMap.put(StyleConstants.RightIndent,
CSS.Attribute.MARGIN_RIGHT);
styleConstantToCssMap.put(StyleConstants.SpaceAbove,
CSS.Attribute.MARGIN_TOP);
styleConstantToCssMap.put(StyleConstants.SpaceBelow,
CSS.Attribute.MARGIN_BOTTOM);
styleConstantToCssMap.put(StyleConstants.Alignment,
CSS.Attribute.TEXT_ALIGN);
// HTML->CSS
htmlValueToCssValueMap.put("disc", CSS.Value.DISC);
htmlValueToCssValueMap.put("square", CSS.Value.SQUARE);
htmlValueToCssValueMap.put("circle", CSS.Value.CIRCLE);
htmlValueToCssValueMap.put("1", CSS.Value.DECIMAL);
htmlValueToCssValueMap.put("a", CSS.Value.LOWER_ALPHA);
htmlValueToCssValueMap.put("A", CSS.Value.UPPER_ALPHA);
htmlValueToCssValueMap.put("i", CSS.Value.LOWER_ROMAN);
htmlValueToCssValueMap.put("I", CSS.Value.UPPER_ROMAN);
// CSS-> internal CSS
cssValueToInternalValueMap.put("none", CSS.Value.NONE);
cssValueToInternalValueMap.put("disc", CSS.Value.DISC);
cssValueToInternalValueMap.put("square", CSS.Value.SQUARE);
cssValueToInternalValueMap.put("circle", CSS.Value.CIRCLE);
cssValueToInternalValueMap.put("decimal", CSS.Value.DECIMAL);
cssValueToInternalValueMap.put("lower-roman", CSS.Value.LOWER_ROMAN);
cssValueToInternalValueMap.put("upper-roman", CSS.Value.UPPER_ROMAN);
cssValueToInternalValueMap.put("lower-alpha", CSS.Value.LOWER_ALPHA);
cssValueToInternalValueMap.put("upper-alpha", CSS.Value.UPPER_ALPHA);
cssValueToInternalValueMap.put("repeat", CSS.Value.BACKGROUND_REPEAT);
cssValueToInternalValueMap.put("no-repeat",
CSS.Value.BACKGROUND_NO_REPEAT);
cssValueToInternalValueMap.put("repeat-x",
CSS.Value.BACKGROUND_REPEAT_X);
cssValueToInternalValueMap.put("repeat-y",
CSS.Value.BACKGROUND_REPEAT_Y);
cssValueToInternalValueMap.put("scroll",
CSS.Value.BACKGROUND_SCROLL);
cssValueToInternalValueMap.put("fixed",
CSS.Value.BACKGROUND_FIXED);
// Register all the CSS attribute keys for archival/unarchival
Object[] keys = CSS.Attribute.allAttributes;
try {
for (Object key : keys) {
StyleContext.registerStaticAttributeKey(key);
}
} catch (Throwable e) {
e.printStackTrace();
}
// Register all the CSS Values for archival/unarchival
keys = CSS.Value.allValues;
try {
for (Object key : keys) {
StyleContext.registerStaticAttributeKey(key);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
Return the set of all possible CSS attribute keys.
/**
* Return the set of all possible CSS attribute keys.
*/
public static Attribute[] getAllAttributeKeys() {
Attribute[] keys = new Attribute[Attribute.allAttributes.length];
System.arraycopy(Attribute.allAttributes, 0, keys, 0, Attribute.allAttributes.length);
return keys;
}
Translates a string to a CSS.Attribute
object.
This will return null
if there is no attribute
by the given name.
Params: - name – the name of the CSS attribute to fetch the
typesafe enumeration for
Returns: the CSS.Attribute
object,
or null
if the string
doesn't represent a valid attribute key
/**
* Translates a string to a <code>CSS.Attribute</code> object.
* This will return <code>null</code> if there is no attribute
* by the given name.
*
* @param name the name of the CSS attribute to fetch the
* typesafe enumeration for
* @return the <code>CSS.Attribute</code> object,
* or <code>null</code> if the string
* doesn't represent a valid attribute key
*/
public static final Attribute getAttribute(String name) {
return attributeMap.get(name);
}
Translates a string to a CSS.Value
object.
This will return null
if there is no value
by the given name.
Params: - name – the name of the CSS value to fetch the
typesafe enumeration for
Returns: the CSS.Value
object,
or null
if the string
doesn't represent a valid CSS value name; this does
not mean that it doesn't represent a valid CSS value
/**
* Translates a string to a <code>CSS.Value</code> object.
* This will return <code>null</code> if there is no value
* by the given name.
*
* @param name the name of the CSS value to fetch the
* typesafe enumeration for
* @return the <code>CSS.Value</code> object,
* or <code>null</code> if the string
* doesn't represent a valid CSS value name; this does
* not mean that it doesn't represent a valid CSS value
*/
static final Value getValue(String name) {
return valueMap.get(name);
}
//
// Conversion related methods/classes
//
Returns a URL for the given CSS url string. If relative,
base
is used as the parent. If a valid URL can not
be found, this will not throw a MalformedURLException, instead
null will be returned.
/**
* Returns a URL for the given CSS url string. If relative,
* <code>base</code> is used as the parent. If a valid URL can not
* be found, this will not throw a MalformedURLException, instead
* null will be returned.
*/
static URL getURL(URL base, String cssString) {
if (cssString == null) {
return null;
}
if (cssString.startsWith("url(") &&
cssString.endsWith(")")) {
cssString = cssString.substring(4, cssString.length() - 1);
}
// Absolute first
try {
URL url = new URL(cssString);
if (url != null) {
return url;
}
} catch (MalformedURLException mue) {
}
// Then relative
if (base != null) {
// Relative URL, try from base
try {
URL url = new URL(base, cssString);
return url;
}
catch (MalformedURLException muee) {
}
}
return null;
}
Converts a type Color to a hex string
in the format "#RRGGBB"
/**
* Converts a type Color to a hex string
* in the format "#RRGGBB"
*/
static String colorToHex(Color color) {
String colorstr = "#";
// Red
String str = Integer.toHexString(color.getRed());
if (str.length() > 2)
str = str.substring(0, 2);
else if (str.length() < 2)
colorstr += "0" + str;
else
colorstr += str;
// Green
str = Integer.toHexString(color.getGreen());
if (str.length() > 2)
str = str.substring(0, 2);
else if (str.length() < 2)
colorstr += "0" + str;
else
colorstr += str;
// Blue
str = Integer.toHexString(color.getBlue());
if (str.length() > 2)
str = str.substring(0, 2);
else if (str.length() < 2)
colorstr += "0" + str;
else
colorstr += str;
return colorstr;
}
Convert a "#FFFFFF" hex string to a Color.
If the color specification is bad, an attempt
will be made to fix it up.
/**
* Convert a "#FFFFFF" hex string to a Color.
* If the color specification is bad, an attempt
* will be made to fix it up.
*/
static final Color hexToColor(String value) {
String digits;
int n = value.length();
if (value.startsWith("#")) {
digits = value.substring(1, Math.min(value.length(), 7));
} else {
digits = value;
}
String hstr = "0x" + digits;
Color c;
try {
c = Color.decode(hstr);
} catch (NumberFormatException nfe) {
c = null;
}
return c;
}
Convert a color string such as "RED" or "#NNNNNN" or "rgb(r, g, b)"
to a Color.
/**
* Convert a color string such as "RED" or "#NNNNNN" or "rgb(r, g, b)"
* to a Color.
*/
static Color stringToColor(String str) {
Color color;
if (str == null) {
return null;
}
if (str.length() == 0)
color = Color.black;
else if (str.startsWith("rgb(")) {
color = parseRGB(str);
}
else if (str.charAt(0) == '#')
color = hexToColor(str);
else if (str.equalsIgnoreCase("Black"))
color = hexToColor("#000000");
else if(str.equalsIgnoreCase("Silver"))
color = hexToColor("#C0C0C0");
else if(str.equalsIgnoreCase("Gray"))
color = hexToColor("#808080");
else if(str.equalsIgnoreCase("White"))
color = hexToColor("#FFFFFF");
else if(str.equalsIgnoreCase("Maroon"))
color = hexToColor("#800000");
else if(str.equalsIgnoreCase("Red"))
color = hexToColor("#FF0000");
else if(str.equalsIgnoreCase("Purple"))
color = hexToColor("#800080");
else if(str.equalsIgnoreCase("Fuchsia"))
color = hexToColor("#FF00FF");
else if(str.equalsIgnoreCase("Green"))
color = hexToColor("#008000");
else if(str.equalsIgnoreCase("Lime"))
color = hexToColor("#00FF00");
else if(str.equalsIgnoreCase("Olive"))
color = hexToColor("#808000");
else if(str.equalsIgnoreCase("Yellow"))
color = hexToColor("#FFFF00");
else if(str.equalsIgnoreCase("Navy"))
color = hexToColor("#000080");
else if(str.equalsIgnoreCase("Blue"))
color = hexToColor("#0000FF");
else if(str.equalsIgnoreCase("Teal"))
color = hexToColor("#008080");
else if(str.equalsIgnoreCase("Aqua"))
color = hexToColor("#00FFFF");
else if(str.equalsIgnoreCase("Orange"))
color = hexToColor("#FF8000");
else
color = hexToColor(str); // sometimes get specified without leading #
return color;
}
Parses a String in the format rgb(r, g, b)
where
each of the Color components is either an integer, or a floating number
with a % after indicating a percentage value of 255. Values are
constrained to fit with 0-255. The resulting Color is returned.
/**
* Parses a String in the format <code>rgb(r, g, b)</code> where
* each of the Color components is either an integer, or a floating number
* with a % after indicating a percentage value of 255. Values are
* constrained to fit with 0-255. The resulting Color is returned.
*/
private static Color parseRGB(String string) {
// Find the next numeric char
int[] index = new int[1];
index[0] = 4;
int red = getColorComponent(string, index);
int green = getColorComponent(string, index);
int blue = getColorComponent(string, index);
return new Color(red, green, blue);
}
Returns the next integer value from string
starting
at index[0]
. The value can either can an integer, or
a percentage (floating number ending with %), in which case it is
multiplied by 255.
/**
* Returns the next integer value from <code>string</code> starting
* at <code>index[0]</code>. The value can either can an integer, or
* a percentage (floating number ending with %), in which case it is
* multiplied by 255.
*/
private static int getColorComponent(String string, int[] index) {
int length = string.length();
char aChar;
// Skip non-decimal chars
while(index[0] < length && (aChar = string.charAt(index[0])) != '-' &&
!Character.isDigit(aChar) && aChar != '.') {
index[0]++;
}
int start = index[0];
if (start < length && string.charAt(index[0]) == '-') {
index[0]++;
}
while(index[0] < length &&
Character.isDigit(string.charAt(index[0]))) {
index[0]++;
}
if (index[0] < length && string.charAt(index[0]) == '.') {
// Decimal value
index[0]++;
while(index[0] < length &&
Character.isDigit(string.charAt(index[0]))) {
index[0]++;
}
}
if (start != index[0]) {
try {
float value = Float.parseFloat(string.substring
(start, index[0]));
if (index[0] < length && string.charAt(index[0]) == '%') {
index[0]++;
value = value * 255f / 100f;
}
return Math.min(255, Math.max(0, (int)value));
} catch (NumberFormatException nfe) {
// Treat as 0
}
}
return 0;
}
static int getIndexOfSize(float pt, int[] sizeMap) {
for (int i = 0; i < sizeMap.length; i ++ )
if (pt <= sizeMap[i])
return i + 1;
return sizeMap.length;
}
static int getIndexOfSize(float pt, StyleSheet ss) {
int[] sizeMap = (ss != null) ? ss.getSizeMap() :
StyleSheet.sizeMapDefault;
return getIndexOfSize(pt, sizeMap);
}
Returns: an array of all the strings in value
that are separated by whitespace.
/**
* @return an array of all the strings in <code>value</code>
* that are separated by whitespace.
*/
static String[] parseStrings(String value) {
int current, last;
int length = (value == null) ? 0 : value.length();
Vector<String> temp = new Vector<String>(4);
current = 0;
while (current < length) {
// Skip ws
while (current < length && Character.isWhitespace
(value.charAt(current))) {
current++;
}
last = current;
while (current < length && !Character.isWhitespace
(value.charAt(current))) {
current++;
}
if (last != current) {
temp.addElement(value.substring(last, current));
}
current++;
}
String[] retValue = new String[temp.size()];
temp.copyInto(retValue);
return retValue;
}
Return the point size, given a size index. Legal HTML index sizes
are 1-7.
/**
* Return the point size, given a size index. Legal HTML index sizes
* are 1-7.
*/
float getPointSize(int index, StyleSheet ss) {
ss = getStyleSheet(ss);
int[] sizeMap = (ss != null) ? ss.getSizeMap() :
StyleSheet.sizeMapDefault;
--index;
if (index < 0)
return sizeMap[0];
else if (index > sizeMap.length - 1)
return sizeMap[sizeMap.length - 1];
else
return sizeMap[index];
}
private void translateEmbeddedAttributes(AttributeSet htmlAttrSet,
MutableAttributeSet cssAttrSet) {
Enumeration keys = htmlAttrSet.getAttributeNames();
if (htmlAttrSet.getAttribute(StyleConstants.NameAttribute) ==
HTML.Tag.HR) {
// HR needs special handling due to us treating it as a leaf.
translateAttributes(HTML.Tag.HR, htmlAttrSet, cssAttrSet);
}
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
if (key instanceof HTML.Tag) {
HTML.Tag tag = (HTML.Tag)key;
Object o = htmlAttrSet.getAttribute(tag);
if (o != null && o instanceof AttributeSet) {
translateAttributes(tag, (AttributeSet)o, cssAttrSet);
}
} else if (key instanceof CSS.Attribute) {
cssAttrSet.addAttribute(key, htmlAttrSet.getAttribute(key));
}
}
}
private void translateAttributes(HTML.Tag tag,
AttributeSet htmlAttrSet,
MutableAttributeSet cssAttrSet) {
Enumeration names = htmlAttrSet.getAttributeNames();
while (names.hasMoreElements()) {
Object name = names.nextElement();
if (name instanceof HTML.Attribute) {
HTML.Attribute key = (HTML.Attribute)name;
/*
* HTML.Attribute.ALIGN needs special processing.
* It can map to to 1 of many(3) possible CSS attributes
* depending on the nature of the tag the attribute is
* part off and depending on the value of the attribute.
*/
if (key == HTML.Attribute.ALIGN) {
String htmlAttrValue = (String)htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
if (htmlAttrValue != null) {
CSS.Attribute cssAttr = getCssAlignAttribute(tag, htmlAttrSet);
if (cssAttr != null) {
Object o = getCssValue(cssAttr, htmlAttrValue);
if (o != null) {
cssAttrSet.addAttribute(cssAttr, o);
}
}
}
} else {
if (key == HTML.Attribute.SIZE && !isHTMLFontTag(tag)) {
/*
* The html size attribute has a mapping in the CSS world only
* if it is par of a font or base font tag.
*/
} else if (tag == HTML.Tag.TABLE && key == HTML.Attribute.BORDER) {
int borderWidth = getTableBorder(htmlAttrSet);
if (borderWidth > 0) {
translateAttribute(HTML.Attribute.BORDER, Integer.toString(borderWidth), cssAttrSet);
}
} else {
translateAttribute(key, (String) htmlAttrSet.getAttribute(key), cssAttrSet);
}
}
} else if (name instanceof CSS.Attribute) {
cssAttrSet.addAttribute(name, htmlAttrSet.getAttribute(name));
}
}
}
private void translateAttribute(HTML.Attribute key,
String htmlAttrValue,
MutableAttributeSet cssAttrSet) {
/*
* In the case of all remaining HTML.Attribute's they
* map to 1 or more CCS.Attribute.
*/
CSS.Attribute[] cssAttrList = getCssAttribute(key);
if (cssAttrList == null || htmlAttrValue == null) {
return;
}
for (Attribute cssAttr : cssAttrList) {
Object o = getCssValue(cssAttr, htmlAttrValue);
if (o != null) {
cssAttrSet.addAttribute(cssAttr , o);
}
}
}
Given a CSS.Attribute object and its corresponding HTML.Attribute's
value, this method returns a CssValue object to associate with the
CSS attribute.
Params: - the – CSS.Attribute
- a – String containing the value associated HTML.Attribtue.
/**
* Given a CSS.Attribute object and its corresponding HTML.Attribute's
* value, this method returns a CssValue object to associate with the
* CSS attribute.
*
* @param the CSS.Attribute
* @param a String containing the value associated HTML.Attribtue.
*/
Object getCssValue(CSS.Attribute cssAttr, String htmlAttrValue) {
CssValue value = (CssValue)valueConvertor.get(cssAttr);
Object o = value.parseHtmlValue(htmlAttrValue);
return o;
}
Maps an HTML.Attribute object to its appropriate CSS.Attributes.
@param HTML.Attribute Returns: CSS.Attribute[]
/**
* Maps an HTML.Attribute object to its appropriate CSS.Attributes.
*
* @param HTML.Attribute
* @return CSS.Attribute[]
*/
private CSS.Attribute[] getCssAttribute(HTML.Attribute hAttr) {
return htmlAttrToCssAttrMap.get(hAttr);
}
Maps HTML.Attribute.ALIGN to either:
CSS.Attribute.TEXT_ALIGN
CSS.Attribute.FLOAT
CSS.Attribute.VERTICAL_ALIGN
based on the tag associated with the attribute and the
value of the attribute.
Params: - AttributeSet – containing HTML attributes.
Returns: CSS.Attribute mapping for HTML.Attribute.ALIGN.
/**
* Maps HTML.Attribute.ALIGN to either:
* CSS.Attribute.TEXT_ALIGN
* CSS.Attribute.FLOAT
* CSS.Attribute.VERTICAL_ALIGN
* based on the tag associated with the attribute and the
* value of the attribute.
*
* @param AttributeSet containing HTML attributes.
* @return CSS.Attribute mapping for HTML.Attribute.ALIGN.
*/
private CSS.Attribute getCssAlignAttribute(HTML.Tag tag,
AttributeSet htmlAttrSet) {
return CSS.Attribute.TEXT_ALIGN;
/*
String htmlAttrValue = (String)htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
CSS.Attribute cssAttr = CSS.Attribute.TEXT_ALIGN;
if (htmlAttrValue != null && htmlAttrSet instanceof Element) {
Element elem = (Element)htmlAttrSet;
if (!elem.isLeaf() && tag.isBlock() && validTextAlignValue(htmlAttrValue)) {
return CSS.Attribute.TEXT_ALIGN;
} else if (isFloater(htmlAttrValue)) {
return CSS.Attribute.FLOAT;
} else if (elem.isLeaf()) {
return CSS.Attribute.VERTICAL_ALIGN;
}
}
return null;
*/
}
Fetches the tag associated with the HTML AttributeSet.
Params: - AttributeSet – containing the HTML attributes.
Returns: HTML.Tag
/**
* Fetches the tag associated with the HTML AttributeSet.
*
* @param AttributeSet containing the HTML attributes.
* @return HTML.Tag
*/
private HTML.Tag getHTMLTag(AttributeSet htmlAttrSet) {
Object o = htmlAttrSet.getAttribute(StyleConstants.NameAttribute);
if (o instanceof HTML.Tag) {
HTML.Tag tag = (HTML.Tag) o;
return tag;
}
return null;
}
private boolean isHTMLFontTag(HTML.Tag tag) {
return (tag != null && ((tag == HTML.Tag.FONT) || (tag == HTML.Tag.BASEFONT)));
}
private boolean isFloater(String alignValue) {
return (alignValue.equals("left") || alignValue.equals("right"));
}
private boolean validTextAlignValue(String alignValue) {
return (isFloater(alignValue) || alignValue.equals("center"));
}
Base class to CSS values in the attribute sets. This
is intended to act as a convertor to/from other attribute
formats.
The CSS parser uses the parseCssValue method to convert
a string to whatever format is appropriate a given key
(i.e. these convertors are stored in a map using the
CSS.Attribute as a key and the CssValue as the value).
The HTML to CSS conversion process first converts the
HTML.Attribute to a CSS.Attribute, and then calls
the parseHtmlValue method on the value of the HTML
attribute to produce the corresponding CSS value.
The StyleConstants to CSS conversion process first
converts the StyleConstants attribute to a
CSS.Attribute, and then calls the fromStyleConstants
method to convert the StyleConstants value to a
CSS value.
The CSS to StyleConstants conversion process first
converts the StyleConstants attribute to a
CSS.Attribute, and then calls the toStyleConstants
method to convert the CSS value to a StyleConstants
value.
/**
* Base class to CSS values in the attribute sets. This
* is intended to act as a convertor to/from other attribute
* formats.
* <p>
* The CSS parser uses the parseCssValue method to convert
* a string to whatever format is appropriate a given key
* (i.e. these convertors are stored in a map using the
* CSS.Attribute as a key and the CssValue as the value).
* <p>
* The HTML to CSS conversion process first converts the
* HTML.Attribute to a CSS.Attribute, and then calls
* the parseHtmlValue method on the value of the HTML
* attribute to produce the corresponding CSS value.
* <p>
* The StyleConstants to CSS conversion process first
* converts the StyleConstants attribute to a
* CSS.Attribute, and then calls the fromStyleConstants
* method to convert the StyleConstants value to a
* CSS value.
* <p>
* The CSS to StyleConstants conversion process first
* converts the StyleConstants attribute to a
* CSS.Attribute, and then calls the toStyleConstants
* method to convert the CSS value to a StyleConstants
* value.
*/
static class CssValue implements Serializable {
Convert a CSS value string to the internal format
(for fast processing) used in the attribute sets.
The fallback storage for any value that we don't
have a special binary format for is a String.
/**
* Convert a CSS value string to the internal format
* (for fast processing) used in the attribute sets.
* The fallback storage for any value that we don't
* have a special binary format for is a String.
*/
Object parseCssValue(String value) {
return value;
}
Convert an HTML attribute value to a CSS attribute
value. If there is no conversion, return null.
This is implemented to simply forward to the CSS
parsing by default (since some of the attribute
values are the same). If the attribute value
isn't recognized as a CSS value it is generally
returned as null.
/**
* Convert an HTML attribute value to a CSS attribute
* value. If there is no conversion, return null.
* This is implemented to simply forward to the CSS
* parsing by default (since some of the attribute
* values are the same). If the attribute value
* isn't recognized as a CSS value it is generally
* returned as null.
*/
Object parseHtmlValue(String value) {
return parseCssValue(value);
}
Converts a StyleConstants
attribute value to
a CSS attribute value. If there is no conversion,
returns null
. By default, there is no conversion.
Params: - key – the
StyleConstants
attribute - value – the value of a
StyleConstants
attribute to be converted
Returns: the CSS value that represents the
StyleConstants
value
/**
* Converts a <code>StyleConstants</code> attribute value to
* a CSS attribute value. If there is no conversion,
* returns <code>null</code>. By default, there is no conversion.
*
* @param key the <code>StyleConstants</code> attribute
* @param value the value of a <code>StyleConstants</code>
* attribute to be converted
* @return the CSS value that represents the
* <code>StyleConstants</code> value
*/
Object fromStyleConstants(StyleConstants key, Object value) {
return null;
}
Converts a CSS attribute value to a
StyleConstants
value. If there is no conversion, returns
null
.
By default, there is no conversion.
Params: - key – the
StyleConstants
attribute - v – the view containing
AttributeSet
Returns: the StyleConstants
attribute value that
represents the CSS attribute value
/**
* Converts a CSS attribute value to a
* <code>StyleConstants</code>
* value. If there is no conversion, returns
* <code>null</code>.
* By default, there is no conversion.
*
* @param key the <code>StyleConstants</code> attribute
* @param v the view containing <code>AttributeSet</code>
* @return the <code>StyleConstants</code> attribute value that
* represents the CSS attribute value
*/
Object toStyleConstants(StyleConstants key, View v) {
return null;
}
Return the CSS format of the value
/**
* Return the CSS format of the value
*/
public String toString() {
return svalue;
}
The value as a string... before conversion to a
binary format.
/**
* The value as a string... before conversion to a
* binary format.
*/
String svalue;
}
By default CSS attributes are represented as simple
strings. They also have no conversion to/from
StyleConstants by default. This class represents the
value as a string (via the superclass), but
provides StyleConstants conversion support for the
CSS attributes that are held as strings.
/**
* By default CSS attributes are represented as simple
* strings. They also have no conversion to/from
* StyleConstants by default. This class represents the
* value as a string (via the superclass), but
* provides StyleConstants conversion support for the
* CSS attributes that are held as strings.
*/
static class StringValue extends CssValue {
Convert a CSS value string to the internal format
(for fast processing) used in the attribute sets.
This produces a StringValue, so that it can be
used to convert from CSS to StyleConstants values.
/**
* Convert a CSS value string to the internal format
* (for fast processing) used in the attribute sets.
* This produces a StringValue, so that it can be
* used to convert from CSS to StyleConstants values.
*/
Object parseCssValue(String value) {
StringValue sv = new StringValue();
sv.svalue = value;
return sv;
}
Converts a StyleConstants
attribute value to
a CSS attribute value. If there is no conversion
returns null
.
Params: - key – the
StyleConstants
attribute - value – the value of a
StyleConstants
attribute to be converted
Returns: the CSS value that represents the
StyleConstants
value
/**
* Converts a <code>StyleConstants</code> attribute value to
* a CSS attribute value. If there is no conversion
* returns <code>null</code>.
*
* @param key the <code>StyleConstants</code> attribute
* @param value the value of a <code>StyleConstants</code>
* attribute to be converted
* @return the CSS value that represents the
* <code>StyleConstants</code> value
*/
Object fromStyleConstants(StyleConstants key, Object value) {
if (key == StyleConstants.Italic) {
if (value.equals(Boolean.TRUE)) {
return parseCssValue("italic");
}
return parseCssValue("");
} else if (key == StyleConstants.Underline) {
if (value.equals(Boolean.TRUE)) {
return parseCssValue("underline");
}
return parseCssValue("");
} else if (key == StyleConstants.Alignment) {
int align = ((Integer)value).intValue();
String ta;
switch(align) {
case StyleConstants.ALIGN_LEFT:
ta = "left";
break;
case StyleConstants.ALIGN_RIGHT:
ta = "right";
break;
case StyleConstants.ALIGN_CENTER:
ta = "center";
break;
case StyleConstants.ALIGN_JUSTIFIED:
ta = "justify";
break;
default:
ta = "left";
}
return parseCssValue(ta);
} else if (key == StyleConstants.StrikeThrough) {
if (value.equals(Boolean.TRUE)) {
return parseCssValue("line-through");
}
return parseCssValue("");
} else if (key == StyleConstants.Superscript) {
if (value.equals(Boolean.TRUE)) {
return parseCssValue("super");
}
return parseCssValue("");
} else if (key == StyleConstants.Subscript) {
if (value.equals(Boolean.TRUE)) {
return parseCssValue("sub");
}
return parseCssValue("");
}
return null;
}
Converts a CSS attribute value to a
StyleConstants
value.
If there is no conversion, returns null
.
By default, there is no conversion.
Params: - key – the
StyleConstants
attribute
Returns: the StyleConstants
attribute value that
represents the CSS attribute value
/**
* Converts a CSS attribute value to a
* <code>StyleConstants</code> value.
* If there is no conversion, returns <code>null</code>.
* By default, there is no conversion.
*
* @param key the <code>StyleConstants</code> attribute
* @return the <code>StyleConstants</code> attribute value that
* represents the CSS attribute value
*/
Object toStyleConstants(StyleConstants key, View v) {
if (key == StyleConstants.Italic) {
if (svalue.indexOf("italic") >= 0) {
return Boolean.TRUE;
}
return Boolean.FALSE;
} else if (key == StyleConstants.Underline) {
if (svalue.indexOf("underline") >= 0) {
return Boolean.TRUE;
}
return Boolean.FALSE;
} else if (key == StyleConstants.Alignment) {
if (svalue.equals("right")) {
return new Integer(StyleConstants.ALIGN_RIGHT);
} else if (svalue.equals("center")) {
return new Integer(StyleConstants.ALIGN_CENTER);
} else if (svalue.equals("justify")) {
return new Integer(StyleConstants.ALIGN_JUSTIFIED);
}
return new Integer(StyleConstants.ALIGN_LEFT);
} else if (key == StyleConstants.StrikeThrough) {
if (svalue.indexOf("line-through") >= 0) {
return Boolean.TRUE;
}
return Boolean.FALSE;
} else if (key == StyleConstants.Superscript) {
if (svalue.indexOf("super") >= 0) {
return Boolean.TRUE;
}
return Boolean.FALSE;
} else if (key == StyleConstants.Subscript) {
if (svalue.indexOf("sub") >= 0) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
return null;
}
// Used by ViewAttributeSet
boolean isItalic() {
return (svalue.indexOf("italic") != -1);
}
boolean isStrike() {
return (svalue.indexOf("line-through") != -1);
}
boolean isUnderline() {
return (svalue.indexOf("underline") != -1);
}
boolean isSub() {
return (svalue.indexOf("sub") != -1);
}
boolean isSup() {
return (svalue.indexOf("sup") != -1);
}
}
Represents a value for the CSS.FONT_SIZE attribute.
The binary format of the value can be one of several
types. If the type is Float,
the value is specified in terms of point or
percentage, depending upon the ending of the
associated string.
If the type is Integer, the value is specified
in terms of a size index.
/**
* Represents a value for the CSS.FONT_SIZE attribute.
* The binary format of the value can be one of several
* types. If the type is Float,
* the value is specified in terms of point or
* percentage, depending upon the ending of the
* associated string.
* If the type is Integer, the value is specified
* in terms of a size index.
*/
class FontSize extends CssValue {
Returns the size in points. This is ultimately
what we need for the purpose of creating/fetching
a Font object.
Params: - a – the attribute set the value is being
requested from. We may need to walk up the
resolve hierarchy if it's relative.
/**
* Returns the size in points. This is ultimately
* what we need for the purpose of creating/fetching
* a Font object.
*
* @param a the attribute set the value is being
* requested from. We may need to walk up the
* resolve hierarchy if it's relative.
*/
int getValue(AttributeSet a, StyleSheet ss) {
ss = getStyleSheet(ss);
if (index) {
// it's an index, translate from size table
return Math.round(getPointSize((int) value, ss));
}
else if (lu == null) {
return Math.round(value);
}
else {
if (lu.type == 0) {
boolean isW3CLengthUnits = (ss == null) ? false : ss.isW3CLengthUnits();
return Math.round(lu.getValue(isW3CLengthUnits));
}
if (a != null) {
AttributeSet resolveParent = a.getResolveParent();
if (resolveParent != null) {
int pValue = StyleConstants.getFontSize(resolveParent);
float retValue;
if (lu.type == 1 || lu.type == 3) {
retValue = lu.value * (float)pValue;
}
else {
retValue = lu.value + (float)pValue;
}
return Math.round(retValue);
}
}
// a is null, or no resolve parent.
return 12;
}
}
Object parseCssValue(String value) {
FontSize fs = new FontSize();
fs.svalue = value;
try {
if (value.equals("xx-small")) {
fs.value = 1;
fs.index = true;
} else if (value.equals("x-small")) {
fs.value = 2;
fs.index = true;
} else if (value.equals("small")) {
fs.value = 3;
fs.index = true;
} else if (value.equals("medium")) {
fs.value = 4;
fs.index = true;
} else if (value.equals("large")) {
fs.value = 5;
fs.index = true;
} else if (value.equals("x-large")) {
fs.value = 6;
fs.index = true;
} else if (value.equals("xx-large")) {
fs.value = 7;
fs.index = true;
} else {
fs.lu = new LengthUnit(value, (short)1, 1f);
}
// relative sizes, larger | smaller (adjust from parent by
// 1.5 pixels)
// em, ex refer to parent sizes
// lengths: pt, mm, cm, pc, in, px
// em (font height 3em would be 3 times font height)
// ex (height of X)
// lengths are (+/-) followed by a number and two letter
// unit identifier
} catch (NumberFormatException nfe) {
fs = null;
}
return fs;
}
Object parseHtmlValue(String value) {
if ((value == null) || (value.length() == 0)) {
return null;
}
FontSize fs = new FontSize();
fs.svalue = value;
try {
/*
* relative sizes in the size attribute are relative
* to the <basefont>'s size.
*/
int baseFontSize = getBaseFontSize();
if (value.charAt(0) == '+') {
int relSize = Integer.valueOf(value.substring(1)).intValue();
fs.value = baseFontSize + relSize;
fs.index = true;
} else if (value.charAt(0) == '-') {
int relSize = -Integer.valueOf(value.substring(1)).intValue();
fs.value = baseFontSize + relSize;
fs.index = true;
} else {
fs.value = Integer.parseInt(value);
if (fs.value > 7) {
fs.value = 7;
} else if (fs.value < 0) {
fs.value = 0;
}
fs.index = true;
}
} catch (NumberFormatException nfe) {
fs = null;
}
return fs;
}
Converts a StyleConstants
attribute value to
a CSS attribute value. If there is no conversion
returns null
. By default, there is no conversion.
Params: - key – the
StyleConstants
attribute - value – the value of a
StyleConstants
attribute to be converted
Returns: the CSS value that represents the
StyleConstants
value
/**
* Converts a <code>StyleConstants</code> attribute value to
* a CSS attribute value. If there is no conversion
* returns <code>null</code>. By default, there is no conversion.
*
* @param key the <code>StyleConstants</code> attribute
* @param value the value of a <code>StyleConstants</code>
* attribute to be converted
* @return the CSS value that represents the
* <code>StyleConstants</code> value
*/
Object fromStyleConstants(StyleConstants key, Object value) {
if (value instanceof Number) {
FontSize fs = new FontSize();
fs.value = getIndexOfSize(((Number)value).floatValue(), StyleSheet.sizeMapDefault);
fs.svalue = Integer.toString((int)fs.value);
fs.index = true;
return fs;
}
return parseCssValue(value.toString());
}
Converts a CSS attribute value to a StyleConstants
value. If there is no conversion, returns null
.
By default, there is no conversion.
Params: - key – the
StyleConstants
attribute
Returns: the StyleConstants
attribute value that
represents the CSS attribute value
/**
* Converts a CSS attribute value to a <code>StyleConstants</code>
* value. If there is no conversion, returns <code>null</code>.
* By default, there is no conversion.
*
* @param key the <code>StyleConstants</code> attribute
* @return the <code>StyleConstants</code> attribute value that
* represents the CSS attribute value
*/
Object toStyleConstants(StyleConstants key, View v) {
if (v != null) {
return Integer.valueOf(getValue(v.getAttributes(), null));
}
return Integer.valueOf(getValue(null, null));
}
float value;
boolean index;
LengthUnit lu;
}
static class FontFamily extends CssValue {
Returns the font family to use.
/**
* Returns the font family to use.
*/
String getValue() {
return family;
}
Object parseCssValue(String value) {
int cIndex = value.indexOf(',');
FontFamily ff = new FontFamily();
ff.svalue = value;
ff.family = null;
if (cIndex == -1) {
setFontName(ff, value);
}
else {
boolean done = false;
int lastIndex;
int length = value.length();
cIndex = 0;
while (!done) {
// skip ws.
while (cIndex < length &&
Character.isWhitespace(value.charAt(cIndex)))
cIndex++;
// Find next ','
lastIndex = cIndex;
cIndex = value.indexOf(',', cIndex);
if (cIndex == -1) {
cIndex = length;
}
if (lastIndex < length) {
if (lastIndex != cIndex) {
int lastCharIndex = cIndex;
if (cIndex > 0 && value.charAt(cIndex - 1) == ' '){
lastCharIndex--;
}
setFontName(ff, value.substring
(lastIndex, lastCharIndex));
done = (ff.family != null);
}
cIndex++;
}
else {
done = true;
}
}
}
if (ff.family == null) {
ff.family = Font.SANS_SERIF;
}
return ff;
}
private void setFontName(FontFamily ff, String fontName) {
ff.family = fontName;
}
Object parseHtmlValue(String value) {
// TBD
return parseCssValue(value);
}
Converts a StyleConstants
attribute value to
a CSS attribute value. If there is no conversion
returns null
. By default, there is no conversion.
Params: - key – the
StyleConstants
attribute - value – the value of a
StyleConstants
attribute to be converted
Returns: the CSS value that represents the
StyleConstants
value
/**
* Converts a <code>StyleConstants</code> attribute value to
* a CSS attribute value. If there is no conversion
* returns <code>null</code>. By default, there is no conversion.
*
* @param key the <code>StyleConstants</code> attribute
* @param value the value of a <code>StyleConstants</code>
* attribute to be converted
* @return the CSS value that represents the
* <code>StyleConstants</code> value
*/
Object fromStyleConstants(StyleConstants key, Object value) {
return parseCssValue(value.toString());
}
Converts a CSS attribute value to a StyleConstants
value. If there is no conversion, returns null
.
By default, there is no conversion.
Params: - key – the
StyleConstants
attribute
Returns: the StyleConstants
attribute value that
represents the CSS attribute value
/**
* Converts a CSS attribute value to a <code>StyleConstants</code>
* value. If there is no conversion, returns <code>null</code>.
* By default, there is no conversion.
*
* @param key the <code>StyleConstants</code> attribute
* @return the <code>StyleConstants</code> attribute value that
* represents the CSS attribute value
*/
Object toStyleConstants(StyleConstants key, View v) {
return family;
}
String family;
}
static class FontWeight extends CssValue {
int getValue() {
return weight;
}
Object parseCssValue(String value) {
FontWeight fw = new FontWeight();
fw.svalue = value;
if (value.equals("bold")) {
fw.weight = 700;
} else if (value.equals("normal")) {
fw.weight = 400;
} else {
// PENDING(prinz) add support for relative values
try {
fw.weight = Integer.parseInt(value);
} catch (NumberFormatException nfe) {
fw = null;
}
}
return fw;
}
Converts a StyleConstants
attribute value to
a CSS attribute value. If there is no conversion
returns null
. By default, there is no conversion.
Params: - key – the
StyleConstants
attribute - value – the value of a
StyleConstants
attribute to be converted
Returns: the CSS value that represents the
StyleConstants
value
/**
* Converts a <code>StyleConstants</code> attribute value to
* a CSS attribute value. If there is no conversion
* returns <code>null</code>. By default, there is no conversion.
*
* @param key the <code>StyleConstants</code> attribute
* @param value the value of a <code>StyleConstants</code>
* attribute to be converted
* @return the CSS value that represents the
* <code>StyleConstants</code> value
*/
Object fromStyleConstants(StyleConstants key, Object value) {
if (value.equals(Boolean.TRUE)) {
return parseCssValue("bold");
}
return parseCssValue("normal");
}
Converts a CSS attribute value to a StyleConstants
value. If there is no conversion, returns null
.
By default, there is no conversion.
Params: - key – the
StyleConstants
attribute
Returns: the StyleConstants
attribute value that
represents the CSS attribute value
/**
* Converts a CSS attribute value to a <code>StyleConstants</code>
* value. If there is no conversion, returns <code>null</code>.
* By default, there is no conversion.
*
* @param key the <code>StyleConstants</code> attribute
* @return the <code>StyleConstants</code> attribute value that
* represents the CSS attribute value
*/
Object toStyleConstants(StyleConstants key, View v) {
return (weight > 500) ? Boolean.TRUE : Boolean.FALSE;
}
boolean isBold() {
return (weight > 500);
}
int weight;
}
static class ColorValue extends CssValue {
Returns the color to use.
/**
* Returns the color to use.
*/
Color getValue() {
return c;
}
Object parseCssValue(String value) {
Color c = stringToColor(value);
if (c != null) {
ColorValue cv = new ColorValue();
cv.svalue = value;
cv.c = c;
return cv;
}
return null;
}
Object parseHtmlValue(String value) {
return parseCssValue(value);
}
Converts a StyleConstants
attribute value to
a CSS attribute value. If there is no conversion
returns null
. By default, there is no conversion.
Params: - key – the
StyleConstants
attribute - value – the value of a
StyleConstants
attribute to be converted
Returns: the CSS value that represents the
StyleConstants
value
/**
* Converts a <code>StyleConstants</code> attribute value to
* a CSS attribute value. If there is no conversion
* returns <code>null</code>. By default, there is no conversion.
*
* @param key the <code>StyleConstants</code> attribute
* @param value the value of a <code>StyleConstants</code>
* attribute to be converted
* @return the CSS value that represents the
* <code>StyleConstants</code> value
*/
Object fromStyleConstants(StyleConstants key, Object value) {
ColorValue colorValue = new ColorValue();
colorValue.c = (Color)value;
colorValue.svalue = colorToHex(colorValue.c);
return colorValue;
}
Converts a CSS attribute value to a StyleConstants
value. If there is no conversion, returns null
.
By default, there is no conversion.
Params: - key – the
StyleConstants
attribute
Returns: the StyleConstants
attribute value that
represents the CSS attribute value
/**
* Converts a CSS attribute value to a <code>StyleConstants</code>
* value. If there is no conversion, returns <code>null</code>.
* By default, there is no conversion.
*
* @param key the <code>StyleConstants</code> attribute
* @return the <code>StyleConstants</code> attribute value that
* represents the CSS attribute value
*/
Object toStyleConstants(StyleConstants key, View v) {
return c;
}
Color c;
}
static class BorderStyle extends CssValue {
CSS.Value getValue() {
return style;
}
Object parseCssValue(String value) {
CSS.Value cssv = CSS.getValue(value);
if (cssv != null) {
if ((cssv == CSS.Value.INSET) ||
(cssv == CSS.Value.OUTSET) ||
(cssv == CSS.Value.NONE) ||
(cssv == CSS.Value.DOTTED) ||
(cssv == CSS.Value.DASHED) ||
(cssv == CSS.Value.SOLID) ||
(cssv == CSS.Value.DOUBLE) ||
(cssv == CSS.Value.GROOVE) ||
(cssv == CSS.Value.RIDGE)) {
BorderStyle bs = new BorderStyle();
bs.svalue = value;
bs.style = cssv;
return bs;
}
}
return null;
}
private void writeObject(java.io.ObjectOutputStream s)
throws IOException {
s.defaultWriteObject();
if (style == null) {
s.writeObject(null);
}
else {
s.writeObject(style.toString());
}
}
private void readObject(ObjectInputStream s)
throws ClassNotFoundException, IOException {
s.defaultReadObject();
Object value = s.readObject();
if (value != null) {
style = CSS.getValue((String)value);
}
}
// CSS.Values are static, don't archive it.
transient private CSS.Value style;
}
static class LengthValue extends CssValue {
if this length value may be negative.
/**
* if this length value may be negative.
*/
boolean mayBeNegative;
LengthValue() {
this(false);
}
LengthValue(boolean mayBeNegative) {
this.mayBeNegative = mayBeNegative;
}
Returns the length (span) to use.
/**
* Returns the length (span) to use.
*/
float getValue() {
return getValue(false);
}
float getValue(boolean isW3CLengthUnits) {
return getValue(0, isW3CLengthUnits);
}
Returns the length (span) to use. If the value represents
a percentage, it is scaled based on currentValue
.
/**
* Returns the length (span) to use. If the value represents
* a percentage, it is scaled based on <code>currentValue</code>.
*/
float getValue(float currentValue) {
return getValue(currentValue, false);
}
float getValue(float currentValue, boolean isW3CLengthUnits) {
if (percentage) {
return span * currentValue;
}
return LengthUnit.getValue(span, units, isW3CLengthUnits);
}
Returns true if the length represents a percentage of the
containing box.
/**
* Returns true if the length represents a percentage of the
* containing box.
*/
boolean isPercentage() {
return percentage;
}
Object parseCssValue(String value) {
LengthValue lv;
try {
// Assume pixels
float absolute = Float.valueOf(value).floatValue();
lv = new LengthValue();
lv.span = absolute;
} catch (NumberFormatException nfe) {
// Not pixels, use LengthUnit
LengthUnit lu = new LengthUnit(value,
LengthUnit.UNINITALIZED_LENGTH,
0);
// PENDING: currently, we only support absolute values and
// percentages.
switch (lu.type) {
case 0:
// Absolute
lv = new LengthValue();
lv.span =
(mayBeNegative) ? lu.value : Math.max(0, lu.value);
lv.units = lu.units;
break;
case 1:
// %
lv = new LengthValue();
lv.span = Math.max(0, Math.min(1, lu.value));
lv.percentage = true;
break;
default:
return null;
}
}
lv.svalue = value;
return lv;
}
Object parseHtmlValue(String value) {
if (value.equals(HTML.NULL_ATTRIBUTE_VALUE)) {
value = "1";
}
return parseCssValue(value);
}
Converts a StyleConstants
attribute value to
a CSS attribute value. If there is no conversion,
returns null
. By default, there is no conversion.
Params: - key – the
StyleConstants
attribute - value – the value of a
StyleConstants
attribute to be converted
Returns: the CSS value that represents the
StyleConstants
value
/**
* Converts a <code>StyleConstants</code> attribute value to
* a CSS attribute value. If there is no conversion,
* returns <code>null</code>. By default, there is no conversion.
*
* @param key the <code>StyleConstants</code> attribute
* @param value the value of a <code>StyleConstants</code>
* attribute to be converted
* @return the CSS value that represents the
* <code>StyleConstants</code> value
*/
Object fromStyleConstants(StyleConstants key, Object value) {
LengthValue v = new LengthValue();
v.svalue = value.toString();
v.span = ((Float)value).floatValue();
return v;
}
Converts a CSS attribute value to a StyleConstants
value. If there is no conversion, returns null
.
By default, there is no conversion.
Params: - key – the
StyleConstants
attribute
Returns: the StyleConstants
attribute value that
represents the CSS attribute value
/**
* Converts a CSS attribute value to a <code>StyleConstants</code>
* value. If there is no conversion, returns <code>null</code>.
* By default, there is no conversion.
*
* @param key the <code>StyleConstants</code> attribute
* @return the <code>StyleConstants</code> attribute value that
* represents the CSS attribute value
*/
Object toStyleConstants(StyleConstants key, View v) {
return new Float(getValue(false));
}
If true, span is a percentage value, and that to determine
the length another value needs to be passed in. /** If true, span is a percentage value, and that to determine
* the length another value needs to be passed in. */
boolean percentage;
Either the absolute value (percentage == false) or
a percentage value. /** Either the absolute value (percentage == false) or
* a percentage value. */
float span;
String units = null;
}
BorderWidthValue is used to model BORDER_XXX_WIDTH and adds support
for the thin/medium/thick values.
/**
* BorderWidthValue is used to model BORDER_XXX_WIDTH and adds support
* for the thin/medium/thick values.
*/
static class BorderWidthValue extends LengthValue {
BorderWidthValue(String svalue, int index) {
this.svalue = svalue;
span = values[index];
percentage = false;
}
Object parseCssValue(String value) {
if (value != null) {
if (value.equals("thick")) {
return new BorderWidthValue(value, 2);
}
else if (value.equals("medium")) {
return new BorderWidthValue(value, 1);
}
else if (value.equals("thin")) {
return new BorderWidthValue(value, 0);
}
}
// Assume its a length.
return super.parseCssValue(value);
}
Object parseHtmlValue(String value) {
if (value == HTML.NULL_ATTRIBUTE_VALUE) {
return parseCssValue("medium");
}
return parseCssValue(value);
}
Values used to represent border width. /** Values used to represent border width. */
private static final float[] values = { 1, 2, 4 };
}
Handles uniquing of CSS values, like lists, and background image
repeating.
/**
* Handles uniquing of CSS values, like lists, and background image
* repeating.
*/
static class CssValueMapper extends CssValue {
Object parseCssValue(String value) {
Object retValue = cssValueToInternalValueMap.get(value);
if (retValue == null) {
retValue = cssValueToInternalValueMap.get(value.toLowerCase());
}
return retValue;
}
Object parseHtmlValue(String value) {
Object retValue = htmlValueToCssValueMap.get(value);
if (retValue == null) {
retValue = htmlValueToCssValueMap.get(value.toLowerCase());
}
return retValue;
}
}
Used for background images, to represent the position.
/**
* Used for background images, to represent the position.
*/
static class BackgroundPosition extends CssValue {
float horizontalPosition;
float verticalPosition;
// bitmask: bit 0, horizontal relative, bit 1 horizontal relative to
// font size, 2 vertical relative to size, 3 vertical relative to
// font size.
//
short relative;
Object parseCssValue(String value) {
// 'top left' and 'left top' both mean the same as '0% 0%'.
// 'top', 'top center' and 'center top' mean the same as '50% 0%'.
// 'right top' and 'top right' mean the same as '100% 0%'.
// 'left', 'left center' and 'center left' mean the same as
// '0% 50%'.
// 'center' and 'center center' mean the same as '50% 50%'.
// 'right', 'right center' and 'center right' mean the same as
// '100% 50%'.
// 'bottom left' and 'left bottom' mean the same as '0% 100%'.
// 'bottom', 'bottom center' and 'center bottom' mean the same as
// '50% 100%'.
// 'bottom right' and 'right bottom' mean the same as '100% 100%'.
String[] strings = CSS.parseStrings(value);
int count = strings.length;
BackgroundPosition bp = new BackgroundPosition();
bp.relative = 5;
bp.svalue = value;
if (count > 0) {
// bit 0 for vert, 1 hor, 2 for center
short found = 0;
int index = 0;
while (index < count) {
// First, check for keywords
String string = strings[index++];
if (string.equals("center")) {
found |= 4;
continue;
}
else {
if ((found & 1) == 0) {
if (string.equals("top")) {
found |= 1;
}
else if (string.equals("bottom")) {
found |= 1;
bp.verticalPosition = 1;
continue;
}
}
if ((found & 2) == 0) {
if (string.equals("left")) {
found |= 2;
bp.horizontalPosition = 0;
}
else if (string.equals("right")) {
found |= 2;
bp.horizontalPosition = 1;
}
}
}
}
if (found != 0) {
if ((found & 1) == 1) {
if ((found & 2) == 0) {
// vert and no horiz.
bp.horizontalPosition = .5f;
}
}
else if ((found & 2) == 2) {
// horiz and no vert.
bp.verticalPosition = .5f;
}
else {
// no horiz, no vert, but center
bp.horizontalPosition = bp.verticalPosition = .5f;
}
}
else {
// Assume lengths
LengthUnit lu = new LengthUnit(strings[0], (short)0, 0f);
if (lu.type == 0) {
bp.horizontalPosition = lu.value;
bp.relative = (short)(1 ^ bp.relative);
}
else if (lu.type == 1) {
bp.horizontalPosition = lu.value;
}
else if (lu.type == 3) {
bp.horizontalPosition = lu.value;
bp.relative = (short)((1 ^ bp.relative) | 2);
}
if (count > 1) {
lu = new LengthUnit(strings[1], (short)0, 0f);
if (lu.type == 0) {
bp.verticalPosition = lu.value;
bp.relative = (short)(4 ^ bp.relative);
}
else if (lu.type == 1) {
bp.verticalPosition = lu.value;
}
else if (lu.type == 3) {
bp.verticalPosition = lu.value;
bp.relative = (short)((4 ^ bp.relative) | 8);
}
}
else {
bp.verticalPosition = .5f;
}
}
}
return bp;
}
boolean isHorizontalPositionRelativeToSize() {
return ((relative & 1) == 1);
}
boolean isHorizontalPositionRelativeToFontSize() {
return ((relative & 2) == 2);
}
float getHorizontalPosition() {
return horizontalPosition;
}
boolean isVerticalPositionRelativeToSize() {
return ((relative & 4) == 4);
}
boolean isVerticalPositionRelativeToFontSize() {
return ((relative & 8) == 8);
}
float getVerticalPosition() {
return verticalPosition;
}
}
Used for BackgroundImages.
/**
* Used for BackgroundImages.
*/
static class BackgroundImage extends CssValue {
private boolean loadedImage;
private ImageIcon image;
Object parseCssValue(String value) {
BackgroundImage retValue = new BackgroundImage();
retValue.svalue = value;
return retValue;
}
Object parseHtmlValue(String value) {
return parseCssValue(value);
}
// PENDING: this base is wrong for linked style sheets.
ImageIcon getImage(URL base) {
if (!loadedImage) {
synchronized(this) {
if (!loadedImage) {
URL url = CSS.getURL(base, svalue);
loadedImage = true;
if (url != null) {
image = new ImageIcon();
Image tmpImg = Toolkit.getDefaultToolkit().createImage(url);
if (tmpImg != null) {
image.setImage(tmpImg);
}
}
}
}
}
return image;
}
}
Parses a length value, this is used internally, and never added
to an AttributeSet or returned to the developer.
/**
* Parses a length value, this is used internally, and never added
* to an AttributeSet or returned to the developer.
*/
static class LengthUnit implements Serializable {
static Hashtable<String, Float> lengthMapping = new Hashtable<String, Float>(6);
static Hashtable<String, Float> w3cLengthMapping = new Hashtable<String, Float>(6);
static {
lengthMapping.put("pt", new Float(1f));
// Not sure about 1.3, determined by experiementation.
lengthMapping.put("px", new Float(1.3f));
lengthMapping.put("mm", new Float(2.83464f));
lengthMapping.put("cm", new Float(28.3464f));
lengthMapping.put("pc", new Float(12f));
lengthMapping.put("in", new Float(72f));
int res = 72;
try {
res = Toolkit.getDefaultToolkit().getScreenResolution();
} catch (HeadlessException e) {
}
// mapping according to the CSS2 spec
w3cLengthMapping.put("pt", new Float(res/72f));
w3cLengthMapping.put("px", new Float(1f));
w3cLengthMapping.put("mm", new Float(res/25.4f));
w3cLengthMapping.put("cm", new Float(res/2.54f));
w3cLengthMapping.put("pc", new Float(res/6f));
w3cLengthMapping.put("in", new Float(res));
}
LengthUnit(String value, short defaultType, float defaultValue) {
parse(value, defaultType, defaultValue);
}
void parse(String value, short defaultType, float defaultValue) {
type = defaultType;
this.value = defaultValue;
int length = value.length();
if (length > 0 && value.charAt(length - 1) == '%') {
try {
this.value = Float.valueOf(value.substring(0, length - 1)).
floatValue() / 100.0f;
type = 1;
}
catch (NumberFormatException nfe) { }
}
if (length >= 2) {
units = value.substring(length - 2, length);
Float scale = lengthMapping.get(units);
if (scale != null) {
try {
this.value = Float.valueOf(value.substring(0,
length - 2)).floatValue();
type = 0;
}
catch (NumberFormatException nfe) { }
}
else if (units.equals("em") ||
units.equals("ex")) {
try {
this.value = Float.valueOf(value.substring(0,
length - 2)).floatValue();
type = 3;
}
catch (NumberFormatException nfe) { }
}
else if (value.equals("larger")) {
this.value = 2f;
type = 2;
}
else if (value.equals("smaller")) {
this.value = -2;
type = 2;
}
else {
// treat like points.
try {
this.value = Float.valueOf(value).floatValue();
type = 0;
} catch (NumberFormatException nfe) {}
}
}
else if (length > 0) {
// treat like points.
try {
this.value = Float.valueOf(value).floatValue();
type = 0;
} catch (NumberFormatException nfe) {}
}
}
float getValue(boolean w3cLengthUnits) {
Hashtable<String, Float> mapping = (w3cLengthUnits) ? w3cLengthMapping : lengthMapping;
float scale = 1;
if (units != null) {
Float scaleFloat = mapping.get(units);
if (scaleFloat != null) {
scale = scaleFloat.floatValue();
}
}
return this.value * scale;
}
static float getValue(float value, String units, Boolean w3cLengthUnits) {
Hashtable<String, Float> mapping = (w3cLengthUnits) ? w3cLengthMapping : lengthMapping;
float scale = 1;
if (units != null) {
Float scaleFloat = mapping.get(units);
if (scaleFloat != null) {
scale = scaleFloat.floatValue();
}
}
return value * scale;
}
public String toString() {
return type + " " + value;
}
// 0 - value indicates real value
// 1 - % value, value relative to depends upon key.
// 50% will have a value = .5
// 2 - add value to parent value.
// 3 - em/ex relative to font size of element (except for
// font-size, which is relative to parent).
short type;
float value;
String units = null;
static final short UNINITALIZED_LENGTH = (short)10;
}
Class used to parse font property. The font property is shorthand
for the other font properties. This expands the properties, placing
them in the attributeset.
/**
* Class used to parse font property. The font property is shorthand
* for the other font properties. This expands the properties, placing
* them in the attributeset.
*/
static class ShorthandFontParser {
Parses the shorthand font string value
, placing the
result in attr
.
/**
* Parses the shorthand font string <code>value</code>, placing the
* result in <code>attr</code>.
*/
static void parseShorthandFont(CSS css, String value,
MutableAttributeSet attr) {
// font is of the form:
// [ <font-style> || <font-variant> || <font-weight> ]? <font-size>
// [ / <line-height> ]? <font-family>
String[] strings = CSS.parseStrings(value);
int count = strings.length;
int index = 0;
// bitmask, 1 for style, 2 for variant, 3 for weight
short found = 0;
int maxC = Math.min(3, count);
// Check for font-style font-variant font-weight
while (index < maxC) {
if ((found & 1) == 0 && isFontStyle(strings[index])) {
css.addInternalCSSValue(attr, CSS.Attribute.FONT_STYLE,
strings[index++]);
found |= 1;
}
else if ((found & 2) == 0 && isFontVariant(strings[index])) {
css.addInternalCSSValue(attr, CSS.Attribute.FONT_VARIANT,
strings[index++]);
found |= 2;
}
else if ((found & 4) == 0 && isFontWeight(strings[index])) {
css.addInternalCSSValue(attr, CSS.Attribute.FONT_WEIGHT,
strings[index++]);
found |= 4;
}
else if (strings[index].equals("normal")) {
index++;
}
else {
break;
}
}
if ((found & 1) == 0) {
css.addInternalCSSValue(attr, CSS.Attribute.FONT_STYLE,
"normal");
}
if ((found & 2) == 0) {
css.addInternalCSSValue(attr, CSS.Attribute.FONT_VARIANT,
"normal");
}
if ((found & 4) == 0) {
css.addInternalCSSValue(attr, CSS.Attribute.FONT_WEIGHT,
"normal");
}
// string at index should be the font-size
if (index < count) {
String fontSize = strings[index];
int slashIndex = fontSize.indexOf('/');
if (slashIndex != -1) {
fontSize = fontSize.substring(0, slashIndex);
strings[index] = strings[index].substring(slashIndex);
}
else {
index++;
}
css.addInternalCSSValue(attr, CSS.Attribute.FONT_SIZE,
fontSize);
}
else {
css.addInternalCSSValue(attr, CSS.Attribute.FONT_SIZE,
"medium");
}
// Check for line height
if (index < count && strings[index].startsWith("/")) {
String lineHeight = null;
if (strings[index].equals("/")) {
if (++index < count) {
lineHeight = strings[index++];
}
}
else {
lineHeight = strings[index++].substring(1);
}
// line height
if (lineHeight != null) {
css.addInternalCSSValue(attr, CSS.Attribute.LINE_HEIGHT,
lineHeight);
}
else {
css.addInternalCSSValue(attr, CSS.Attribute.LINE_HEIGHT,
"normal");
}
}
else {
css.addInternalCSSValue(attr, CSS.Attribute.LINE_HEIGHT,
"normal");
}
// remainder of strings are font-family
if (index < count) {
String family = strings[index++];
while (index < count) {
family += " " + strings[index++];
}
css.addInternalCSSValue(attr, CSS.Attribute.FONT_FAMILY,
family);
}
else {
css.addInternalCSSValue(attr, CSS.Attribute.FONT_FAMILY,
Font.SANS_SERIF);
}
}
private static boolean isFontStyle(String string) {
return (string.equals("italic") ||
string.equals("oblique"));
}
private static boolean isFontVariant(String string) {
return (string.equals("small-caps"));
}
private static boolean isFontWeight(String string) {
if (string.equals("bold") || string.equals("bolder") ||
string.equals("italic") || string.equals("lighter")) {
return true;
}
// test for 100-900
return (string.length() == 3 &&
string.charAt(0) >= '1' && string.charAt(0) <= '9' &&
string.charAt(1) == '0' && string.charAt(2) == '0');
}
}
Parses the background property into its intrinsic values.
/**
* Parses the background property into its intrinsic values.
*/
static class ShorthandBackgroundParser {
Parses the shorthand font string value
, placing the
result in attr
.
/**
* Parses the shorthand font string <code>value</code>, placing the
* result in <code>attr</code>.
*/
static void parseShorthandBackground(CSS css, String value,
MutableAttributeSet attr) {
String[] strings = parseStrings(value);
int count = strings.length;
int index = 0;
// bitmask: 0 for image, 1 repeat, 2 attachment, 3 position,
// 4 color
short found = 0;
while (index < count) {
String string = strings[index++];
if ((found & 1) == 0 && isImage(string)) {
css.addInternalCSSValue(attr, CSS.Attribute.
BACKGROUND_IMAGE, string);
found |= 1;
}
else if ((found & 2) == 0 && isRepeat(string)) {
css.addInternalCSSValue(attr, CSS.Attribute.
BACKGROUND_REPEAT, string);
found |= 2;
}
else if ((found & 4) == 0 && isAttachment(string)) {
css.addInternalCSSValue(attr, CSS.Attribute.
BACKGROUND_ATTACHMENT, string);
found |= 4;
}
else if ((found & 8) == 0 && isPosition(string)) {
if (index < count && isPosition(strings[index])) {
css.addInternalCSSValue(attr, CSS.Attribute.
BACKGROUND_POSITION,
string + " " +
strings[index++]);
}
else {
css.addInternalCSSValue(attr, CSS.Attribute.
BACKGROUND_POSITION, string);
}
found |= 8;
}
else if ((found & 16) == 0 && isColor(string)) {
css.addInternalCSSValue(attr, CSS.Attribute.
BACKGROUND_COLOR, string);
found |= 16;
}
}
if ((found & 1) == 0) {
css.addInternalCSSValue(attr, CSS.Attribute.BACKGROUND_IMAGE,
null);
}
if ((found & 2) == 0) {
css.addInternalCSSValue(attr, CSS.Attribute.BACKGROUND_REPEAT,
"repeat");
}
if ((found & 4) == 0) {
css.addInternalCSSValue(attr, CSS.Attribute.
BACKGROUND_ATTACHMENT, "scroll");
}
if ((found & 8) == 0) {
css.addInternalCSSValue(attr, CSS.Attribute.
BACKGROUND_POSITION, null);
}
// Currently, there is no good way to express this.
/*
if ((found & 16) == 0) {
css.addInternalCSSValue(attr, CSS.Attribute.BACKGROUND_COLOR,
null);
}
*/
}
static boolean isImage(String string) {
return (string.startsWith("url(") && string.endsWith(")"));
}
static boolean isRepeat(String string) {
return (string.equals("repeat-x") || string.equals("repeat-y") ||
string.equals("repeat") || string.equals("no-repeat"));
}
static boolean isAttachment(String string) {
return (string.equals("fixed") || string.equals("scroll"));
}
static boolean isPosition(String string) {
return (string.equals("top") || string.equals("bottom") ||
string.equals("left") || string.equals("right") ||
string.equals("center") ||
(string.length() > 0 &&
Character.isDigit(string.charAt(0))));
}
static boolean isColor(String string) {
return (CSS.stringToColor(string) != null);
}
}
Used to parser margin and padding.
/**
* Used to parser margin and padding.
*/
static class ShorthandMarginParser {
Parses the shorthand margin/padding/border string
value
, placing the result in attr
.
names
give the 4 instrinsic property names.
/**
* Parses the shorthand margin/padding/border string
* <code>value</code>, placing the result in <code>attr</code>.
* <code>names</code> give the 4 instrinsic property names.
*/
static void parseShorthandMargin(CSS css, String value,
MutableAttributeSet attr,
CSS.Attribute[] names) {
String[] strings = parseStrings(value);
int count = strings.length;
int index = 0;
switch (count) {
case 0:
// empty string
return;
case 1:
// Identifies all values.
for (int counter = 0; counter < 4; counter++) {
css.addInternalCSSValue(attr, names[counter], strings[0]);
}
break;
case 2:
// 0 & 2 = strings[0], 1 & 3 = strings[1]
css.addInternalCSSValue(attr, names[0], strings[0]);
css.addInternalCSSValue(attr, names[2], strings[0]);
css.addInternalCSSValue(attr, names[1], strings[1]);
css.addInternalCSSValue(attr, names[3], strings[1]);
break;
case 3:
css.addInternalCSSValue(attr, names[0], strings[0]);
css.addInternalCSSValue(attr, names[1], strings[1]);
css.addInternalCSSValue(attr, names[2], strings[2]);
css.addInternalCSSValue(attr, names[3], strings[1]);
break;
default:
for (int counter = 0; counter < 4; counter++) {
css.addInternalCSSValue(attr, names[counter],
strings[counter]);
}
break;
}
}
}
static class ShorthandBorderParser {
static Attribute[] keys = {
Attribute.BORDER_TOP, Attribute.BORDER_RIGHT,
Attribute.BORDER_BOTTOM, Attribute.BORDER_LEFT,
};
static void parseShorthandBorder(MutableAttributeSet attributes,
CSS.Attribute key, String value) {
Object[] parts = new Object[CSSBorder.PARSERS.length];
String[] strings = parseStrings(value);
for (String s : strings) {
boolean valid = false;
for (int i = 0; i < parts.length; i++) {
Object v = CSSBorder.PARSERS[i].parseCssValue(s);
if (v != null) {
if (parts[i] == null) {
parts[i] = v;
valid = true;
}
break;
}
}
if (!valid) {
// Part is non-parseable or occurred more than once.
return;
}
}
// Unspecified parts get default values.
for (int i = 0; i < parts.length; i++) {
if (parts[i] == null) {
parts[i] = CSSBorder.DEFAULTS[i];
}
}
// Dispatch collected values to individual properties.
for (int i = 0; i < keys.length; i++) {
if ((key == Attribute.BORDER) || (key == keys[i])) {
for (int k = 0; k < parts.length; k++) {
attributes.addAttribute(
CSSBorder.ATTRIBUTES[k][i], parts[k]);
}
}
}
}
}
Calculate the requirements needed to tile the requirements
given by the iterator that would be tiled. The calculation
takes into consideration margin and border spacing.
/**
* Calculate the requirements needed to tile the requirements
* given by the iterator that would be tiled. The calculation
* takes into consideration margin and border spacing.
*/
static SizeRequirements calculateTiledRequirements(LayoutIterator iter, SizeRequirements r) {
long minimum = 0;
long maximum = 0;
long preferred = 0;
int lastMargin = 0;
int totalSpacing = 0;
int n = iter.getCount();
for (int i = 0; i < n; i++) {
iter.setIndex(i);
int margin0 = lastMargin;
int margin1 = (int) iter.getLeadingCollapseSpan();
totalSpacing += Math.max(margin0, margin1);
preferred += (int) iter.getPreferredSpan(0);
minimum += iter.getMinimumSpan(0);
maximum += iter.getMaximumSpan(0);
lastMargin = (int) iter.getTrailingCollapseSpan();
}
totalSpacing += lastMargin;
totalSpacing += 2 * iter.getBorderWidth();
// adjust for the spacing area
minimum += totalSpacing;
preferred += totalSpacing;
maximum += totalSpacing;
// set return value
if (r == null) {
r = new SizeRequirements();
}
r.minimum = (minimum > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)minimum;
r.preferred = (preferred > Integer.MAX_VALUE) ? Integer.MAX_VALUE :(int) preferred;
r.maximum = (maximum > Integer.MAX_VALUE) ? Integer.MAX_VALUE :(int) maximum;
return r;
}
Calculate a tiled layout for the given iterator.
This should be done collapsing the neighboring
margins to be a total of the maximum of the two
neighboring margin areas as described in the CSS spec.
/**
* Calculate a tiled layout for the given iterator.
* This should be done collapsing the neighboring
* margins to be a total of the maximum of the two
* neighboring margin areas as described in the CSS spec.
*/
static void calculateTiledLayout(LayoutIterator iter, int targetSpan) {
/*
* first pass, calculate the preferred sizes, adjustments needed because
* of margin collapsing, and the flexibility to adjust the sizes.
*/
long preferred = 0;
long currentPreferred;
int lastMargin = 0;
int totalSpacing = 0;
int n = iter.getCount();
int adjustmentWeightsCount = LayoutIterator.WorstAdjustmentWeight + 1;
//max gain we can get adjusting elements with adjustmentWeight <= i
long gain[] = new long[adjustmentWeightsCount];
//max loss we can get adjusting elements with adjustmentWeight <= i
long loss[] = new long[adjustmentWeightsCount];
for (int i = 0; i < adjustmentWeightsCount; i++) {
gain[i] = loss[i] = 0;
}
for (int i = 0; i < n; i++) {
iter.setIndex(i);
int margin0 = lastMargin;
int margin1 = (int) iter.getLeadingCollapseSpan();
iter.setOffset(Math.max(margin0, margin1));
totalSpacing += iter.getOffset();
currentPreferred = (long)iter.getPreferredSpan(targetSpan);
iter.setSpan((int) currentPreferred);
preferred += currentPreferred;
gain[iter.getAdjustmentWeight()] +=
(long)iter.getMaximumSpan(targetSpan) - currentPreferred;
loss[iter.getAdjustmentWeight()] +=
currentPreferred - (long)iter.getMinimumSpan(targetSpan);
lastMargin = (int) iter.getTrailingCollapseSpan();
}
totalSpacing += lastMargin;
totalSpacing += 2 * iter.getBorderWidth();
for (int i = 1; i < adjustmentWeightsCount; i++) {
gain[i] += gain[i - 1];
loss[i] += loss[i - 1];
}
/*
* Second pass, expand or contract by as much as possible to reach
* the target span. This takes the margin collapsing into account
* prior to adjusting the span.
*/
// determine the adjustment to be made
int allocated = targetSpan - totalSpacing;
long desiredAdjustment = allocated - preferred;
long adjustmentsArray[] = (desiredAdjustment > 0) ? gain : loss;
desiredAdjustment = Math.abs(desiredAdjustment);
int adjustmentLevel = 0;
for (;adjustmentLevel <= LayoutIterator.WorstAdjustmentWeight;
adjustmentLevel++) {
// adjustmentsArray[] is sorted. I do not bother about
// binary search though
if (adjustmentsArray[adjustmentLevel] >= desiredAdjustment) {
break;
}
}
float adjustmentFactor = 0.0f;
if (adjustmentLevel <= LayoutIterator.WorstAdjustmentWeight) {
desiredAdjustment -= (adjustmentLevel > 0) ?
adjustmentsArray[adjustmentLevel - 1] : 0;
if (desiredAdjustment != 0) {
float maximumAdjustment =
adjustmentsArray[adjustmentLevel] -
((adjustmentLevel > 0) ?
adjustmentsArray[adjustmentLevel - 1] : 0
);
adjustmentFactor = desiredAdjustment / maximumAdjustment;
}
}
// make the adjustments
int totalOffset = (int)iter.getBorderWidth();
for (int i = 0; i < n; i++) {
iter.setIndex(i);
iter.setOffset( iter.getOffset() + totalOffset);
if (iter.getAdjustmentWeight() < adjustmentLevel) {
iter.setSpan((int)
((allocated > preferred) ?
Math.floor(iter.getMaximumSpan(targetSpan)) :
Math.ceil(iter.getMinimumSpan(targetSpan))
)
);
} else if (iter.getAdjustmentWeight() == adjustmentLevel) {
int availableSpan = (allocated > preferred) ?
(int) iter.getMaximumSpan(targetSpan) - iter.getSpan() :
iter.getSpan() - (int) iter.getMinimumSpan(targetSpan);
int adj = (int)Math.floor(adjustmentFactor * availableSpan);
iter.setSpan(iter.getSpan() +
((allocated > preferred) ? adj : -adj));
}
totalOffset = (int) Math.min((long) iter.getOffset() +
(long) iter.getSpan(),
Integer.MAX_VALUE);
}
// while rounding we could lose several pixels.
int roundError = targetSpan - totalOffset -
(int)iter.getTrailingCollapseSpan() -
(int)iter.getBorderWidth();
int adj = (roundError > 0) ? 1 : -1;
roundError *= adj;
boolean canAdjust = true;
while (roundError > 0 && canAdjust) {
// check for infinite loop
canAdjust = false;
int offsetAdjust = 0;
// try to distribute roundError. one pixel per cell
for (int i = 0; i < n; i++) {
iter.setIndex(i);
iter.setOffset(iter.getOffset() + offsetAdjust);
int curSpan = iter.getSpan();
if (roundError > 0) {
int boundGap = (adj > 0) ?
(int)Math.floor(iter.getMaximumSpan(targetSpan)) - curSpan :
curSpan - (int)Math.ceil(iter.getMinimumSpan(targetSpan));
if (boundGap >= 1) {
canAdjust = true;
iter.setSpan(curSpan + adj);
offsetAdjust += adj;
roundError--;
}
}
}
}
}
An iterator to express the requirements to use when computing
layout.
/**
* An iterator to express the requirements to use when computing
* layout.
*/
interface LayoutIterator {
void setOffset(int offs);
int getOffset();
void setSpan(int span);
int getSpan();
int getCount();
void setIndex(int i);
float getMinimumSpan(float parentSpan);
float getPreferredSpan(float parentSpan);
float getMaximumSpan(float parentSpan);
int getAdjustmentWeight(); //0 is the best weight WorstAdjustmentWeight is a worst one
//float getAlignment();
float getBorderWidth();
float getLeadingCollapseSpan();
float getTrailingCollapseSpan();
public static final int WorstAdjustmentWeight = 2;
}
//
// Serialization support
//
private void writeObject(java.io.ObjectOutputStream s)
throws IOException
{
s.defaultWriteObject();
// Determine what values in valueConvertor need to be written out.
Enumeration keys = valueConvertor.keys();
s.writeInt(valueConvertor.size());
if (keys != null) {
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
Object value = valueConvertor.get(key);
if (!(key instanceof Serializable) &&
(key = StyleContext.getStaticAttributeKey(key)) == null) {
// Should we throw an exception here?
key = null;
value = null;
}
else if (!(value instanceof Serializable) &&
(value = StyleContext.getStaticAttributeKey(value)) == null){
// Should we throw an exception here?
key = null;
value = null;
}
s.writeObject(key);
s.writeObject(value);
}
}
}
private void readObject(ObjectInputStream s)
throws ClassNotFoundException, IOException
{
s.defaultReadObject();
// Reconstruct the hashtable.
int numValues = s.readInt();
valueConvertor = new Hashtable<Object, Object>();
while (numValues-- > 0) {
Object key = s.readObject();
Object value = s.readObject();
Object staticKey = StyleContext.getStaticAttribute(key);
if (staticKey != null) {
key = staticKey;
}
Object staticValue = StyleContext.getStaticAttribute(value);
if (staticValue != null) {
value = staticValue;
}
if (key != null && value != null) {
valueConvertor.put(key, value);
}
}
}
/*
* we need StyleSheet for resolving lenght units. (see
* isW3CLengthUnits)
* we can not pass stylesheet for handling relative sizes. (do not
* think changing public API is necessary)
* CSS is not likely to be accessed from more then one thread.
* Having local storage for StyleSheet for resolving relative
* sizes is safe
*
* idk 08/30/2004
*/
private StyleSheet getStyleSheet(StyleSheet ss) {
if (ss != null) {
styleSheet = ss;
}
return styleSheet;
}
//
// Instance variables
//
Maps from CSS key to CssValue. /** Maps from CSS key to CssValue. */
private transient Hashtable<Object, Object> valueConvertor;
Size used for relative units. /** Size used for relative units. */
private int baseFontSize;
private transient StyleSheet styleSheet = null;
static int baseFontSizeIndex = 3;
}