/* * * 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 org.apache.flex.compiler.internal.fxg.dom.richtext; import static org.apache.flex.compiler.fxg.FXGConstants.*; import java.util.Collection; import org.apache.flex.compiler.fxg.dom.IFXGNode; import org.apache.flex.compiler.internal.fxg.dom.AbstractFXGNode; import org.apache.flex.compiler.internal.fxg.dom.DOMParserHelper; import org.apache.flex.compiler.internal.fxg.dom.types.AlignmentBaseline; import org.apache.flex.compiler.internal.fxg.dom.types.BaselineShift; import org.apache.flex.compiler.internal.fxg.dom.types.BreakOpportunity; import org.apache.flex.compiler.internal.fxg.dom.types.ColorWithEnum; import org.apache.flex.compiler.internal.fxg.dom.types.DigitCase; import org.apache.flex.compiler.internal.fxg.dom.types.DigitWidth; import org.apache.flex.compiler.internal.fxg.dom.types.DominantBaseline; import org.apache.flex.compiler.internal.fxg.dom.types.FontStyle; import org.apache.flex.compiler.internal.fxg.dom.types.FontWeight; import org.apache.flex.compiler.internal.fxg.dom.types.Kerning; import org.apache.flex.compiler.internal.fxg.dom.types.LigatureLevel; import org.apache.flex.compiler.internal.fxg.dom.types.NumberInherit; import org.apache.flex.compiler.internal.fxg.dom.types.TextDecoration; import org.apache.flex.compiler.internal.fxg.dom.types.TextRotation; import org.apache.flex.compiler.internal.fxg.dom.types.TypographicCase; import org.apache.flex.compiler.internal.fxg.dom.types.WhiteSpaceCollapse; import org.apache.flex.compiler.internal.fxg.dom.types.BaselineShift.BaselineShiftAsEnum; import org.apache.flex.compiler.internal.fxg.dom.types.ColorWithEnum.ColorEnum; import org.apache.flex.compiler.internal.fxg.dom.types.NumberInherit.NumberInheritAsEnum; import org.apache.flex.compiler.problems.FXGUnknownAttributeValueProblem; import org.apache.flex.compiler.problems.ICompilerProblem; /** * A base text left node class that have character formatting. */ public abstract class AbstractRichTextLeafNode extends AbstractRichTextNode { protected static final double FONTSIZE_MIN_INCLUSIVE = 1.0; protected static final double FONTSIZE_MAX_INCLUSIVE = 720.0; protected static final double BASELINESHIFT_MIN_INCLUSIVE = -1000.0; protected static final double BASELINESHIFT_MAX_INCLUSIVE = 1000.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 TRACKING_MIN_INCLUSIVE = -1000.0; protected static final double TRACKING_MAX_INCLUSIVE = 1000.0; //-------------------------------------------------------------------------- // // Attributes // //-------------------------------------------------------------------------- // Text Leaf Attributes public String fontFamily = "Arial"; public double fontSize = 12.0; public FontStyle fontStyle = FontStyle.NORMAL; public FontWeight fontWeight = FontWeight.NORMAL; public Kerning kerning = Kerning.AUTO; public double lineHeight = 120.0; public TextDecoration textDecoration = TextDecoration.NONE; public boolean lineThrough = false; public int color = AbstractFXGNode.COLOR_BLACK; public double textAlpha = 1.0; public WhiteSpaceCollapse whiteSpaceCollapse = WhiteSpaceCollapse.COLLAPSE; public NumberInherit backgroundAlpha = NumberInherit.newInstance(1.0);; public ColorWithEnum backgroundColor = ColorWithEnum.newInstance(ColorEnum.TRANSPARENT); public BaselineShift baselineShift = BaselineShift.newInstance(0.0); public BreakOpportunity breakOpportunity = BreakOpportunity.AUTO; public DigitCase digitCase = DigitCase.DEFAULT; public DigitWidth digitWidth = DigitWidth.DEFAULT; public DominantBaseline dominantBaseline = DominantBaseline.AUTO; public AlignmentBaseline alignmentBaseline = AlignmentBaseline.USEDOMINANTBASELINE; public LigatureLevel ligatureLevel = LigatureLevel.COMMON; public String locale = "en"; public TypographicCase typographicCase = TypographicCase.DEFAULT; public double trackingLeft = 0.0; public double trackingRight = 0.0; public TextRotation textRotation = TextRotation.AUTO; /** * This implementation processes text leaf attributes that are common to * <RichText>, <p>, and <span>. * <p> * The right hand side of an ActionScript assignment is generated for * each property based on the expected type of the attribute. * </p> * <p> * Text leaf attributes include: * <ul> * <li><b>fontFamily</b> (String): The font family name used to render the * text. Default value is Times New Roman (Times on Mac OS X).</li> * <li><b>fontSize</b> (Number): The size of the glyphs that is used to * render the text, specified in point sizes. Default is 12. Minimum 1 * point. Maximum 500 points.</li> * <li><b>fontStyle</b> (String): [normal, italic] 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): [normal, bold] The boldness or lightness * of the glyphs that is used to render the text. Default is normal.</li> * <li><b>lineHeight</b> (Percent | Number): The leading, or the distance * from the previous line's baseline to this one, in points. Default is * 120%. Minimum value for percent or number is 0.</li> * <li><b>tracking</b> (Percent): Space added to the advance after each * character, as a percentage of the current point size. Percentages can be * negative, to bring characters closer together. Default is 0.</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 0x000000.</li> * <li><b>textAlpha</b> (alpha): 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.</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.</li> * </ul> * </p> * @param name - the unqualified attribute name * @param value - the attribute value */ @Override public void setAttribute(String name, String value, Collection<ICompilerProblem> problems) { 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, problems); } else if (FXG_FONTSTYLE_ATTRIBUTE.equals(name)) { fontStyle = TextHelper.getFontStyle(this, value, fontStyle, problems); } else if (FXG_FONTWEIGHT_ATTRIBUTE.equals(name)) { fontWeight = TextHelper.getFontWeight(this, value, fontWeight, problems); } else if (FXG_KERNING_ATTRIBUTE.equals(name)) { kerning = TextHelper.getKerning(this, value, kerning, problems); } 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, problems); } else if (FXG_TEXTDECORATION_ATTRIBUTE.equals(name)) { textDecoration = TextHelper.getTextDecoration(this, value, textDecoration, problems); } else if ( FXG_LINETHROUGH_ATTRIBUTE.equals(name)) { lineThrough = DOMParserHelper.parseBoolean(this, value, name, lineThrough, problems); } else if (FXG_COLOR_ATTRIBUTE.equals(name)) { color = DOMParserHelper.parseRGB(this, value, name, color, problems); } else if (FXG_TEXTALPHA_ATTRIBUTE.equals(name)) { textAlpha = DOMParserHelper.parseDouble(this, value, name, ALPHA_MIN_INCLUSIVE, ALPHA_MAX_INCLUSIVE, textAlpha, problems); } else if (FXG_WHITESPACECOLLAPSE_ATTRIBUTE.equals(name)) { whiteSpaceCollapse = TextHelper.getWhiteSpaceCollapse(this, value, whiteSpaceCollapse, problems); } else if (FXG_BACKGROUNDALPHA_ATTRIBUTE.equals(name)) { backgroundAlpha = getAlphaInherit(this, name, value, ALPHA_MIN_INCLUSIVE, ALPHA_MAX_INCLUSIVE, backgroundAlpha.getNumberInheritAsDbl(), problems); } else if (FXG_BACKGROUNDCOLOR_ATTRIBUTE.equals(name)) { backgroundColor = getColorWithEnum(this, name, value, backgroundColor.getColorWithEnumAsString(), problems); } else if (FXG_BASELINESHIFT_ATTRIBUTE.equals(name)) { baselineShift = getBaselineShift(this, name, value, BASELINESHIFT_MIN_INCLUSIVE, BASELINESHIFT_MAX_INCLUSIVE, baselineShift.getBaselineShiftAsDbl(), problems); } else if (FXG_BREAKOPPORTUNITY_ATTRIBUTE.equals(name)) { breakOpportunity = TextHelper.getBreakOpportunity(this, value, breakOpportunity, problems); } else if (FXG_DIGITCASE_ATTRIBUTE.equals(name)) { digitCase = TextHelper.getDigitCase(this, value, digitCase, problems); } else if (FXG_DIGITWIDTH_ATTRIBUTE.equals(name)) { digitWidth = TextHelper.getDigitWidth(this, value, digitWidth, problems); } else if (FXG_DOMINANTBASELINE_ATTRIBUTE.equals(name)) { dominantBaseline = TextHelper.getDominantBaseline(this, value, dominantBaseline, problems); } else if (FXG_ALIGNMENTBASELINE_ATTRIBUTE.equals(name)) { alignmentBaseline = TextHelper.getAlignmentBaseline(this, value, alignmentBaseline, problems); } else if (FXG_LIGATURELEVEL_ATTRIBUTE.equals(name)) { ligatureLevel = TextHelper.getLigatureLevel(this, value, ligatureLevel, problems); } else if (FXG_LOCALE_ATTRIBUTE.equals(name)) { locale = value; } else if (FXG_TYPOGRAPHICCASE_ATTRIBUTE.equals(name)) { typographicCase = TextHelper.getTypographicCase(this, value, typographicCase, problems); } else if (FXG_TRACKINGLEFT_ATTRIBUTE.equals(name)) { trackingLeft = DOMParserHelper.parseNumberPercent(this, value, name, TRACKING_MIN_INCLUSIVE, TRACKING_MAX_INCLUSIVE, trackingLeft, problems); } else if (FXG_TRACKINGRIGHT_ATTRIBUTE.equals(name)) { trackingRight = DOMParserHelper.parseNumberPercent(this, value, name, TRACKING_MIN_INCLUSIVE, TRACKING_MAX_INCLUSIVE, trackingRight, problems); } else if (FXG_TEXTROTATION_ATTRIBUTE.equals(name)) { textRotation = TextHelper.getTextRotation(this, value, textRotation, problems); } else { super.setAttribute(name, value, problems); return; } // Remember attribute was set on this node. rememberAttribute(name, value); } //-------------------------------------------------------------------------- // // Helper Methods // //-------------------------------------------------------------------------- /** * 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. * @param problems problem collection used to collect problems occurred within this method * @return the matching BaselineShift rule. */ private BaselineShift getBaselineShift(IFXGNode node, String name, String value, double min, double max, double defaultValue, Collection<ICompilerProblem> problems) { try { return BaselineShift.newInstance(DOMParserHelper.parseNumberPercent(this, value, min, max)); } catch(Exception 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); } //Unknown baseline shift. problems.add(new FXGUnknownAttributeValueProblem(node.getDocumentPath(), node.getStartLine(), node.getStartColumn(), name, value)); return BaselineShift.newInstance(defaultValue); } } /** * 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 problems problem collection used to collect problems occurred within this method * @return the matching NumberInherit rule. */ private NumberInherit getAlphaInherit(IFXGNode node, String name, String value, double min, double max, double defaultValue, Collection<ICompilerProblem> problems) { try { return NumberInherit.newInstance(DOMParserHelper.parseDouble(this, value, ALPHA_MIN_INCLUSIVE, ALPHA_MAX_INCLUSIVE)); } catch(Exception e) { if (FXG_INHERIT_VALUE.equals(value)) { return NumberInherit.newInstance(NumberInheritAsEnum.INHERIT); } //Unknown number inherit problems.add(new FXGUnknownAttributeValueProblem(node.getDocumentPath(), node.getStartLine(), node.getStartColumn(), name, value)); return NumberInherit.newInstance(defaultValue); } } /** * 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. * @param defaultValue - default color value * @param problems problem collection used to collect problems occurred within this method * @return the matching NumberInherit rule. */ private ColorWithEnum getColorWithEnum(IFXGNode node, String attribute, String value, int defaultValue, Collection<ICompilerProblem> problems) { 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, defaultValue, problems)); } } }