
com.skynav.ttpe.style.StyleCollector Maven / Gradle / Ivy
The newest version!
/*
* 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.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 {
protected TransformerContext context;
protected FontCache fontCache;
protected Defaults defaults;
protected Extent extBounds;
protected Extent refBounds;
protected WritingMode writingMode;
protected String language;
protected Font font;
private int synthesizedStylesIndex;
private Map styles;
private List attributes;
private BidiLevelIterator bidi;
public StyleCollector(StyleCollector sc) {
this(sc.context, sc.fontCache, sc.defaults, sc.extBounds, sc.refBounds, sc.writingMode, sc.language, sc.font, sc.styles);
}
public StyleCollector
(TransformerContext context, FontCache fontCache, Defaults defaults, Extent extBounds, Extent refBounds, WritingMode writingMode, String language, Font font, Map styles) {
this.context = context;
this.fontCache = fontCache;
this.defaults = defaults;
this.extBounds = extBounds;
this.refBounds = refBounds;
this.writingMode = writingMode;
this.language = language;
this.font = font;
this.styles = styles;
this.bidi = new BidiLevelIterator();
}
public Defaults getDefaults() {
return defaults;
}
protected void setFont(Font font) {
this.font = font;
}
public void clear() {
if (attributes != null)
attributes.clear();
}
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(), null, 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) : -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) {
collectCommonStyles(e, 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());
}
}
}
}
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 != defaults.getOrientation()) && (lastEnd > lastBegin))
addAttribute(StyleAttribute.ORIENTATION, lastOrientation, lastBegin, i);
lastOrientation = o;
lastBegin = i;
}
lastEnd = i + 1;
}
if (lastBegin < end) {
if ((lastOrientation != null) && (lastOrientation != defaults.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 bidiRunBreakingAttributes;
static {
Set s = new java.util.HashSet();
s.add(StyleAttribute.ORIENTATION);
bidiRunBreakingAttributes = Collections.unmodifiableSet(s);
}
private int[] getBidiRunLimits(int begin, int end) {
Set limits = new java.util.TreeSet();
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 extract() {
List 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) {
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(), null, null, 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);
// 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(), null, null, 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(), null, null, 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(), null, 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);
Length blur = to.getBlur();
double b = Helpers.resolveLength(e, blur, Axis.VERTICAL, extBounds, refBounds, fs);
v = new Outline(c, t, b);
}
}
if (v != null)
addAttribute(StyleAttribute.OUTLINE, 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 lengths = new java.util.ArrayList();
if (Lengths.isLengths(s.getValue(), null, context, minMax, treatments, lengths)) {
assert lengths.size() == 1;
v = Double.valueOf(Helpers.resolveLength(e, lengths.get(0), Axis.VERTICAL, extBounds, refBounds, fs));
}
}
}
if (v != null)
addAttribute(StyleAttribute.LINE_HEIGHT, v, begin, end);
}
private Font getFontFromStyles(Element e, StyleSet styles) {
StyleSpecification s;
// families
List fontFamilies = null;
s = styles.get(ttsFontFamilyAttrName);
if (s != null) {
List families = new java.util.ArrayList();
Object[] treatments = new Object[] { QuotedGenericFontFamilyTreatment.Allow };
if (Fonts.isFontFamilies(s.getValue(), null, context, treatments, families)) {
if (!families.isEmpty()) {
List familyNames = new java.util.ArrayList();
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 fontFeatures = new java.util.HashSet();
// variant features
s = styles.get(ttsFontVariantAttrName);
if (s != null) {
Set variants = new java.util.HashSet();
if (Fonts.isFontVariants(s.getValue(), null, 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 lengths = new java.util.ArrayList();
if (Lengths.isLengths(s.getValue(), null, 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 fontCache.mapFont(fontFamilies, fontStyle, fontWeight, language, writingMode.getAxis(IPD), fontSize, fontFeatures);
}
protected StyleSet getStyles(Element e) {
String style = Documents.getAttribute(e, isdCSSAttrName, null);
if (style != null) {
StyleSet styles = this.styles.get(style);
if (styles != null)
return styles;
else
return StyleSet.EMPTY;
} else
return StyleSet.EMPTY;
}
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) {
if (attributes == null)
attributes = new java.util.ArrayList();
attributes.add(new StyleAttributeInterval(attribute, value, begin, end));
}
protected List getIntervals(Set attributes) {
List intervals = new java.util.ArrayList();
if (this.attributes != null) {
for (StyleAttributeInterval i : this.attributes) {
if (attributes.contains(i.getAttribute()))
intervals.add(i);
}
}
return intervals;
}
protected List getIntervals(StyleAttribute attribute) {
List intervals = new java.util.ArrayList();
if (this.attributes != null) {
for (StyleAttributeInterval i : this.attributes) {
if (i.getAttribute().equals(attribute))
intervals.add(i);
}
}
return intervals;
}
protected List getDefaultFontFamilies(Element e, StyleSet styles) {
return defaults.getFontFamilies();
}
protected FontStyle getDefaultFontStyle(Element e, StyleSet styles) {
return defaults.getFontStyle();
}
protected FontWeight getDefaultFontWeight(Element e, StyleSet styles) {
return defaults.getFontWeight();
}
protected Extent getDefaultFontSize(Element e, StyleSet styles) {
return defaults.getFontSize();
}
protected Set getDefaultFontFeatures(Element e, StyleSet styles) {
return defaults.getFontFeatures();
}
protected Extent parseFontSize(Element e, StyleSpecification s) {
Integer[] minMax = new Integer[] { 1, 2 };
Object[] treatments = new Object[] { NegativeTreatment.Allow, MixedUnitsTreatment.Allow };
List lengths = new java.util.ArrayList();
if (Lengths.isLengths(s.getValue(), null, context, minMax, treatments, lengths)) {
assert lengths.size() > 0;
if (lengths.size() == 1)
lengths.add(lengths.get(0));
Extent fs = (font != null) ? font.getSize() : Extent.UNIT;
double w = Helpers.resolveLength(e, lengths.get(0), Axis.VERTICAL, extBounds, refBounds, fs);
double h = Helpers.resolveLength(e, lengths.get(1), Axis.HORIZONTAL, extBounds, refBounds, fs);
return new Extent(w, h);
} else
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy