
com.skynav.ttpe.text.AnnotatedPhraseCollector 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.text;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
import com.skynav.ttpe.geometry.Axis;
import com.skynav.ttpe.style.Annotation;
import com.skynav.ttpe.style.AnnotationStyleCollector;
import com.skynav.ttpe.style.Defaults;
import com.skynav.ttpe.style.Emphasis;
import com.skynav.ttpe.style.Outline;
import com.skynav.ttpe.style.StyleAttribute;
import com.skynav.ttpe.style.StyleAttributeInterval;
import com.skynav.ttpe.style.StyleCollector;
import com.skynav.ttv.util.StyleSet;
import com.skynav.xml.helpers.Documents;
import static com.skynav.ttpe.style.Constants.*;
import static com.skynav.ttpe.text.Constants.*;
public class AnnotatedPhraseCollector extends PhraseCollector {
private List bases; // base phrases
private List baseStarts; // indices into base phrases list of start of base containers
private Map> annotations; // annotations expressed as map from bases to annotation lists
private int currentBase; // current index into bases when processing text container and text spans
public AnnotatedPhraseCollector(AnnotationStyleCollector styleCollector) {
super(styleCollector);
this.currentBase = -1;
}
public List collect(Element e) {
Annotation annotation = styleCollector.getAnnotation(e);
if (annotation == Annotation.EMPHASIS)
return collectEmphasized(e);
else
return collectAnnotated(e);
}
@Override
protected void emit(Element e) {
if (bases != null) {
for (Phrase base : bases) {
if (annotations != null) {
List baseAnnotations = annotations.get(base);
if (baseAnnotations != null) {
base.add(StyleAttribute.ANNOTATIONS, baseAnnotations.toArray(new Phrase[baseAnnotations.size()]), 0, base.length());
}
}
}
add(new AnnotatedPhrase(e, bases, styleCollector.extract()));
}
}
private List collectEmphasized(Element e) {
clear();
collectBaseWithEmphasis(e);
collectTextWithEmphasis(e);
emit(e);
return extract();
}
private void collectBaseWithEmphasis(Element eBase) {
collectBase(eBase, true);
}
private void collectTextWithEmphasis(Element eBase) {
Element eText = null;
if (currentBase < 0) {
if ((baseStarts == null) || baseStarts.isEmpty())
currentBase = -1;
else
currentBase = baseStarts.get(baseStarts.get(0));
}
if (currentBase >= 0) {
Defaults defaults = styleCollector.getDefaults();
Document d = eBase.getOwnerDocument();
for (int i = currentBase, n = bases.size(); i < n; ++i) {
Phrase b = bases.get(i);
if (b != null) {
int nb = b.length();
if (nb > 0) {
Emphasis emphasis = b.getEmphasis(-1, defaults);
if ((emphasis != null) && !emphasis.isNone()) {
String emphasisText = emphasis.resolveText(b.getFont(-1, defaults).isVertical() ? Axis.VERTICAL : Axis.HORIZONTAL);
assert (emphasisText != null) && (emphasisText.length() > 0);
emphasisText = emphasisText.substring(0,1);
StringBuffer sb = new StringBuffer();
for (int j = 0; j < nb; ++j)
sb.append(emphasisText);
Text t = d.createTextNode(sb.toString());
eText = Documents.createElement(d, ttSpanElementName);
StyleSet styles = styleCollector.addStyles(eText);
styles.merge(ttsRubyPositionAttrName, getRubyPosition(emphasis));
Outline outline = b.getOutline(-1, defaults);
if ((outline != null) && !outline.isNone())
styles.merge(ttsTextOutlineAttrName, getOutline(outline));
eText.appendChild(t);
collectText(eText, true);
}
}
}
}
}
}
private String getRubyPosition(Emphasis e) {
Emphasis.Position p = e.getPosition();
if (p == Emphasis.Position.BEFORE)
return "before";
else if (p == Emphasis.Position.AFTER)
return "after";
else
return "auto";
}
private String getOutline(Outline o) {
return o.toTextOutlineString();
}
private List collectAnnotated(Element e) {
clear();
for (Node n = e.getFirstChild(); n != null; n = n.getNextSibling()) {
if (n instanceof Text) {
// ignore #PCDATA
} else if (n instanceof Element) {
Element c = (Element) n;
if (Documents.isElement(c, ttSpanElementName)) {
Annotation annotation = styleCollector.getAnnotation(c);
if (annotation == null) {
// ignore span children that do not specify tts:ruby
} else if (annotation == Annotation.BASE_CONTAINER) {
addBaseStart();
collectBaseContainer(c);
} else if (annotation == Annotation.TEXT_CONTAINER) {
if (baseStarts != null) {
collectTextContainer(c);
}
} else if (annotation == Annotation.BASE) {
addBaseStart();
collectBase(c);
} else if (annotation == Annotation.TEXT) {
if (baseStarts != null) {
collectText(c);
}
} else if (annotation == Annotation.DELIMITER) {
collectDelimiter(c);
} else {
throw new IllegalStateException();
}
} else {
// ignore non-span children
}
}
}
if (bases != null)
addBaseStart();
emit(e);
return extract();
}
private void collectBaseContainer(Element e) {
for (Node n = e.getFirstChild(); n != null; n = n.getNextSibling()) {
if (n instanceof Text) {
// ignore #PCDATA
} else if (n instanceof Element) {
Element c = (Element) n;
if (Documents.isElement(c, ttSpanElementName)) {
Annotation annotation = styleCollector.getAnnotation(c);
if (annotation == null) {
// ignore span children that do not specify tts:ruby
} else if (annotation == Annotation.BASE) {
collectBase(c);
} else {
// ignore non-base annotation children
}
} else {
// ignore non-span children
}
}
}
}
private void collectTextContainer(Element e) {
if (currentBase < 0) {
if ((baseStarts == null) || baseStarts.isEmpty())
currentBase = -1;
else
currentBase = baseStarts.get(baseStarts.size() - 1);
}
for (Node n = e.getFirstChild(); n != null; n = n.getNextSibling()) {
if (n instanceof Text) {
// ignore #PCDATA
} else if (n instanceof Element) {
Element c = (Element) n;
if (Documents.isElement(c, ttSpanElementName)) {
Annotation annotation = styleCollector.getAnnotation(c);
if (annotation == null) {
// ignore span children that do not specify tts:ruby
} else if (annotation == Annotation.TEXT) {
collectText(c);
} else {
// ignore non-text annotation children
}
} else {
// ignore non-span children
}
}
}
currentBase = -1;
}
private void collectBase(Element e) {
collectBase(e, false);
}
private void collectBase(Element e, boolean emphasis) {
StringBuffer text = new StringBuffer();
for (Node n = e.getFirstChild(); n != null; n = n.getNextSibling()) {
if (n instanceof Text) {
String t = ((Text) n).getWholeText();
if (t != null)
text.append(t);
} else {
// [TBD] handle nested spans when collecting emphasis as base content
}
}
if (emphasis) {
for (int i = 0, n = text.length(); i < n; ++i) {
char c = text.charAt(i);
addBaseStart();
collectBase(e, new String(new char[]{c}));
}
} else
collectBase(e, text.toString());
}
private void collectBase(Element e, String content) {
StyleCollector sc = new AnnotationStyleCollector(styleCollector, null);
sc.collectSpanStyles(e, -1, -1);
sc.collectContentStyles(content, 0, content.length());
addBase(e, content, sc.extract());
}
private void collectText(Element e) {
collectText(e, false);
}
private void collectText(Element e, boolean emphasis) {
if (currentBase < 0) {
if ((baseStarts == null) || baseStarts.isEmpty())
currentBase = -1;
else
currentBase = baseStarts.get(baseStarts.size() - 1);
}
if (currentBase < bases.size()) {
StringBuffer text = new StringBuffer();
for (Node n = e.getFirstChild(); n != null; n = n.getNextSibling()) {
if (n instanceof Text) {
String t = ((Text) n).getWholeText();
if (t != null)
text.append(t);
}
}
StyleCollector sc = new AnnotationStyleCollector(styleCollector, (currentBase < 0) ? null : bases.get(currentBase).getElement());
sc.collectSpanStyles(e, -1, -1);
String content = text.toString();
sc.collectContentStyles(content, 0, content.length());
if (!emphasis || !bases.get(currentBase).isWhitespace())
addAnnotation(e, content, sc.extract());
++currentBase;
} else
currentBase = -1;
}
private void collectDelimiter(Element e) {
// [TBD] - IMPLEMENT ME
}
private void addBase(Element e, String base, List attributes) {
if (bases == null)
bases = new java.util.ArrayList();
bases.add(newPhrase(e, base, attributes));
}
private void addBaseStart() {
if (baseStarts == null)
baseStarts = new java.util.ArrayList();
baseStarts.add(bases != null ? bases.size() : 0);
}
private void addAnnotation(Element e, String annotation, List attributes) {
if (currentBase >= 0) {
Phrase base = bases.get(currentBase);
if (annotations == null)
annotations = new java.util.HashMap>();
List baseAnnotations = annotations.get(base);
if (baseAnnotations == null) {
baseAnnotations = new java.util.ArrayList();
annotations.put(base, baseAnnotations);
}
baseAnnotations.add(newPhrase(e, annotation, attributes));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy