/* * Copyright 2014-15 Skynav, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY SKYNAV, INC. AND ITS CONTRIBUTORS “AS IS” AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL SKYNAV, INC. OR ITS CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.skynav.ttpe.style; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import com.skynav.ttpe.fonts.Combination; import com.skynav.ttpe.fonts.Font; import com.skynav.ttpe.fonts.FontCache; import com.skynav.ttpe.fonts.FontFeature; import com.skynav.ttpe.fonts.FontKerning; import com.skynav.ttpe.fonts.FontStyle; import com.skynav.ttpe.fonts.FontWeight; import com.skynav.ttpe.fonts.Orientation; import com.skynav.ttpe.geometry.Axis; import com.skynav.ttpe.geometry.Direction; import com.skynav.ttpe.geometry.Extent; import com.skynav.ttpe.geometry.WritingMode; import com.skynav.ttpe.util.Characters; import com.skynav.ttv.model.value.FontFamily; import com.skynav.ttv.model.value.FontVariant; import com.skynav.ttv.model.value.Length; import com.skynav.ttv.util.Location; import com.skynav.ttv.util.StyleSet; import com.skynav.ttv.util.StyleSpecification; import com.skynav.ttv.verifier.util.Fonts; import com.skynav.ttv.verifier.util.Keywords; import com.skynav.ttv.verifier.util.Lengths; import com.skynav.ttv.verifier.util.MixedUnitsTreatment; import com.skynav.ttv.verifier.util.NegativeTreatment; import com.skynav.ttv.verifier.util.QuotedGenericFontFamilyTreatment; import com.skynav.ttx.transformer.TransformerContext; import com.skynav.xml.helpers.Documents; import static com.skynav.ttpe.geometry.Dimension.*; import static com.skynav.ttpe.style.Constants.*; import static com.skynav.ttpe.text.Constants.*; public class StyleCollector { private StyleCollector parent; // parent style collector (or null if no parent) private TransformerContext context; // transformation context private FontCache fontCache; // font state cache private Defaults defaults; // style defaults state private Extent extBounds; // extent of outer (canvas) viewport context, remains constant during collection private Extent refBounds; // extent of nearest viewport context, remains constant during collection private Extent cellResolution; // cell resolution of collector context, remains constant during collection private WritingMode writingMode; // writing mode of collector context, remains constant during collection private String language; // language of collector context, remains constant during collection private Font font; // font of current element being collected private int synthesizedStylesIndex; // index of next synthesized style identifier private Map<String,StyleSet> styles; // map of all isd:css style sets, identified by id string private List<StyleAttributeInterval> attributes; // text attributes being collected private BidiLevelIterator bidi; // bidi iterator, reused as needed public StyleCollector(StyleCollector sc) { this(sc, sc.context, sc.fontCache, sc.defaults, sc.extBounds, sc.refBounds, sc.cellResolution, sc.writingMode, sc.language, sc.font, sc.styles); } public StyleCollector (StyleCollector parent, TransformerContext context, FontCache fontCache, Defaults defaults, Extent extBounds, Extent refBounds, Extent cellResolution, WritingMode writingMode, String language, Font font, Map<String,StyleSet> styles) { this.parent = parent; this.context = context; this.defaults = defaults; this.fontCache = fontCache; this.extBounds = extBounds; this.refBounds = refBounds; this.cellResolution = cellResolution; this.writingMode = writingMode; this.language = language; this.font = font; this.styles = styles; this.bidi = new BidiLevelIterator(); } public StyleCollector getParent() { return parent; } public TransformerContext getContext() { for (StyleCollector sc = this; sc != null; sc = sc.getParent()) { if (sc.context != null) return sc.context; } return null; } public FontCache getFontCache() { return fontCache; } public Defaults getDefaults() { return defaults; } protected void setFont(Font font) { this.font = font; } public Font getFont() { return this.font; } public Extent getExternalBounds() { return extBounds; } public Extent getReferenceBounds() { return refBounds; } public Extent getCellResolution() { return cellResolution; } public void clear() { if (attributes != null) attributes.clear(); } public boolean isDisplayed(Element e) { Display display; StyleSpecification s = getStyles(e).get(ttsDisplayAttrName); if (s != null) { String v = s.getValue(); display = Display.valueOf(v.toUpperCase()); } else display = getDefaults().getDisplay(); return display != Display.NONE; } public boolean generatesAnnotationBlock(Element e) { if (Documents.isElement(e, ttSpanElementName)) { Annotation r = getAnnotation(e); return (r != null) && ((r == Annotation.CONTAINER) || (r == Annotation.EMPHASIS)); } else return false; } public Annotation getAnnotation(Element e) { StyleSet styles = getStyles(e); StyleSpecification s; s = styles.get(ttsRubyAttrName); if (s != null) return Annotation.fromValue(s.getValue()); s = styles.get(ttsTextEmphasisAttrName); if (s != null) return Annotation.EMPHASIS; return null; } public boolean generatesInlineBlock(Element e) { if (!Documents.isElement(e, ttSpanElementName)) return false; else { StyleSet styles = getStyles(e); if (styles.get(ttsTextAlignAttrName) != null) return true; else if (styles.get(ttsIPDAttrName) != null) return true; else if (styles.get(ttsBPDAttrName) != null) return true; else return false; } } public void collectParagraphStyles(Element e) { StyleSet styles = getStyles(e); int begin = -1; int end = -1; // collect common styles collectCommonStyles(e, begin, end, styles); // collect paragraph styles StyleSpecification s; Object v; // ANNOTATION_RESERVE s = styles.get(ttsRubyReserveAttrName); v = null; if (s != null) { com.skynav.ttv.model.value.TextReserve[] retReserve = new com.skynav.ttv.model.value.TextReserve[1]; if (com.skynav.ttv.verifier.util.Reserve.isReserve(s.getValue(), new Location(), context, retReserve)) { com.skynav.ttv.model.value.TextReserve ar = retReserve[0]; Extent fs = (font != null) ? font.getSize() : Extent.UNIT; Length reserve = ar.getReserve(); double r = (reserve != null) ? Helpers.resolveLength(e, reserve, Axis.VERTICAL, extBounds, refBounds, fs, cellResolution) : -1; v = new AnnotationReserve(ar.getPosition().name(), r); } } if (v != null) addAttribute(StyleAttribute.ANNOTATION_RESERVE, v, begin, end); // BLOCK_ALIGNMENT s = styles.get(ttsDisplayAlignAttrName); v = null; if (s != null) v = BlockAlignment.valueOf(s.getValue().toUpperCase()); if (v != null) addAttribute(StyleAttribute.BLOCK_ALIGNMENT, v, begin, end); } public void collectSpanStyles(Element e, int begin, int end) { assert (begin < 0) || (end - begin) > 0; StyleSet styles = getStyles(e); // collect common styles collectCommonStyles(e, begin, end); // collect non-common span styles StyleSpecification s; Object v; // BACKGROUND_COLOR (paragraph is handled as block presentation state, not outer text style state) s = styles.get(ttsBackgroundColorAttrName); v = null; if (s != null) { com.skynav.ttv.model.value.Color[] retColor = new com.skynav.ttv.model.value.Color[1]; if (com.skynav.ttv.verifier.util.Colors.isColor(s.getValue(), new Location(), context, retColor)) v = new Color(retColor[0].getRed(), retColor[0].getGreen(), retColor[0].getBlue(), retColor[0].getAlpha()); } if (v != null) addAttribute(StyleAttribute.BACKGROUND_COLOR, v, begin, end); // LANGUAGE String xmlLang = Documents.getAttribute(e, xmlLanguageAttrName, null); v = null; if (xmlLang != null) v = xmlLang; if (v != null) addAttribute(StyleAttribute.LANGUAGE, v, begin, end); // WHITESPACE String xmlSpace = Documents.getAttribute(e, xmlSpaceAttrName, null); v = null; if (xmlSpace != null) v = Whitespace.valueOf(xmlSpace.toUpperCase()); if (v != null) addAttribute(StyleAttribute.WHITESPACE, v, begin, end); } public void maybeWrapWithBidiControls(Element e) { StyleSet styles = getStyles(e); // style values StyleSpecification s; String v; // direction Direction d = null; s = styles.get(ttsDirectionAttrName); if (s != null) { v = s.getValue().toLowerCase(); if (v.equals("ltr")) d = Direction.LR; else if (v.equals("rtl")) d = Direction.RL; } // unicode bidi UnicodeBidi u = null; s = styles.get(ttsUnicodeBidiAttrName); if (s != null) { v = s.getValue().toLowerCase(); if (v.equals("normal")) u = UnicodeBidi.NORMAL; else if (v.equals("embed")) u = UnicodeBidi.EMBED; else if (v.equals("bidioverride")) u = UnicodeBidi.OVERRIDE; } int c = 0; if ((u != null) && (u != UnicodeBidi.NORMAL) && (d != null)) { if (u == UnicodeBidi.EMBED) { if (d == Direction.RL) c = Characters.UC_RLE; else if (d == Direction.LR) c = Characters.UC_LRE; } else if (u == UnicodeBidi.OVERRIDE) { if (d == Direction.RL) c = Characters.UC_RLO; else if (d == Direction.LR) c = Characters.UC_LRO; } } if (c != 0) { NodeList children = e.getChildNodes(); if (children.getLength() == 1) { Node n = children.item(0); if (n instanceof Text) { Text t = (Text) n; StringBuffer sb = new StringBuffer(); sb.append((char) c); sb.append(t.getWholeText()); sb.append((char) Characters.UC_PDF); t.replaceWholeText(sb.toString()); } } else { // [TBD] handle multiple children } } } public void collectContentStyles(String content, int begin, int end) { int contentLength = content.length(); if (begin < 0) begin = 0; if (begin > contentLength) begin = contentLength; if ((end < 0) || (end > contentLength)) end = contentLength; if (begin > end) begin = end; collectContentOrientation(content, begin, end); collectContentBidiLevels(content, begin, end); } public void collectContentOrientation(String content, int begin, int end) { if (isVertical()) { int lastBegin = begin; int lastEnd = lastBegin; Orientation lastOrientation = null; for (int i = begin, n = end; i < n; ++i) { int c = content.charAt(i); Orientation o = Orientation.fromCharacter(c); if (o != lastOrientation) { if ((lastOrientation != null) && (lastOrientation != getDefaults().getOrientation()) && (lastEnd > lastBegin)) addAttribute(StyleAttribute.ORIENTATION, lastOrientation, lastBegin, i); lastOrientation = o; lastBegin = i; } lastEnd = i + 1; } if (lastBegin < end) { if ((lastOrientation != null) && (lastOrientation != getDefaults().getOrientation()) && (lastEnd > lastBegin)) addAttribute(StyleAttribute.ORIENTATION, lastOrientation, lastBegin, lastEnd); } } } private boolean isVertical() { return writingMode.getAxis(IPD) == Axis.VERTICAL; } protected void collectContentBidiLevels(String content, int begin, int end) { int defaultLevel = getDefaultBidiLevel(); int index = 0; for (int limit : getBidiRunLimits(begin, end)) { if (index >= end) break; else if (limit > index) collectContentBidiLevels(content, index, limit, defaultLevel); index = limit; } } private int getDefaultBidiLevel() { if (writingMode.getAxis(IPD) == Axis.VERTICAL) return 0; else return (writingMode.getDirection(IPD) == Direction.RL) ? 1 : 0; } private static final Set<StyleAttribute> bidiRunBreakingAttributes; static { Set<StyleAttribute> s = new java.util.HashSet<StyleAttribute>(); s.add(StyleAttribute.ORIENTATION); bidiRunBreakingAttributes = Collections.unmodifiableSet(s); } private int[] getBidiRunLimits(int begin, int end) { Set<Integer> limits = new java.util.TreeSet<Integer>(); limits.add(begin); for (StyleAttributeInterval i : getIntervals(bidiRunBreakingAttributes)) { int b = i.getBegin(); if ((b >= begin) && (b < end)) limits.add(b); int e = i.getEnd(); if ((e > begin) && (e <= end)) limits.add(e); } limits.add(end); int[] la = new int[limits.size()]; int i = 0; for (int l : limits) la[i++] = l; return la; } protected void collectContentBidiLevels(String content, int begin, int end, int defaultLevel) { BidiLevelIterator bi = bidi.setParagraph(content.substring(begin, end), defaultLevel); int lastBegin = begin; int lastLevel = -1; int maxLevel = lastLevel; for (int i = bi.first(); i != BidiLevelIterator.DONE; i = bi.next()) { int level = bi.level(); if (level != lastLevel) { if (lastLevel >= 0) { addAttribute(StyleAttribute.BIDI, Integer.valueOf(lastLevel), lastBegin, i); } lastLevel = level; lastBegin = i; } if (level > maxLevel) maxLevel = level; } if (lastBegin < end) { if ((lastLevel >= 0) && (maxLevel > 0)) addAttribute(StyleAttribute.BIDI, Integer.valueOf(lastLevel), lastBegin, end); } } public void addEmbedding(Object object, int begin, int end) { addAttribute(StyleAttribute.EMBEDDING, object, begin, end); } public List<StyleAttributeInterval> extract() { List<StyleAttributeInterval> attributes = this.attributes; this.attributes = null; return attributes; } public StyleSet addStyles(Element e) { StyleSet styles = getStyles(e); if (styles == StyleSet.EMPTY) styles = newStyles(e); Documents.setAttribute(e, isdCSSAttrName, styles.getId()); return styles; } protected void collectCommonStyles(Element e, int begin, int end) { collectCommonStyles(e, begin, end, getStyles(e)); } protected void collectCommonStyles(Element e, int begin, int end, StyleSet styles) { assert (begin < 0) || (end - begin) > 0; StyleSpecification s; Object v; // COLOR Color color = null; s = styles.get(ttsColorAttrName); v = null; if (s != null) { com.skynav.ttv.model.value.Color[] retColor = new com.skynav.ttv.model.value.Color[1]; if (com.skynav.ttv.verifier.util.Colors.isColor(s.getValue(), new Location(), context, retColor)) v = color = new Color(retColor[0].getRed(), retColor[0].getGreen(), retColor[0].getBlue(), retColor[0].getAlpha()); } if (v != null) addAttribute(StyleAttribute.COLOR, v, begin, end); // FONT collectCommonFontStyles(e, begin, end, styles); // PADDING s = styles.get(ttsPaddingAttrName); v = null; if (s != null) { double[] padding = null; if (!Keywords.isAuto(s.getValue())) { Integer[] minMax = new Integer[] { 1, 4 }; Object[] treatments = new Object[] { NegativeTreatment.Error, MixedUnitsTreatment.Allow }; List<Length> lengths = new java.util.ArrayList<Length>(); if (Lengths.isLengths(s.getValue(), new Location(), context, minMax, treatments, lengths)) { Length[] la = lengths.toArray(new Length[lengths.size()]); Extent fs = (font != null) ? font.getSize() : Extent.UNIT; padding = Helpers.resolvePadding(e, la, writingMode, extBounds, refBounds, fs, cellResolution); } } if (padding != null) v = new Padding(padding); } if (v != null) addAttribute(StyleAttribute.PADDING, v, begin, end); // TEXT_ALIGN s = styles.get(ttsTextAlignAttrName); v = null; if (s != null) v = InlineAlignment.fromValue(s.getValue()); if (v != null) addAttribute(StyleAttribute.INLINE_ALIGNMENT, v, begin, end); // TEXT_COMBINE s = styles.get(ttsTextCombineAttrName); v = null; if (s != null) { com.skynav.ttv.model.value.TextCombine[] retCombine = new com.skynav.ttv.model.value.TextCombine[1]; if (com.skynav.ttv.verifier.util.Combine.isCombine(s.getValue(), new Location(), context, retCombine)) { com.skynav.ttv.model.value.TextCombine tc = retCombine[0]; v = new Combination(tc.getStyle().name(), tc.getCount()); } } if (v != null) addAttribute(StyleAttribute.COMBINATION, v, begin, end); // TEXT_EMPHASIS s = styles.get(ttsTextEmphasisAttrName); v = null; if (s != null) { com.skynav.ttv.model.value.TextEmphasis[] retEmphasis = new com.skynav.ttv.model.value.TextEmphasis[1]; if (com.skynav.ttv.verifier.util.Emphasis.isEmphasis(s.getValue(), new Location(), context, retEmphasis)) { com.skynav.ttv.model.value.TextEmphasis te = retEmphasis[0]; com.skynav.ttv.model.value.Color teColor = te.getColor(); Color c = (teColor != null) ? new Color(teColor.getRed(), teColor.getGreen(), teColor.getBlue(), teColor.getAlpha()) : color; v = new Emphasis(te.getStyle().name(), te.getText(), te.getPosition().name(), c); } } if (v != null) addAttribute(StyleAttribute.EMPHASIS, v, begin, end); // TEXT_OUTLINE s = styles.get(ttsTextOutlineAttrName); v = null; if (s != null) { com.skynav.ttv.model.value.TextOutline[] retOutline = new com.skynav.ttv.model.value.TextOutline[1]; if (com.skynav.ttv.verifier.util.Outline.isOutline(s.getValue(), new Location(), context, retOutline)) { com.skynav.ttv.model.value.TextOutline to = retOutline[0]; com.skynav.ttv.model.value.Color toColor = to.getColor(); Color c = (toColor != null) ? new Color(toColor.getRed(), toColor.getGreen(), toColor.getBlue(), toColor.getAlpha()) : color; Extent fs = (font != null) ? font.getSize() : Extent.UNIT; Length thickness = to.getThickness(); double t = Helpers.resolveLength(e, thickness, Axis.VERTICAL, extBounds, refBounds, fs, cellResolution); Length blur = to.getBlur(); double b = Helpers.resolveLength(e, blur, Axis.VERTICAL, extBounds, refBounds, fs, cellResolution); v = new Outline(c, t, b); } } if (v != null) addAttribute(StyleAttribute.OUTLINE, v, begin, end); // VISIBILITY s = styles.get(ttsVisibilityAttrName); v = null; if (s != null) v = Visibility.valueOf(s.getValue().toUpperCase()); else v = getDefaultVisibility(e, styles); if (v != null) addAttribute(StyleAttribute.VISIBILITY, v, begin, end); // WRAP s = styles.get(ttsWrapOptionAttrName); v = null; if (s != null) v = Wrap.valueOf(s.getValue().toUpperCase()); if (v != null) addAttribute(StyleAttribute.WRAP, v, begin, end); } protected void collectCommonFontStyles(Element e, int begin, int end, StyleSet styles) { collectFontStyle(e, begin, end, styles); collectLineHeightStyle(e, begin, end, styles); } protected void collectFontStyle(Element e, int begin, int end, StyleSet styles) { Font f = getFontFromStyles(e, styles); if (f != null) { addAttribute(StyleAttribute.FONT, f, begin, end); setFont(f); } } protected void collectLineHeightStyle(Element e, int begin, int end, StyleSet styles) { StyleSpecification s = styles.get(ttsLineHeightAttrName); Object v = null; if (s != null) { Extent fs = (font != null) ? font.getSize() : Extent.UNIT; if (Keywords.isNormal(s.getValue())) { v = Double.valueOf(fs.getDimension(Axis.VERTICAL) * 1.25); } else { Integer[] minMax = new Integer[] { 1, 1 }; Object[] treatments = new Object[] { NegativeTreatment.Error, MixedUnitsTreatment.Error }; List<Length> lengths = new java.util.ArrayList<Length>(); if (Lengths.isLengths(s.getValue(), new Location(), context, minMax, treatments, lengths)) { assert lengths.size() == 1; v = Double.valueOf(Helpers.resolveLength(e, lengths.get(0), Axis.VERTICAL, extBounds, refBounds, fs, cellResolution)); } } } if (v != null) addAttribute(StyleAttribute.LINE_HEIGHT, v, begin, end); } private Font getFontFromStyles(Element e, StyleSet styles) { StyleSpecification s; // families List<String> fontFamilies = null; s = styles.get(ttsFontFamilyAttrName); if (s != null) { List<FontFamily> families = new java.util.ArrayList<FontFamily>(); Object[] treatments = new Object[] { QuotedGenericFontFamilyTreatment.Allow }; if (Fonts.isFontFamilies(s.getValue(), new Location(), context, treatments, families)) { if (!families.isEmpty()) { List<String> familyNames = new java.util.ArrayList<String>(); for (FontFamily family : families) familyNames.add(family.toString()); fontFamilies = familyNames; } } } if (fontFamilies == null) fontFamilies = getDefaultFontFamilies(e, styles); // style FontStyle fontStyle = null; s = styles.get(ttsFontStyleAttrName); if (s != null) fontStyle = FontStyle.valueOf(s.getValue().toUpperCase()); if (fontStyle == null) fontStyle = getDefaultFontStyle(e, styles); // weight FontWeight fontWeight = null; s = styles.get(ttsFontWeightAttrName); if (s != null) fontWeight = FontWeight.valueOf(s.getValue().toUpperCase()); if (fontWeight == null) fontWeight = getDefaultFontWeight(e, styles); // size Extent fontSize = null; s = styles.get(ttsFontSizeAttrName); if (s != null) { Extent fs = parseFontSize(e, s); if (fs != null) fontSize = fs; } if (fontSize == null) fontSize = getDefaultFontSize(e, styles); // features Set<FontFeature> fontFeatures = new java.util.HashSet<FontFeature>(); // variant features s = styles.get(ttsFontVariantAttrName); if (s != null) { Set<FontVariant> variants = new java.util.HashSet<FontVariant>(); if (Fonts.isFontVariants(s.getValue(), new Location(), context, variants)) { if (!variants.isEmpty()) { for (FontVariant fv : variants) { FontFeature f = FontFeature.fromVariant(fv); if (f != null) fontFeatures.add(f); } } } } // shear feature s = styles.get(ttsFontShearAttrName); if (s != null) { Integer[] minMax = new Integer[] { 1, 1 }; Object[] treatments = new Object[] { NegativeTreatment.Allow, MixedUnitsTreatment.Error }; List<Length> lengths = new java.util.ArrayList<Length>(); if (Lengths.isLengths(s.getValue(), new Location(), context, minMax, treatments, lengths)) { assert lengths.size() == 1; Length length = lengths.get(0); if (length.getUnits() == Length.Unit.Percentage) fontFeatures.add(new FontFeature("oblq", new Object[]{Double.valueOf(length.getValue() / 100.0)})); } } // kerning feature s = styles.get(ttsFontKerningAttrName); if (s != null) { FontKerning k = FontKerning.valueOf(s.getValue().toUpperCase()); fontFeatures.add(new FontFeature("kern", new Object[]{k})); } if (fontFeatures.isEmpty()) fontFeatures = getDefaultFontFeatures(e, styles); return getFontCache().mapFont(fontFamilies, fontStyle, fontWeight, language, writingMode.getAxis(IPD), fontSize, fontFeatures); } protected StyleSet getStyles(Element e) { String css = Documents.getAttribute(e, isdCSSAttrName, null); return (css != null) ? getStyles(css) : StyleSet.EMPTY; } protected StyleSet getStyles(String css) { assert css != null; String[] ids = css.trim().split("\\s+"); if (ids.length < 1) return StyleSet.EMPTY; else if (ids.length < 2) return this.styles.get(ids[0]); else return mergeStyles(ids); } private StyleSet mergeStyles(String[] ids) { StyleSet styles = new StyleSet(); for (String id : ids) styles.merge(getStyles(id), getContext().getConditionEvaluatorState()); return styles; } protected StyleSet newStyles(Element e) { StyleSet styles = new StyleSet(); String id = generateSynthesizedStylesId(); styles.setId(id); this.styles.put(id, styles); return styles; } private String generateSynthesizedStylesId() { StringBuffer sb = new StringBuffer(); sb.append('_'); sb.append(synthesizedStylesIndex++); return sb.toString(); } protected void addAttribute(StyleAttribute attribute, Object value, int begin, int end) { assert (begin < 0) || (end - begin) > 0; if (attributes == null) attributes = new java.util.ArrayList<StyleAttributeInterval>(); attributes.add(new StyleAttributeInterval(attribute, value, begin, end)); } protected List<StyleAttributeInterval> getIntervals(Set<StyleAttribute> attributes) { List<StyleAttributeInterval> intervals = new java.util.ArrayList<StyleAttributeInterval>(); if (this.attributes != null) { for (StyleAttributeInterval i : this.attributes) { if (attributes.contains(i.getAttribute())) intervals.add(i); } } return intervals; } protected List<StyleAttributeInterval> getIntervals(StyleAttribute attribute) { List<StyleAttributeInterval> intervals = new java.util.ArrayList<StyleAttributeInterval>(); if (this.attributes != null) { for (StyleAttributeInterval i : this.attributes) { if (i.getAttribute().equals(attribute)) intervals.add(i); } } return intervals; } protected List<String> getDefaultFontFamilies(Element e, StyleSet styles) { return getDefaults().getFontFamilies(); } protected FontStyle getDefaultFontStyle(Element e, StyleSet styles) { return getDefaults().getFontStyle(); } protected FontWeight getDefaultFontWeight(Element e, StyleSet styles) { return getDefaults().getFontWeight(); } protected Extent getDefaultFontSize(Element e, StyleSet styles) { return getDefaults().getFontSize(); } protected Set<FontFeature> getDefaultFontFeatures(Element e, StyleSet styles) { return getDefaults().getFontFeatures(); } protected Visibility getDefaultVisibility(Element e, StyleSet styles) { Visibility v = null; boolean forcedDisplay = (Boolean) getContext().getExternalParameters().getParameter("forcedDisplay"); if (forcedDisplay) { if (Documents.isElement(e, ttSpanElementName)) { for (Node n = e.getParentNode(); n != null; n = n.getParentNode()) { if (n instanceof Element) { Element p = (Element) n; StyleSet pStyles = getStyles(p); StyleSpecification s = pStyles.get(ttsVisibilityAttrName); if (s != null) { v = Visibility.valueOf(s.getValue().toUpperCase()); break; } else if (Documents.isElement(p, ttSpanElementName)) { continue; } else { break; } } } } } if (v == null) v = getDefaults().getVisibility(); return v; } protected Extent parseFontSize(Element e, StyleSpecification s) { Integer[] minMax = new Integer[] { 1, 2 }; Object[] treatments = new Object[] { NegativeTreatment.Allow, MixedUnitsTreatment.Allow }; List<Length> lengths = new java.util.ArrayList<Length>(); if (Lengths.isLengths(s.getValue(), new Location(), context, minMax, treatments, lengths)) { assert lengths.size() > 0; Extent fs = (font != null) ? font.getSize() : Extent.UNIT; Extent refBounds = this.refBounds; if (!Documents.isElement(e, isdRegionElementName)) refBounds = fs; double w, h; if (lengths.size() == 1) { h = Helpers.resolveLength(e, lengths.get(0), Axis.VERTICAL, extBounds, refBounds, fs, cellResolution); w = h; } else { w = Helpers.resolveLength(e, lengths.get(0), Axis.HORIZONTAL, extBounds, refBounds, fs, cellResolution); h = Helpers.resolveLength(e, lengths.get(1), Axis.VERTICAL, extBounds, refBounds, fs, cellResolution); } return new Extent(w, h); } else return null; } }