/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.adobe.internal.fxg.dom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.adobe.fxg.FXGConstants.*;
import com.adobe.fxg.FXGException;
import com.adobe.fxg.dom.FXGNode;
import com.adobe.internal.fxg.dom.richtext.AbstractRichTextNode;
import com.adobe.internal.fxg.dom.richtext.BRNode;
import com.adobe.internal.fxg.dom.richtext.DivNode;
import com.adobe.internal.fxg.dom.richtext.ImgNode;
import com.adobe.internal.fxg.dom.richtext.LinkNode;
import com.adobe.internal.fxg.dom.richtext.ParagraphNode;
import com.adobe.internal.fxg.dom.richtext.SpanNode;
import com.adobe.internal.fxg.dom.richtext.TCYNode;
import com.adobe.internal.fxg.dom.richtext.TabNode;
import com.adobe.internal.fxg.dom.richtext.TextHelper;
import com.adobe.internal.fxg.dom.richtext.TextLayoutFormatNode;
import com.adobe.internal.fxg.dom.types.AlignmentBaseline;
import com.adobe.internal.fxg.dom.types.BaselineOffset;
import com.adobe.internal.fxg.dom.types.BaselineShift;
import com.adobe.internal.fxg.dom.types.BlockProgression;
import com.adobe.internal.fxg.dom.types.BreakOpportunity;
import com.adobe.internal.fxg.dom.types.ColorWithEnum;
import com.adobe.internal.fxg.dom.types.DigitCase;
import com.adobe.internal.fxg.dom.types.DigitWidth;
import com.adobe.internal.fxg.dom.types.Direction;
import com.adobe.internal.fxg.dom.types.DominantBaseline;
import com.adobe.internal.fxg.dom.types.FontStyle;
import com.adobe.internal.fxg.dom.types.FontWeight;
import com.adobe.internal.fxg.dom.types.JustificationRule;
import com.adobe.internal.fxg.dom.types.JustificationStyle;
import com.adobe.internal.fxg.dom.types.Kerning;
import com.adobe.internal.fxg.dom.types.LeadingModel;
import com.adobe.internal.fxg.dom.types.LigatureLevel;
import com.adobe.internal.fxg.dom.types.LineBreak;
import com.adobe.internal.fxg.dom.types.NumberAuto;
import com.adobe.internal.fxg.dom.types.NumberInherit;
import com.adobe.internal.fxg.dom.types.TextAlign;
import com.adobe.internal.fxg.dom.types.TextDecoration;
import com.adobe.internal.fxg.dom.types.TextJustify;
import com.adobe.internal.fxg.dom.types.TextRotation;
import com.adobe.internal.fxg.dom.types.TypographicCase;
import com.adobe.internal.fxg.dom.types.VerticalAlign;
import com.adobe.internal.fxg.dom.types.WhiteSpaceCollapse;
import com.adobe.internal.fxg.dom.types.BaselineOffset.BaselineOffsetAsEnum;
import com.adobe.internal.fxg.dom.types.BaselineShift.BaselineShiftAsEnum;
import com.adobe.internal.fxg.dom.types.ColorWithEnum.ColorEnum;
import com.adobe.internal.fxg.dom.types.NumberAuto.NumberAutoAsEnum;
import com.adobe.internal.fxg.dom.types.NumberInherit.NumberInheritAsEnum;
/**
* Represents a <RichText> element of an FXG Document.
*
* @since 2.0
* @author Min Plunkett
*/
public class RichTextNode extends GraphicContentNode implements TextNode
{
protected static final double FONTSIZE_MIN_INCLUSIVE = 1.0;
protected static final double FONTSIZE_MAX_INCLUSIVE = 720.0;
protected static final double PADDING_MIN_INCLUSIVE = 0.0;
protected static final double PADDING_MAX_INCLUSIVE = 1000.0;
protected static final double BASELINEOFFSET_MIN_INCLUSIVE = 0.0;
protected static final double BASELINEOFFSET_MAX_INCLUSIVE = 1000.0;
protected static final double BASELINESHIFT_MIN_INCLUSIVE = -1000.0;
protected static final double BASELINESHIFT_MAX_INCLUSIVE = 1000.0;
protected static final int COLUMNCOUNT_MIN_INCLUSIVE = 0;
protected static final int COLUMNCOUNT_MAX_INCLUSIVE = 50;
protected static final double COLUMNGAP_MIN_INCLUSIVE = 0.0;
protected static final double COLUMNGAP_MAX_INCLUSIVE = 1000.0;
protected static final double COLUMNWIDTH_MIN_INCLUSIVE = 0.0;
protected static final double COLUMNWIDTH_MAX_INCLUSIVE = 8000.0;
protected static final double LINEHEIGHT_PERCENT_MIN_INCLUSIVE = -1000.0;
protected static final double LINEHEIGHT_PERCENT_MAX_INCLUSIVE = 1000.0;
protected static final double LINEHEIGHT_PIXEL_MIN_INCLUSIVE = -720.0;
protected static final double LINEHEIGHT_PIXEL_MAX_INCLUSIVE = 720.0;
protected static final double PARAGRAPH_INDENT_MIN_INCLUSIVE = 0.0;
protected static final double PARAGRAPH_INDENT_MAX_INCLUSIVE = 1000.00;
protected static final double PARAGRAPH_SPACE_MIN_INCLUSIVE = 0.0;
protected static final double PARAGRAPH_SPACE_MAX_INCLUSIVE = 1000.00;
protected static final double TEXTINDENT_MIN_INCLUSIVE = -1000.0;
protected static final double TEXTINDENT_MAX_INCLUSIVE = 1000.0;
protected static final double TRACKING_MIN_INCLUSIVE = -1000.0;
protected static final double TRACKING_MAX_INCLUSIVE = 1000.0;
//--------------------------------------------------------------------------
//
// Attributes
//
//--------------------------------------------------------------------------
/** The width. */
public double width = 0.0;
/** The height. */
public double height = 0.0;
// Text Flow Attributes
/** The block progression: Controls the direction in which lines are
* stacked. In Latin text, this is tb, because lines start at the top and
* proceed downward. In vertical Chinese or Japanese, this is rl,
* because lines should start at the right side of the container and
* proceed leftward. Default to "tb".*/
public BlockProgression blockProgression = BlockProgression.TB;
/** The padding left: Inset from left edge to content area. Units in
* pixels, defaults to 0. Minumum/maximum values 0/1000. Non-inheriting. */
public NumberInherit paddingLeft = NumberInherit.newInstance(0.0);
/** The padding right: Inset from right edge to content area. Units in
* pixels, defaults to 0. Minumum/maximum values 0/1000. Non-inheriting. */
public NumberInherit paddingRight = NumberInherit.newInstance(0.0);
/** The padding top: Inset from top edge to content area. Units in
* pixels, defaults to 0. Minumum/maximum values 0/1000. Non-inheriting. */
public NumberInherit paddingTop = NumberInherit.newInstance(0.0);
/** The padding bottom: Inset from bottom edge to content area. Units in
* pixels, defaults to 0. Minumum/maximum values 0/1000. Non-inheriting. */
public NumberInherit paddingBottom = NumberInherit.newInstance(0.0);
/** The line break: This is an enumeration. "toFit" is the default.
* Non-inheriting. */
public LineBreak lineBreak = LineBreak.TOFIT;
/** The column gap: Space between columns in pixels. Does not include
* space before the first column or after the last column - that is
* padding. Legal values range from 0 to 1000. Default value is 0.
* Non-inheriting. */
public NumberInherit columnGap = NumberInherit.newInstance(20.0);
/** The column count ("auto" | integer): Number of columns. The
* column number overrides the other column settings. Value is an Integer,
* or auto if unspecified. If it's an integer, the range of legal values
* is 0 to 50. If columnCount is not specified, but columnWidth is, then
* columnWidth is used to create as many columns as can fit in the
* container. Non-inheriting. Default is "auto". */
public NumberAuto columnCount = NumberAuto.newInstance(NumberAutoAsEnum.AUTO);
/** The column width ("auto" | Number): Width of columns in pixels.
* If you specify the width of the columns, but not the count,
* TextLayout will create as many columns of that width as possible given
* the the container width and columnGap settings. Any remainder space
* is left after the last column. Legal values are 0 to 8000. Default
* is "auto". Non-inheriting. */
public NumberAuto columnWidth = NumberAuto.newInstance(NumberAutoAsEnum.AUTO);
/** The first baseline offset ("auto", "ascent", "lineHeight"
* | Number): Specifies the position of the first line of text in the
* container (first in each column) relative to the top of the container.
* The first line may appear at the position of the line's ascent, or below
* by the lineHeight of the first line. Or it may be offset by a pixel
* amount. The default value (auto) specifies that the line top be
* aligned to the container top inset. The baseline that this property
* refers to is deduced from the container's locale as follows:
* ideographicBottom for Chinese and Japanese locales, roman otherwise.
* Minumum/maximum values 0/1000. Default is "auto". */
public BaselineOffset firstBaselineOffset = BaselineOffset.newInstance(BaselineOffsetAsEnum.AUTO);
/** The vertical align ("top", "middle", "bottom",
* "justify", "inherit"): Vertical alignment of the lines within the
* container. The lines may appear at the top of the container, centered
* within the container, at the bottom, or evenly spread out across the
* depth of the container. Default is "top". Non-inheriting. */
public VerticalAlign verticalAlign = VerticalAlign.TOP;
// Paragraph Attributes
/** The text align ("start", "end", "left", "center",
* "right", "justify"): The alignment of the text relative to the text box
* edges. "start" is the edge specified by the direction property - left
* for direction="ltr", right for direction="rtl". Likewise "end" will be
* the right edge if direction="ltr", and the left edge if direction="rtl".
* Default is "start". */
public TextAlign textAlign = TextAlign.START;
/** The text align last ("start", "end", "left", "center",
* "right", "justify"): The alignment of the last line of the paragraph,
* applies if textAlign is justify. To make a paragraph set all lines
* justified, set textAlign and textAlignLast to justify. Default
* is "start". */
public TextAlign textAlignLast = TextAlign.START;
/** The text indent: The indentation of the first line of
* text in a paragraph. The indent is relative to the start edge.
* Measured in pixels. Default is 0. Can be negative. Minimum/maximum
* values are -1000/1000. */
public double textIndent = 0.0;
/** The paragraph start indent (Number | "inherit"): The indentation
* applied to the start edge (left edge if direction is ltr, right edge
* otherwise). Measured in pixels. Legal values range from 0 to 1000.
* Default is 0. */
public double paragraphStartIndent = 0.0;
/** The paragraph end indent (Number | "inherit"): The indentation
* applied to the end edge (right edge if direction is ltr, left edge
* otherwise). Measured in pixels. Legal values range from 0 to 1000.
* Default is 0. */
public double paragraphEndIndent = 0.0;
/** The paragraph space before (Number | "inherit"): This is the
* "space before" the paragraph. As in CSS, adjacent vertical space
* collapses. For two adjoining paragraphs (A, B), where A has
* paragraphSpaceAfter 12 and B has paragraphSpaceBefore 24, the total
* space between the paragraphs will be 24, the maximum of the two, and
* not 36, the sum. If the paragraph comes at the top of the column,
* no extra space is left before it; I think this is different than CSS.
* Legal values range from 0 to 1000. Default is 0. Minimum is 0. */
public double paragraphSpaceBefore = 0.0;
/** The paragraph space after (Number | "inherit"): This is the
* "space after" the paragraph. As in CSS, adjacent vertical space
* collapses (see note for paragraphSpaceBefore ). No "space after"
* is necessary if the paragraph falls at the bottom of the RichText.
* Legal values range from 0 to 1000. Default is 0. Minimum is 0. </li>
* <li><b>justificationRule</b> (String) ("auto", "space", "eastAsian"):
* Set up the justifier. EastAsian will turn on justification for Japanese.
* Default is "auto". An value of "auto" is resolved based on the
* locale of the paragraph. Values for Japanese ("ja") and Chinese
* ("zh-XX", "zh_XX", etc) resolve to eastAsian, while all other
* locales resolve to space. */
public double paragraphSpaceAfter = 0.0;
/** The direction ("ltr", "rtl"): Controls the dominant
* writing direction for the paragraph (left-to-right or right-to-left),
* and how characters with no implicit writing direction, such as
* punctuation, are treated. Also controls the direction of the columns,
* which are set according to the value of the direction attribute of the
* RichText element. Default is "ltr". */
public Direction direction = Direction.LTR;
/** The justification rule ("auto", "space", "eastAsian"):
* Set up the justifier. EastAsian will turn on justification for Japanese.
* Default is "auto". An value of "auto" is resolved based on the
* locale of the paragraph. Values for Japanese ("ja") and Chinese
* ("zh-XX", "zh_XX", etc) resolve to eastAsian, while all other
* locales resolve to space. */
public JustificationRule justificationRule = JustificationRule.AUTO;
/** The justification style ("auto",
* "prioritizeLeastAdjustment", "pushInKinsoku", "pushOutOnly"): An value
* of "auto" is resolved based on the locale of the paragraph. Currently,
* all locales resolve to pushInKinsoku, however, this value is only
* used in conjunction with a justificationRule value of eastAsian, so is
* only applicable to "ja" and all "zh" locales. PrioritizeLeastAdjustment
* bases justification on either expanding or compressing the line,
* whichever gives a result closest to the desired width. PushInKinsoku
* Bases justification on compressing kinsoku at the end of the line, or
* expanding it if there is no kinsoku or if that space is insufficient.
* PushOutOnly bases justification on expanding the line. Default
* is "auto".*/
public JustificationStyle justificationStyle = JustificationStyle.PRIORITIZELEASTADJUSTMENT;
/** The text justify ("interWord", "distribute"): Default is
* "interWord". Applies when justificationRule is space. interWord
* spreads justification space out to spaces in the line. distribute
* spreads it out to letters as well as spaces. */
public TextJustify textJustify = TextJustify.INTERWORD;
/** The leading model: ("auto", "romanUp", "ideographicTopUp",
* "ideographicCenterUp", "ascentDescentUp", "ideographicTopDown",
* "ideographicCenterDown", "approximateTextField"): Specifies the leading
* basis (baseline to which the <code>lineHeight</code> property refers)
* and the leading direction (which determines whether lineHeight
* property refers to the distance of a line's baseline from that of the
* line before it or the line after it). Default is auto which is resolved
* based on locale. Locale values of Japanese ("ja") and Chinese
* ("zh-XX", "zh_XX", etc) resolve auto to ideographicTopDown and other
* locales resolve to romanUp. */
public LeadingModel leadingModel = LeadingModel.AUTO;
/** The tab stops: Array of tab stops. By default there
* are no tabs. Each tab stop has a: position in pixels, relative to the
* start of the line; alignment<start, center, end, decimal>;
* decimalAlignmentToken(String). Used when you want to align the text
* with a particular character or substring within it (for instance,
* to a decimal point) */
public String tabStops = "";
// Text Leaf Attributes
/** The font family: The font family name used to render
* the text. The font family name may also be a comma-delimited list of
* font families, in which case the client should evaluate them in order.
* If no font is supplied, the client will pick one that is a variant of
* the Arial family, dependent on platform. Which font is used for
* rendering the text is up to the client and also depends on the glyphs
* that are being rendered and the fonts that are available.
* Default value is Arial. */
public String fontFamily = "Arial";
/** The font size: The size of the glyphs that is used to
* render the text, specified in pixels. Default is 12. Minimum 1 pixel.
* Maximum 720 pixels. */
public double fontSize = 12.0;
/** The font style: The style of the glyphs that is used to
* render the text. Legal values are "normal" and "italic". Default is
* "normal". */
public FontStyle fontStyle = FontStyle.NORMAL;
/** The font weight: The boldness or lightness of the glyphs
* that is used to render the text. Legal values are "normal" and "bold".
* Default is "normal". */
public FontWeight fontWeight = FontWeight.NORMAL;
/** The kerning ("on", "off", "auto"): If on, pair kerns are
* honored. If off, there is no font-based kerning applied. If auto,
* kerning is applied to all characters except Kanji, Hiragana or Katakana.
* The default is auto. Otherwise characters are drawn with no pair kerning
* adjustments. */
public Kerning kerning = Kerning.AUTO;
/** The line height: (Percent) | (Number): The distance from the
* baseline of the previous or the next line to the baseline of the
* current line is equal to the maximum amount of the leading applied to
* any character in the line. This is either a number or a percent. This
* can be specified in absolute pixels, or as a percentage. Default is
* 120%. Minimum/maximum value for number is -720/720, Minimum/maximum
* value percent is -1000%/1000%. */
public double lineHeight = 120.0;
/** The text decoration ("none", "underline"): The decoration
* to apply to the text. Default is "none". */
public TextDecoration textDecoration = TextDecoration.NONE;
/** The line through: true if text has strikethrough
* applied, false otherwise. Default is false. */
public boolean lineThrough = false;
/** The text color: The color of the text. Default is #000000. */
public int color = AbstractFXGNode.COLOR_BLACK;
/** The text alpha: The alpha value applied to the text.
* Default is 1.0. */
public double textAlpha = 1.0;
/** The white space collapse ("preserve", "collapse"): This
* is an enumerated value. A value of "collapse" converts line feeds,
* newlines, and tabs to spaces and collapses adjacent spaces to one.
* Leading and trailing whitespace is trimmed. A value of "preserve"
* passes whitespace through unchanged, except hen the whitespace would
* result in an implied <p> and <span> that is all whitespace, in which
* case the whitespace is removed. Default is "collapse". */
public WhiteSpaceCollapse whiteSpaceCollapse = WhiteSpaceCollapse.COLLAPSE;
/** The background alpha: Alpha (transparency) value for the
* background. A value of 0 is fully transparent, and a value of 1 is
* fully opaque. Default value is 1. Non-inheriting. */
public NumberInherit backgroundAlpha = NumberInherit.newInstance(1.0);;
/** Text Leaf Attribute: The background color. */
public ColorWithEnum backgroundColor = ColorWithEnum.newInstance(ColorEnum.TRANSPARENT);
/** The baseline shift: (Number, Percent, "superscript", "subscript"):
* Indicates the baseline shift for the element in pixels. The element is
* shifted perpendicular to the baseline by this amount. In horizontal
* text, a positive baseline shift moves the element up and a negative
* baseline shift moves the element down. The default value is 0.0,
* indicating no shift. A value of "superscript" shifts the text up by
* an amount specified in the font, and applies a transform to the
* fontSize also based on preferences in the font. A value of "subscript"
* shifts the text down by an amount specified in the font, and also
* transforms the fontSize. Percent shifts the text by a percentage of
* the fontSize. Minimum/maximum are -1000/1000, min/max percentage values
* are -1000%/1000%. */
public BaselineShift baselineShift = BaselineShift.newInstance(0.0);
/** The break opportunity ("auto", "any", "none", "all"):
* Controls where a line can legally break. "auto" means line breaking
* opportunities are based on standard Unicode character properties,
* such as breaking between words and on hyphens. Any indicates that the
* line may end at any character. This value is typically used when Roman
* text is embedded in Asian text and it is desirable for breaks to
* happen in the middle of words. None means that no characters in the
* range are treated as line break opportunities. All means that all
* characters in the range are treated as mandatory line break
* opportunities, so you get one character per line. Useful for creating
* effects like text on a path. Default is "auto". */
public BreakOpportunity breakOpportunity = BreakOpportunity.AUTO;
/** The digit case ("default", "lining", "oldStyle"): "default"
* uses the normal digit case from the font. "lining" uses the lining digit
* case from the font. "oldStyle" uses the old style digit case from the
* font. Default is "default". */
public DigitCase digitCase = DigitCase.DEFAULT;
/** The digit width ("default", "proportional", "tabular"):
* Specifies how wide digits will be when the text is set.
* Proportional means that the proportional widths from the font are
* used, and different digits will have different widths. Tabular means
* that every digits has the same width. Default means that the normal
* width from the font is used. Default is "default". */
public DigitWidth digitWidth = DigitWidth.DEFAULT;
/** The dominant baseline ("auto", "roman", "ascent",
* "descent", "ideographicTop", "ideographicCenter", "ideographicBottom"):
* Specifies which of the baselines of the element snaps to the
* alignmentBaseline to determine the vertical position of the element
* on the line. A value of "auto" gets resolved based on the textRotation
* of the span and the locale of the parent paragraph. A textRotation of
* "rotate270" resolves to ideographicCenter. A locale of Japanese ("ja")
* or Chinese ("zh-XX", "zh_XX", etc), resolves to ideographicCenter,
* whereas all others are resolved to roman. Default is auto. */
public DominantBaseline dominantBaseline = DominantBaseline.AUTO;
/** The alignment baseline ("roman", "ascent", "descent",
* "ideographicTop", "ideographicCenter", "ideographicBottom",
* "useDominantBaseline"): Specifies which of the baselines of the line
* containing the element the dominantBaseline snaps to, thus determining
* the vertical position of the element in the line. Default is
* "useDominantBaseline". */
public AlignmentBaseline alignmentBaseline = AlignmentBaseline.USEDOMINANTBASELINE;
/** The ligature level ("minimum", "common", "uncommon",
* "exotic"): The ligature level used for this text. Controls which
* ligatures in the font will be used. Minimum turns on rlig, common is
* rlig + clig + liga, uncommon is rlig + clig + liga + dlig, exotic is
* rlig + clig + liga + dlig + hlig. There is no way to turn the various
* ligature features on independently. Default is "common". </li>
* <li><b>locale</b> (String): The locale of the text. Controls case
* transformations and shaping. Standard locale identifiers as described
* in Unicode Technical Standard #35 are used. For example en,
* en_US and en-US are all English, ja is Japanese. Locale applied at
* the paragraph and higher level impacts resolution of "auto" values
* for dominantBaseline, justificationRule, justificationStyle and
* leadingModel. See individual attributes for resolution values. */
public LigatureLevel ligatureLevel = LigatureLevel.COMMON;
/** The locale: The locale of the text. Controls case
* transformations and shaping. Standard locale identifiers as described
* in Unicode Technical Standard #35 are used. For example en,
* en_US and en-US are all English, ja is Japanese. Locale applied at
* the paragraph and higher level impacts resolution of "auto" values
* for dominantBaseline, justificationRule, justificationStyle and
* leadingModel. See individual attributes for resolution values. */
public String locale = "en";
/** The typographic case ("default", "capsToSmallCaps",
* "uppercase", "lowercase", "lowercaseToSmallCaps": Controls the case in
* which the text will appear. "default" for the font that's
* chosen - i.e., its what you get without applying any features or case
* changes. smallCaps converts all characters to uppercase and applies c2sc.
* uppercase and lowercase are case conversions. caps turns on case.
* lowercaseToSmallCaps converts all characters to uppercase, and for
* those characters which have been converted, applies c2sc.
* Default is "default". */
public TypographicCase typographicCase = TypographicCase.DEFAULT;
/** The tracking left: Space added to the left of
* each character. A Number tracks by a pixel amount, minimum/maximum
* values -1000/1000. Percent is a percent of the current fontSize,
* and may be negative, to bring characters closer together. Legal values
* for percentages are -1000% to 1000%. Default is 0. */
public double trackingLeft = 0.0;
/** The tracking right: Space added to the right of
* each character. A Number tracks by a pixel amount, minimum/maximum
* values -1000/1000. Percent is a percent of the current fontSize,
* and may be negative, to bring characters closer together. Legal values
* for percentages are -1000% to 1000%. Default is 0.*/
public double trackingRight = 0.0;
/** The text rotation ("auto", "rotate0", "rotate90", "rotate180",
* "rotate270"): The rotation of the text, in ninety degree increments.
* Default is "auto". */
public TextRotation textRotation = TextRotation.AUTO;
// Link format properties
/** The link normal format. */
public TextLayoutFormatNode linkNormalFormat = null;
/** The link hover format. */
public TextLayoutFormatNode linkHoverFormat = null;
/** The link active format. */
public TextLayoutFormatNode linkActiveFormat = null;
private boolean contiguous = false;
//--------------------------------------------------------------------------
//
// TextNode Helpers
//
//--------------------------------------------------------------------------
/**
* The attributes set on this node.
*/
protected Map<String, String> textAttributes;
/**
* @return A Map recording the attribute names and values set on this
* text node.
*/
public Map<String, String> getTextAttributes()
{
return textAttributes;
}
/**
* This node's child text nodes.
*/
protected List<TextNode> content;
/**
* @return The List of child nodes of this text node.
*/
public List<TextNode> getTextChildren()
{
return content;
}
/**
* This node's child property nodes.
*/
protected Map<String, TextNode> properties;
/**
* @return The List of child property nodes of this text node.
*/
public Map<String, TextNode> getTextProperties()
{
return properties;
}
/**
* A RichText node can also have special child property nodes that represent
* complex property values that cannot be set via a simple attribute.
*
* @param propertyName the property name
* @param node the node
*/
public void addTextProperty(String propertyName, TextNode node)
{
if (node instanceof TextLayoutFormatNode)
{
if (FXG_LINKACTIVEFORMAT_PROPERTY_ELEMENT.equals(propertyName))
{
if (linkActiveFormat == null)
{
linkActiveFormat = (TextLayoutFormatNode)node;
linkActiveFormat.setParent(this);
if (properties == null)
properties = new HashMap<String, TextNode>(3);
properties.put(propertyName, linkActiveFormat);
}
else
{
// Exception: Multiple LinkFormat elements are not allowed.
throw new FXGException(getStartLine(), getStartColumn(), "MultipleLinkFormatElements");
}
}
else if (FXG_LINKHOVERFORMAT_PROPERTY_ELEMENT.equals(propertyName))
{
if (linkHoverFormat == null)
{
linkHoverFormat = (TextLayoutFormatNode)node;
linkHoverFormat.setParent(this);
if (properties == null)
properties = new HashMap<String, TextNode>(3);
properties.put(propertyName, linkHoverFormat);
}
else
{
// Exception: Multiple LinkFormat elements are not allowed.
throw new FXGException(getStartLine(), getStartColumn(), "MultipleLinkFormatElements");
}
}
else if (FXG_LINKNORMALFORMAT_PROPERTY_ELEMENT.equals(propertyName))
{
if (linkNormalFormat == null)
{
linkNormalFormat = (TextLayoutFormatNode)node;
linkNormalFormat.setParent(this);
if (properties == null)
properties = new HashMap<String, TextNode>(3);
properties.put(propertyName, linkNormalFormat);
}
else
{
// Exception: Multiple LinkFormat elements are not allowed.
throw new FXGException(getStartLine(), getStartColumn(), "MultipleLinkFormatElements");
}
}
else
{
// Exception: Unknown LinkFormat element.
throw new FXGException(node.getStartLine(), node.getStartColumn(), "UnknownLinkFormat", propertyName);
}
}
else
{
addChild(node);
}
}
/**
* <RichText> content allows child <p>, <span> and
* <br /> tags, as well as character data (text content).
*
* @param child - a child FXG node to be added to this node.
* @throws FXGException if the child is not supported by this node.
*/
public void addContentChild(FXGNode child)
{
if (child instanceof ParagraphNode
|| child instanceof DivNode
|| child instanceof SpanNode
|| child instanceof BRNode
|| child instanceof TabNode
|| child instanceof TCYNode
|| child instanceof LinkNode
|| child instanceof ImgNode
|| child instanceof CDATANode)
{
if (child instanceof LinkNode && (((LinkNode)child).href == null))
{
// Exception: Missing href attribute in <a> element.
throw new FXGException(getStartLine(), getStartColumn(), "MissingHref");
}
if (content == null)
{
content = new ArrayList<TextNode>();
contiguous = true;
}
if (!contiguous)
{
throw new FXGException(child.getStartLine(), child.getStartColumn(), "InvalidRichTextContent");
}
content.add((TextNode)child);
}
else
{
throw new FXGException(child.getStartLine(), child.getStartColumn(), "InvalidChildNode", child.getNodeName(), getNodeName());
}
if (child instanceof AbstractRichTextNode)
((AbstractRichTextNode)child).setParent(this);
}
/**
* Remember that an attribute was set on this node.
*
* @param name - the unqualified attribute name.
* @param value - the attribute value.
*/
protected void rememberAttribute(String name, String value)
{
if (textAttributes == null)
textAttributes = new HashMap<String, String>(4);
textAttributes.put(name, value);
}
//--------------------------------------------------------------------------
//
// FXGNode Implementation
//
//--------------------------------------------------------------------------
/**
* This method is invoked for only non-content children. Supported child
* node: CDATANode. Content needs to be contigous.
*
* @param child - a child FXG node to be added to this node.
* @throws FXGException if the child is not supported by this node.
*/
@Override
public void addChild(FXGNode child)
{
if (child instanceof CDATANode)
{
if (TextHelper.ignorableWhitespace(((CDATANode)child).content))
{
/**
* Ignorable white spaces don't break content contiguous
* rule and should be ignored.
*/
return;
}
else
{
throw new FXGException(child.getStartLine(), child.getStartColumn(), "InvalidRichTextContent");
}
}
else
{
super.addChild(child);
contiguous = false;
return;
}
}
/**
* @return The unqualified name of a RichText node, without tag markup.
*/
public String getNodeName()
{
return FXG_RICHTEXT_ELEMENT;
}
/**
* Sets an FXG attribute on this RichText node. Delegates to the parent
* class to process attributes that are not in the list below.
*
* In addition to the attributes supported by all graphic content nodes,
* RichText supports the following attributes.
*
* <p>
* <ul>
* <li><b>width</b> (Number): The width of the text box to render text
* in.</li>
* <li><b>height</b> (Number): The height of the text box to render text
* in.</li>
* <li><b>blockProgression</b> (String) ("tb", "rl"): Controls the
* direction in which lines are stacked. In Latin text, this is tb,
* because lines start at the top and proceed downward. In vertical
* Chinese or Japanese, this is rl, because lines should start at the
* right side of the container and proceed leftward. Default to "tb". </li>
* <li><b>paddingLeft</b> (Number): Inset from left edge to content area.
* Units in pixels, defaults to 0. Minumum/maximum values 0/1000.
* Non-inheriting. </li>
* <li><b>paddingRight</b> (Number): Inset from right edge to content
* area. Units in pixels, defaults to 0. Minumum/maximum values 0/1000.
* Non-inheriting. </li>
* <li><b>paddingTop</b> (Number): Inset from top edge to content area.
* Units in pixels, defaults to 0. Minumum/maximum values 0/1000.
* Non-inheriting. </li>
* <li><b>paddingBottom</b> (Number): Inset from bottom edge to content
* area. Units in pixels, defaults to 0. Minumum/maximum values 0/1000.
* Non-inheriting. </li>
* <li><b>columnGap</b> (Number): Space between columns in pixels. Does
* not include space before the first column or after the last column -
* that is padding. Legal values range from 0 to 1000. Default value is 0.
* columnGap is non-inheriting. </li>
* <li><b>columnCount</b> ("auto" | integer): Number of columns. The
* column number overrides the other column settings. Value is an Integer,
* or auto if unspecified. If it's an integer, the range of legal values
* is 0 to 50. If columnCount is not specified, but columnWidth is, then
* columnWidth is used to create as many columns as can fit in the
* container. columnCount is non-inheriting. Default is "auto". </li>
* <li><b>columnWidth</b> ("auto" | Number): Width of columns in pixels.
* If you specify the width of the columns, but not the count,
* TextLayout will create as many columns of that width as possible given
* the the container width and columnGap settings. Any remainder space
* is left after the last column. Legal values are 0 to 8000. Default
* is "auto". columnWidth is non-inheriting. </li>
* <li><b>firstBaselineOffset</b> (String) ("auto", "ascent", "lineHeight"
* | Number): Specifies the position of the first line of text in the
* container (first in each column) relative to the top of the container.
* The first line may appear at the position of the line's ascent, or below
* by the lineHeight of the first line. Or it may be offset by a pixel
* amount. The default value (auto) specifies that the line top be
* aligned to the container top inset. The baseline that this property
* refers to is deduced from the container's locale as follows:
* ideographicBottom for Chinese and Japanese locales, roman otherwise.
* Minumum/maximum values 0/1000. Default is "auto". </li>
* <li><b>verticalAlign</b> (String) ("top", "middle", "bottom",
* "justify", "inherit"): Vertical alignment of the lines within the
* container. The lines may appear at the top of the container, centered
* within the container, at the bottom, or evenly spread out across the
* depth of the container. Default is "top". verticalAlign is
* non-inheriting. </li>
* <li><b>lineBreak</b> (String) ("toFit", "explicit"): This is an
* enumeration. A value of "toFit" wraps the lines at the edge of the
* enclosing RichText. A value of "explicit" breaks the lines only at a
* Unicode line end character (such as a newline or line separator).
* "toFit" is the default. lineBreak is a non-inheriting attribute. </li>
* <li><b>textAlign</b> (String) ("start", "end", "left", "center",
* "right", "justify"): The alignment of the text relative to the text box
* edges. "start" is the edge specified by the direction property - left
* for direction="ltr", right for direction="rtl". Likewise "end" will be
* the right edge if direction="ltr", and the left edge if direction="rtl".
* Default is "start". </li>
* <li><b>textAlignLast </b> (String) ("start", "end", "left", "center",
* "right", "justify"): The alignment of the last line of the paragraph,
* applies if textAlign is justify. To make a paragraph set all lines
* justified, set textAlign and textAlignLast to justify. Default
* is "start". </li>
* <li><b>textIndent</b> (Number): The indentation of the first line of
* text in a paragraph. The indent is relative to the start edge.
* Measured in pixels. Default is 0. Can be negative. Minimum/maximum
* values are -1000/1000. </li>
* <li><b>paragraphStartIndent</b> (Number | "inherit"): The indentation
* applied to the start edge (left edge if direction is ltr, right edge
* otherwise). Measured in pixels. Legal values range from 0 to 1000.
* Default is 0. </li>
* <li><b>paragraphEndIndent</b> (Number | "inherit"): The indentation
* applied to the end edge (right edge if direction is ltr, left edge
* otherwise). Measured in pixels. Legal values range from 0 to 1000.
* Default is 0. </li>
* <li><b>paragraphSpaceBefore</b> (Number | "inherit"): This is the
* "space before" the paragraph. As in CSS, adjacent vertical space
* collapses. For two adjoining paragraphs (A, B), where A has
* paragraphSpaceAfter 12 and B has paragraphSpaceBefore 24, the total
* space between the paragraphs will be 24, the maximum of the two, and
* not 36, the sum. If the paragraph comes at the top of the column,
* no extra space is left before it; I think this is different than CSS.
* Legal values range from 0 to 1000. Default is 0. Minimum is 0. </li>
* <li><b>paragraphSpaceAfter</b> (Number | "inherit"): This is the
* "space after" the paragraph. As in CSS, adjacent vertical space
* collapses (see note for paragraphSpaceBefore ). No "space after"
* is necessary if the paragraph falls at the bottom of the RichText.
* Legal values range from 0 to 1000. Default is 0. Minimum is 0. </li>
* <li><b>justificationRule</b> (String) ("auto", "space", "eastAsian"):
* Set up the justifier. EastAsian will turn on justification for Japanese.
* Default is "auto". An value of "auto" is resolved based on the
* locale of the paragraph. Values for Japanese ("ja") and Chinese
* ("zh-XX", "zh_XX", etc) resolve to eastAsian, while all other
* locales resolve to space. </li>
* <li><b>justificationStyle</b> (String) ("auto",
* "prioritizeLeastAdjustment", "pushInKinsoku", "pushOutOnly"): An value
* of "auto" is resolved based on the locale of the paragraph. Currently,
* all locales resolve to pushInKinsoku, however, this value is only
* used in conjunction with a justificationRule value of eastAsian, so is
* only applicable to "ja" and all "zh" locales. PrioritizeLeastAdjustment
* bases justification on either expanding or compressing the line,
* whichever gives a result closest to the desired width. PushInKinsoku
* Bases justification on compressing kinsoku at the end of the line, or
* expanding it if there is no kinsoku or if that space is insufficient.
* PushOutOnly bases justification on expanding the line. Default
* is "auto". </li>
* <li><b>textJustify</b> (String) ("interWord", "distribute"): Default is
* "interWord". Applies when justificationRule is space. interWord
* spreads justification space out to spaces in the line. distribute
* spreads it out to letters as well as spaces. </li>
* <li><b>leadingModel</b> (String) ("auto", "romanUp", "ideographicTopUp",
* "ideographicCenterUp", "ascentDescentUp", "ideographicTopDown",
* "ideographicCenterDown", "approximateTextField"): Specifies the leading
* basis (baseline to which the <code>lineHeight</code> property refers)
* and the leading direction (which determines whether lineHeight
* property refers to the distance of a line's baseline from that of the
* line before it or the line after it). Default is auto which is resolved
* based on locale. Locale values of Japanese ("ja") and Chinese
* ("zh-XX", "zh_XX", etc) resolve auto to ideographicTopDown and other
* locales resolve to romanUp. </li>
* <li><b>tabStops</b> (Array): Array of tab stops. By default there
* are no tabs. Each tab stop has a: position in pixels, relative to the
* start of the line; alignment<start, center, end, decimal>;
* decimalAlignmentToken(String). Used when you want to align the text
* with a particular character or substring within it (for instance,
* to a decimal point). </li>
* <li><b>direction</b> (String) ("ltr", "rtl"): Controls the dominant
* writing direction for the paragraph (left-to-right or right-to-left),
* and how characters with no implicit writing direction, such as
* punctuation, are treated. Also controls the direction of the columns,
* which are set according to the value of the direction attribute of the
* RichText element. Default is "ltr". </li>
* <li><b>fontFamily</b> (String): The font family name used to render
* the text. The font family name may also be a comma-delimited list of
* font families, in which case the client should evaluate them in order.
* If no font is supplied, the client will pick one that is a variant of
* the Arial family, dependent on platform. Which font is used for
* rendering the text is up to the client and also depends on the glyphs
* that are being rendered and the fonts that are available.
* Default value is Arial. </li>
* <li><b>fontSize</b> (Number): The size of the glyphs that is used to
* render the text, specified in pixels. Default is 12. Minimum 1 pixel.
* Maximum 720 pixels. </li>
* <li><b>fontStyle</b> (String): The style of the glyphs that is used to
* render the text. Legal values are "normal" and "italic". Default is
* "normal". </li>
* <li><b>fontWeight</b> (String): The boldness or lightness of the glyphs
* that is used to render the text. Legal values are "normal" and "bold".
* Default is "normal". </li>
* <li><b>lineHeight</b> (Percent) | (Number): The distance from the
* baseline of the previous or the next line to the baseline of the
* current line is equal to the maximum amount of the leading applied to
* any character in the line. This is either a number or a percent. This
* can be specified in absolute pixels, or as a percentage. Default is
* 120%. Minimum/maximum value for number is -720/720, Minimum/maximum
* value percent is -1000%/1000%. </li>
* <li><b>textDecoration</b> (String) ("none", "underline"): The decoration
* to apply to the text. Default is "none". </li>
* <li><b>lineThrough</b> (Boolean): true if text has strikethrough
* applied, false otherwise. Default is false. </li>
* <li><b>color</b> (Color): The color of the text. Default is #000000.</li>
* <li><b>textAlpha</b> (Number): The alpha value applied to the text.
* Default is 1.0. </li>
* <li><b>whiteSpaceCollapse</b> (String) ("preserve", "collapse"): This
* is an enumerated value. A value of "collapse" converts line feeds,
* newlines, and tabs to spaces and collapses adjacent spaces to one.
* Leading and trailing whitespace is trimmed. A value of "preserve"
* passes whitespace through unchanged, except hen the whitespace would
* result in an implied <p> and <span> that is all whitespace, in which
* case the whitespace is removed. Default is "collapse". </li>
* <li><b>kerning</b> (String) ("on", "off", "auto"): If on, pair kerns are
* honored. If off, there is no font-based kerning applied. If auto,
* kerning is applied to all characters except Kanji, Hiragana or Katakana.
* The default is auto. Otherwise characters are drawn with no pair kerning
* adjustments. </li>
* <li><b>backgroundAlpha</b> (Number): Alpha (transparency) value for the
* background. A value of 0 is fully transparent, and a value of 1 is
* fully opaque. Default value is 1. backgroundAlpha is non-inheriting.</li>
* <li><b>backgroundColor</b> (color, "transparent"): Background color of
* the text. Can be either transparent, or an integer containing three 8-bit
* RGB components. Default is transparent. BackgroundColor is
* non-inheriting. </li>
* <li><b>baselineShift</b> (Number, Percent, "superscript", "subscript"):
* Indicates the baseline shift for the element in pixels. The element is
* shifted perpendicular to the baseline by this amount. In horizontal
* text, a positive baseline shift moves the element up and a negative
* baseline shift moves the element down. The default value is 0.0,
* indicating no shift. A value of "superscript" shifts the text up by
* an amount specified in the font, and applies a transform to the
* fontSize also based on preferences in the font. A value of "subscript"
* shifts the text down by an amount specified in the font, and also
* transforms the fontSize. Percent shifts the text by a percentage of
* the fontSize. Minimum/maximum are -1000/1000, min/max percentage values
* are -1000%/1000%. </li>
* <li><b>breakOpportunity</b> (String) ("auto", "any", "none", "all"):
* Controls where a line can legally break. Auto means line breaking
* opportunities are based on standard Unicode character properties,
* such as breaking between words and on hyphens. Any indicates that the
* line may end at any character. This value is typically used when Roman
* text is embedded in Asian text and it is desirable for breaks to
* happen in the middle of words. None means that no characters in the
* range are treated as line break opportunities. All means that all
* characters in the range are treated as mandatory line break
* opportunities, so you get one character per line. Useful for creating
* effects like text on a path. Default is auto. </li>
* <li><b>digitCase</b> (String) ("default", "lining", "oldStyle"). Default
* uses the normal digit case from the font. Lining uses the lining digit
* case from the font. oldStyle uses the old style digit case from the
* font. Default is "default". </li>
* <li><b>digitWidth</b> (String) ("default", "proportional", "tabular"):
* Specifies how wide digits will be when the text is set.
* Proportional means that the proportional widths from the font are
* used, and different digits will have different widths. Tabular means
* that every digits has the same width. Default means that the normal
* width from the font is used. Default is "default". </li>
* <li><b>dominantBaseline</b> (String) ("auto", "roman", "ascent",
* "descent", "ideographicTop", "ideographicCenter", "ideographicBottom"):
* Specifies which of the baselines of the element snaps to the
* alignmentBaseline to determine the vertical position of the element
* on the line. A value of "auto" gets resolved based on the textRotation
* of the span and the locale of the parent paragraph. A textRotation of
* "rotate270" resolves to ideographicCenter. A locale of Japanese ("ja")
* or Chinese ("zh-XX", "zh_XX", etc), resolves to ideographicCenter,
* whereas all others are resolved to roman. Default is auto. </li>
* <li><b>alignmentBaseline</b> (String) ("roman", "ascent", "descent",
* "ideographicTop", "ideographicCenter", "ideographicBottom",
* "useDominantBaseline"): Specifies which of the baselines of the line
* containing the element the dominantBaseline snaps to, thus determining
* the vertical position of the element in the line. Default is
* "useDominantBaseline". </li>
* <li><b>ligatureLevel</b> (String) ("minimum", "common", "uncommon",
* "exotic"): The ligature level used for this text. Controls which
* ligatures in the font will be used. Minimum turns on rlig, common is
* rlig + clig + liga, uncommon is rlig + clig + liga + dlig, exotic is
* rlig + clig + liga + dlig + hlig. There is no way to turn the various
* ligature features on independently. Default is "common". </li>
* <li><b>locale</b> (String): The locale of the text. Controls case
* transformations and shaping. Standard locale identifiers as described
* in Unicode Technical Standard #35 are used. For example en,
* en_US and en-US are all English, ja is Japanese. Locale applied at
* the paragraph and higher level impacts resolution of "auto" values
* for dominantBaseline, justificationRule, justificationStyle and
* leadingModel. See individual attributes for resolution values. </li>
* <li><b>typographicCase</b> (String) ("default", "capsToSmallCaps",
* "uppercase", "lowercase", "lowercaseToSmallCaps": Controls the case in
* which the text will appear. "default" is for the font that's
* chosen - i.e., its what you get without applying any features or case
* changes. smallCaps converts all characters to uppercase and applies c2sc.
* uppercase and lowercase are case conversions. caps turns on case.
* lowercaseToSmallCaps converts all characters to uppercase, and for
* those characters which have been converted, applies c2sc.
* Default is "default". </li>
* <li><b>textRotation(String) ("auto", "rotate0", "rotate90", "rotate180",
* "rotate270"): The rotation of the text, in ninety degree increments.
* Default is "auto". </li>
* <li><b>trackingLeft (Number | Percent): Space added to the left of
* each character. A Number tracks by a pixel amount, minimum/maximum
* values -1000/1000. Percent is a percent of the current fontSize,
* and may be negative, to bring characters closer together. Legal values
* for percentages are -1000% to 1000%. Default is 0. </li>
* <li><b>trackingRight (Number | Percent): Space added to the right of
* each character. A Number tracks by a pixel amount, minimum/maximum
* values -1000/1000. Percent is a percent of the current fontSize,
* and may be negative, to bring characters closer together. Legal values
* for percentages are -1000% to 1000%. Default is 0. </li>
* </ul>
* </p>
* @param name - the unqualified attribute name.
* @param value - the attribute value.
*
* @throws FXGException if a value is out of the valid range.
* @see com.adobe.internal.fxg.dom.GraphicContentNode#setAttribute(java.lang.String, java.lang.String)
*/
@Override
public void setAttribute(String name, String value)
{
if (FXG_WIDTH_ATTRIBUTE.equals(name))
{
width = DOMParserHelper.parseDouble(this, value, name);
}
else if (FXG_HEIGHT_ATTRIBUTE.equals(name))
{
height = DOMParserHelper.parseDouble(this, value, name);
}
else if (FXG_BLOCKPROGRESSION_ATTRIBUTE.equals(name))
{
blockProgression = TextHelper.getBlockProgression(this, value);
}
else if (FXG_PADDINGLEFT_ATTRIBUTE.equals(name))
{
paddingLeft = getNumberInherit(this, name, value, PADDING_MIN_INCLUSIVE, PADDING_MAX_INCLUSIVE, paddingLeft.getNumberInheritAsDbl(), "UnknownPaddingLeft");
}
else if (FXG_PADDINGRIGHT_ATTRIBUTE.equals(name))
{
paddingRight = getNumberInherit(this, name, value, PADDING_MIN_INCLUSIVE, PADDING_MAX_INCLUSIVE, paddingRight.getNumberInheritAsDbl(), "UnknownPaddingRight");
}
else if (FXG_PADDINGTOP_ATTRIBUTE.equals(name))
{
paddingTop = getNumberInherit(this, name, value, PADDING_MIN_INCLUSIVE, PADDING_MAX_INCLUSIVE, paddingTop.getNumberInheritAsDbl(), "UnknownPaddingTop");
}
else if (FXG_PADDINGBOTTOM_ATTRIBUTE.equals(name))
{
paddingBottom = getNumberInherit(this, name, value, PADDING_MIN_INCLUSIVE, PADDING_MAX_INCLUSIVE, paddingBottom.getNumberInheritAsDbl(), "UnknownPaddingBottom");
}
else if (FXG_LINEBREAK_ATTRIBUTE.equals(name))
{
lineBreak = TextHelper.getLineBreak(this, value);
}
else if (FXG_COLUMNGAP_ATTRIBUTE.equals(name))
{
columnGap = getNumberInherit(this, name, value, COLUMNGAP_MIN_INCLUSIVE, COLUMNGAP_MAX_INCLUSIVE, columnGap.getNumberInheritAsDbl(), "UnknownColumnGap");
}
else if (FXG_COLUMNCOUNT_ATTRIBUTE.equals(name))
{
columnCount = getNumberAutoInt(this, name, value, COLUMNCOUNT_MIN_INCLUSIVE, COLUMNCOUNT_MAX_INCLUSIVE, columnCount.getNumberAutoAsInt(), "UnknownColumnCount");
}
else if (FXG_COLUMNWIDTH_ATTRIBUTE.equals(name))
{
columnWidth = getNumberAutoDbl(this, name, value, COLUMNWIDTH_MIN_INCLUSIVE, COLUMNWIDTH_MAX_INCLUSIVE, columnWidth.getNumberAutoAsDbl(), "UnknownColumnWidth");
}
else if (FXG_FIRSTBASELINEOFFSET_ATTRIBUTE.equals(name))
{
firstBaselineOffset = getFirstBaselineOffset(this, name, value, BASELINEOFFSET_MIN_INCLUSIVE, BASELINEOFFSET_MAX_INCLUSIVE, firstBaselineOffset.getBaselineOffsetAsDbl());
}
else if (FXG_VERTICALALIGN_ATTRIBUTE.equals(name))
{
verticalAlign = TextHelper.getVerticalAlign(this, value);
}
else if (FXG_TEXTALIGN_ATTRIBUTE.equals(name))
{
textAlign = TextHelper.getTextAlign(this, value);
}
else if (FXG_TEXTALIGNLAST_ATTRIBUTE.equals(name))
{
textAlignLast = TextHelper.getTextAlign(this, value);
}
else if (FXG_TEXTINDENT_ATTRIBUTE.equals(name))
{
textIndent = DOMParserHelper.parseDouble(this, value, name, TEXTINDENT_MIN_INCLUSIVE, TEXTINDENT_MAX_INCLUSIVE, textIndent);
}
else if (FXG_PARAGRAPHSTARTINDENT_ATTRIBUTE.equals(name))
{
paragraphStartIndent = DOMParserHelper.parseDouble(this, value, name, PARAGRAPH_INDENT_MIN_INCLUSIVE, PARAGRAPH_INDENT_MAX_INCLUSIVE, paragraphStartIndent);
}
else if (FXG_PARAGRAPHENDINDENT_ATTRIBUTE.equals(name))
{
paragraphEndIndent = DOMParserHelper.parseDouble(this, value, name, PARAGRAPH_INDENT_MIN_INCLUSIVE, PARAGRAPH_INDENT_MAX_INCLUSIVE, paragraphEndIndent);
}
else if (FXG_PARAGRAPHSPACEBEFORE_ATTRIBUTE.equals(name))
{
paragraphSpaceBefore = DOMParserHelper.parseDouble(this, value, name, PARAGRAPH_SPACE_MIN_INCLUSIVE, PARAGRAPH_SPACE_MAX_INCLUSIVE, paragraphSpaceBefore);
}
else if (FXG_PARAGRAPHSPACEAFTER_ATTRIBUTE.equals(name))
{
paragraphSpaceAfter = DOMParserHelper.parseDouble(this, value, name, PARAGRAPH_SPACE_MIN_INCLUSIVE, PARAGRAPH_SPACE_MAX_INCLUSIVE, paragraphSpaceAfter);
}
else if (FXG_DIRECTION_ATTRIBUTE.equals(name))
{
direction = TextHelper.getDirection(this, value);
}
else if (FXG_JUSTIFICATIONRULE_ATTRIBUTE.equals(name))
{
justificationRule = TextHelper.getJustificationRule(this, value);
}
else if (FXG_JUSTIFICATIONSTYLE_ATTRIBUTE.equals(name))
{
justificationStyle = TextHelper.getJustificationStyle(this, value);
}
else if (FXG_TEXTJUSTIFY_ATTRIBUTE.equals(name))
{
textJustify = TextHelper.getTextJustify(this, value);
}
else if (FXG_LEADINGMODEL_ATTRIBUTE.equals(name))
{
leadingModel = TextHelper.getLeadingModel(this, value);
}
else if (FXG_TABSTOPS_ATTRIBUTE.equals(name))
{
tabStops = TextHelper.parseTabStops(this, value);
}
else if (FXG_FONTFAMILY_ATTRIBUTE.equals(name))
{
fontFamily = value;
}
else if (FXG_FONTSIZE_ATTRIBUTE.equals(name))
{
fontSize = DOMParserHelper.parseDouble(this, value, name, FONTSIZE_MIN_INCLUSIVE, FONTSIZE_MAX_INCLUSIVE, fontSize);
}
else if (FXG_FONTSTYLE_ATTRIBUTE.equals(name))
{
fontStyle = TextHelper.getFontStyle(this, value);
}
else if (FXG_FONTWEIGHT_ATTRIBUTE.equals(name))
{
fontWeight = TextHelper.getFontWeight(this, value);
}
else if (FXG_KERNING_ATTRIBUTE.equals(name))
{
kerning = TextHelper.getKerning(this, value);
}
else if (FXG_LINEHEIGHT_ATTRIBUTE.equals(name))
{
lineHeight = DOMParserHelper.parseNumberPercentWithSeparateRange(this, value, name,
LINEHEIGHT_PIXEL_MIN_INCLUSIVE, LINEHEIGHT_PIXEL_MAX_INCLUSIVE,
LINEHEIGHT_PERCENT_MIN_INCLUSIVE, LINEHEIGHT_PERCENT_MAX_INCLUSIVE, lineHeight);
}
else if (FXG_TEXTDECORATION_ATTRIBUTE.equals(name))
{
textDecoration = TextHelper.getTextDecoration(this, value);
}
else if ( FXG_LINETHROUGH_ATTRIBUTE.equals(name))
{
lineThrough = DOMParserHelper.parseBoolean(this, value, name);
}
else if (FXG_COLOR_ATTRIBUTE.equals(name))
{
color = DOMParserHelper.parseRGB(this, value, name);
}
else if (FXG_TEXTALPHA_ATTRIBUTE.equals(name))
{
textAlpha = DOMParserHelper.parseDouble(this, value, name, ALPHA_MIN_INCLUSIVE, ALPHA_MAX_INCLUSIVE, textAlpha);
}
else if (FXG_WHITESPACECOLLAPSE_ATTRIBUTE.equals(name))
{
whiteSpaceCollapse = TextHelper.getWhiteSpaceCollapse(this, value);
}
else if (FXG_BACKGROUNDALPHA_ATTRIBUTE.equals(name))
{
backgroundAlpha = getAlphaInherit(this, name, value, ALPHA_MIN_INCLUSIVE, ALPHA_MAX_INCLUSIVE, backgroundAlpha.getNumberInheritAsDbl(), "UnknownBackgroundAlpha");
}
else if (FXG_BACKGROUNDCOLOR_ATTRIBUTE.equals(name))
{
backgroundColor = getColorWithEnum(this, name, value);
}
else if (FXG_BASELINESHIFT_ATTRIBUTE.equals(name))
{
baselineShift = getBaselineShift(this, name, value, BASELINESHIFT_MIN_INCLUSIVE, BASELINESHIFT_MAX_INCLUSIVE, baselineShift.getBaselineShiftAsDbl());
}
else if (FXG_BREAKOPPORTUNITY_ATTRIBUTE.equals(name))
{
breakOpportunity = TextHelper.getBreakOpportunity(this, value);
}
else if (FXG_DIGITCASE_ATTRIBUTE.equals(name))
{
digitCase = TextHelper.getDigitCase(this, value);
}
else if (FXG_DIGITWIDTH_ATTRIBUTE.equals(name))
{
digitWidth = TextHelper.getDigitWidth(this, value);
}
else if (FXG_DOMINANTBASELINE_ATTRIBUTE.equals(name))
{
dominantBaseline = TextHelper.getDominantBaseline(this, value);
}
else if (FXG_ALIGNMENTBASELINE_ATTRIBUTE.equals(name))
{
alignmentBaseline = TextHelper.getAlignmentBaseline(this, value);
}
else if (FXG_LIGATURELEVEL_ATTRIBUTE.equals(name))
{
ligatureLevel = TextHelper.getLigatureLevel(this, value);
}
else if (FXG_LOCALE_ATTRIBUTE.equals(name))
{
locale = value;
}
else if (FXG_TYPOGRAPHICCASE_ATTRIBUTE.equals(name))
{
typographicCase = TextHelper.getTypographicCase(this, value);
}
else if (FXG_TRACKINGLEFT_ATTRIBUTE.equals(name))
{
trackingLeft = DOMParserHelper.parseNumberPercent(this, value, name, TRACKING_MIN_INCLUSIVE, TRACKING_MAX_INCLUSIVE, trackingLeft);
}
else if (FXG_TRACKINGRIGHT_ATTRIBUTE.equals(name))
{
trackingRight = DOMParserHelper.parseNumberPercent(this, value, name, TRACKING_MIN_INCLUSIVE, TRACKING_MAX_INCLUSIVE, trackingRight);
}
else if (FXG_TEXTROTATION_ATTRIBUTE.equals(name))
{
textRotation = TextHelper.getTextRotation(this, value);
}
else if (FXG_ID_ATTRIBUTE.equals(name))
{
//id = value;
}
else
{
super.setAttribute(name, value);
}
// Remember that this attribute was set on this node.
rememberAttribute(name, value);
}
//--------------------------------------------------------------------------
//
// Helper Methods
//
//--------------------------------------------------------------------------
/**
* Convert an FXG String value to a BaselineOffset object.
*
* @param value - the FXG String value.
* @param name - the FXG attribute name.
* @param min - the smallest double value that the result must be greater
* or equal to.
* @param max - the largest double value that the result must be smaller
* than or equal to.
* @param defaultValue - the default double value; if the encountered minor
* version is later than the supported minor version and the attribute value
* is out-of-range, the default value is returned.
* @return the matching BaselineOffset rule.
* @throws FXGException if the String did not match a known
* BaselineOffset rule or the value falls out of the specified range
* (inclusive).
*/
private BaselineOffset getFirstBaselineOffset(FXGNode node, String name, String value, double min, double max, double defaultValue)
{
if (FXG_BASELINEOFFSET_AUTO_VALUE.equals(value))
{
return BaselineOffset.newInstance(BaselineOffsetAsEnum.AUTO);
}
else if (FXG_BASELINEOFFSET_ASCENT_VALUE.equals(value))
{
return BaselineOffset.newInstance(BaselineOffsetAsEnum.ASCENT);
}
else if (FXG_BASELINEOFFSET_LINEHEIGHT_VALUE.equals(value))
{
return BaselineOffset.newInstance(BaselineOffsetAsEnum.LINEHEIGHT);
}
else
{
try
{
return BaselineOffset.newInstance(DOMParserHelper.parseDouble(this, value, name, min, max, defaultValue));
}
catch(FXGException e)
{
//Exception: Unknown first baseline offset: {0}
throw new FXGException(node.getStartLine(), node.getStartColumn(), "UnknownFirstBaselineOffset", value);
}
}
}
/**
* Convert an FXG String value to a NumberAuto object.
*
* @param value - the FXG String value.
* @param name - the FXG attribute name.
* @param min - the smallest double value that the result must be greater
* or equal to.
* @param max - the largest double value that the result must be smaller
* than or equal to.
* @param defaultValue - the default double value; if the encountered minor
* version is later than the supported minor version and the attribute value
* is out-of-range, the default value is returned.
* @param errorCode - the error code if value is out-of-range.
* @return the matching NumberAuto rule.
* @throws FXGException if the String did not match a known
* NumberAuto rule.
*/
private NumberAuto getNumberAutoDbl(FXGNode node, String name, String value, double min, double max, double defaultValue, String errorCode)
{
try
{
return NumberAuto.newInstance(DOMParserHelper.parseDouble(this, value, name, min, max, defaultValue));
}catch(FXGException e)
{
if (FXG_NUMBERAUTO_AUTO_VALUE.equals(value))
return NumberAuto.newInstance(NumberAutoAsEnum.AUTO);
else if (FXG_INHERIT_VALUE.equals(value))
return NumberAuto.newInstance(NumberAutoAsEnum.INHERIT);
else
//Exception: Unknown number auto: {0}
throw new FXGException(node.getStartLine(), node.getStartColumn(), errorCode, value);
}
}
/**
* Convert an FXG String value to a NumberAuto object.
*
* @param value - the FXG String value.
* @param name - the FXG attribute name.
* @param min - the smallest int value that the result must be greater
* or equal to.
* @param max - the largest int value that the result must be smaller
* than or equal to.
* @param defaultValue - the default int value; if the encountered minor
* version is later than the supported minor version and the attribute value
* is out-of-range, the default value is returned.
* @param errorCode - the error code if value is out-of-range.
* @return the matching NumberAuto rule.
* @throws FXGException if the String did not match a known
* NumberAuto rule.
*/
private NumberAuto getNumberAutoInt(FXGNode node, String name, String value, int min, int max, int defaultValue, String errorCode)
{
try
{
return NumberAuto.newInstance(DOMParserHelper.parseInt(this, value, name, min, max, defaultValue));
}catch(FXGException e)
{
if (FXG_NUMBERAUTO_AUTO_VALUE.equals(value))
return NumberAuto.newInstance(NumberAutoAsEnum.AUTO);
else if (FXG_INHERIT_VALUE.equals(value))
return NumberAuto.newInstance(NumberAutoAsEnum.INHERIT);
else
//Exception: Unknown number auto: {0}
throw new FXGException(node.getStartLine(), node.getStartColumn(), errorCode, value);
}
}
/**
* Convert an FXG String value to a NumberInherit enumeration.
*
* @param value - the FXG String value.
* @param name - the FXG attribute name.
* @param min - the smallest double value that the result must be greater
* or equal to.
* @param max - the largest double value that the result must be smaller
* than or equal to.
* @param defaultValue - the default double value; if the encountered minor
* version is later than the supported minor version and the attribute value
* is out-of-range, the default value is returned.
* @param errorCode - the error code if value is out-of-range.
* @return the matching NumberInherit rule.
* @throws FXGException if the String did not match a known
* NumberInherit rule or the value falls out of the specified range
* (inclusive).
*/
private NumberInherit getNumberInherit(FXGNode node, String name, String value, double min, double max, double defaultValue, String errorCode)
{
try
{
return NumberInherit.newInstance(DOMParserHelper.parseDouble(this, value, name, min, max, defaultValue));
}catch(FXGException e)
{
if (FXG_INHERIT_VALUE.equals(value))
return NumberInherit.newInstance(NumberInheritAsEnum.INHERIT);
else
//Exception: Unknown number inherit: {0}
throw new FXGException(node.getStartLine(), node.getStartColumn(), errorCode, value);
}
}
/**
* Convert an FXG String value to a BaselineShift enumeration.
*
* @param value - the FXG String value.
* @param name - the FXG attribute name.
* @param min - the smallest double value that the result must be greater
* or equal to.
* @param max - the largest double value that the result must be smaller
* than or equal to.
* @param defaultValue - the default double value; if the encountered minor
* version is later than the supported minor version and the attribute value
* is out-of-range, the default value is returned.
* @return the matching BaselineShift rule.
* @throws FXGException if the String did not match a known
* BaselineShift rule or the value falls out of the specified range
* (inclusive).
*/
private BaselineShift getBaselineShift(FXGNode node, String name, String value, double min, double max, double defaultValue)
{
try
{
return BaselineShift.newInstance(DOMParserHelper.parseNumberPercent(this, value, name, min, max, defaultValue));
}
catch(FXGException e)
{
if (FXG_BASELINESHIFT_SUPERSCRIPT_VALUE.equals(value))
{
return BaselineShift.newInstance(BaselineShiftAsEnum.SUPERSCRIPT);
}
else if (FXG_BASELINESHIFT_SUBSCRIPT_VALUE.equals(value))
{
return BaselineShift.newInstance(BaselineShiftAsEnum.SUBSCRIPT);
}
else
{
//Exception: Unknown baseline shift: {0}
throw new FXGException(node.getStartLine(), node.getStartColumn(), "UnknownBaselineShift", value);
}
}
}
/**
* Convert an FXG String value to a NumberInherit object.
*
* @param value - the FXG String value.
* @param name - the FXG attribute name.
* @param min - the smallest double value that the result must be greater
* or equal to.
* @param max - the largest double value that the result must be smaller
* than or equal to.
* @param defaultValue - the default double value; if the encountered minor
* version is later than the supported minor version and the attribute value
* is out-of-range, the default value is returned.
* @param errorCode - the error code if value is out-of-range.
* @return the matching NumberInherit rule.
* @throws FXGException if the String did not match a known
* NumberInherit rule.
*/
private NumberInherit getAlphaInherit(FXGNode node, String name, String value, double min, double max, double defaultValue, String errorCode)
{
try
{
return NumberInherit.newInstance(DOMParserHelper.parseDouble(this, value, name, ALPHA_MIN_INCLUSIVE, ALPHA_MAX_INCLUSIVE, defaultValue));
}catch(FXGException e)
{
if (FXG_INHERIT_VALUE.equals(value))
{
return NumberInherit.newInstance(NumberInheritAsEnum.INHERIT);
}
else
{
//Exception: Unknown number inherit: {0}
throw new FXGException(node.getStartLine(), node.getStartColumn(), errorCode, value);
}
}
}
/**
* Convert an FXG String value to a NumberInherit object.
*
* @param node - the FXG node.
* @param attribute - the FXG attribute name.
* @param value - the FXG String value.
* @return the matching NumberInherit rule.
* @throws FXGException if the String did not match a known
* NumberInherit rule.
*/
private ColorWithEnum getColorWithEnum(FXGNode node, String attribute, String value)
{
if (FXG_COLORWITHENUM_TRANSPARENT_VALUE.equals(value))
{
return ColorWithEnum.newInstance(ColorEnum.TRANSPARENT);
}
else if (FXG_INHERIT_VALUE.equals(value))
{
return ColorWithEnum.newInstance(ColorEnum.INHERIT);
}
else
{
return ColorWithEnum.newInstance(DOMParserHelper.parseRGB(this, value, attribute));
}
}
}