All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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