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

org.apache.fop.accessibility.fo.StructureTreeEventTrigger Maven / Gradle / Ivy

There is a newer version: 1.2.2.1-jre17
Show newest version
/*
 * 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.
 */

/* $Id$ */

package org.apache.fop.accessibility.fo;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Stack;

import javax.xml.XMLConstants;

import org.xml.sax.helpers.AttributesImpl;

import org.apache.fop.accessibility.StructureTreeElement;
import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.fo.FOEventHandler;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FOText;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fo.extensions.InternalElementMapping;
import org.apache.fop.fo.flow.AbstractRetrieveMarker;
import org.apache.fop.fo.flow.BasicLink;
import org.apache.fop.fo.flow.Block;
import org.apache.fop.fo.flow.BlockContainer;
import org.apache.fop.fo.flow.Character;
import org.apache.fop.fo.flow.ExternalGraphic;
import org.apache.fop.fo.flow.Footnote;
import org.apache.fop.fo.flow.FootnoteBody;
import org.apache.fop.fo.flow.Inline;
import org.apache.fop.fo.flow.InstreamForeignObject;
import org.apache.fop.fo.flow.ListBlock;
import org.apache.fop.fo.flow.ListItem;
import org.apache.fop.fo.flow.ListItemBody;
import org.apache.fop.fo.flow.ListItemLabel;
import org.apache.fop.fo.flow.PageNumber;
import org.apache.fop.fo.flow.PageNumberCitation;
import org.apache.fop.fo.flow.PageNumberCitationLast;
import org.apache.fop.fo.flow.RetrieveMarker;
import org.apache.fop.fo.flow.RetrieveTableMarker;
import org.apache.fop.fo.flow.Wrapper;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.fo.flow.table.TableBody;
import org.apache.fop.fo.flow.table.TableCell;
import org.apache.fop.fo.flow.table.TableFooter;
import org.apache.fop.fo.flow.table.TableHeader;
import org.apache.fop.fo.flow.table.TableRow;
import org.apache.fop.fo.pagination.Flow;
import org.apache.fop.fo.pagination.LayoutMasterSet;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.Root;
import org.apache.fop.fo.pagination.StaticContent;
import org.apache.fop.fo.properties.CommonAccessibilityHolder;
import org.apache.fop.fo.properties.CommonHyphenation;
import org.apache.fop.util.LanguageTags;
import org.apache.fop.util.XMLUtil;

/**
 * A bridge between {@link FOEventHandler} and {@link StructureTreeEventHandler}.
 */
class StructureTreeEventTrigger extends FOEventHandler {

    private StructureTreeEventHandler structureTreeEventHandler;

    private LayoutMasterSet layoutMasterSet;

    private Stack tables = new Stack
(); private Stack inTableHeader = new Stack(); private Stack locales = new Stack(); private final Map states = new HashMap(); private static final class State { private final Stack
tables; private final Stack inTableHeader; private final Stack locales; @SuppressWarnings("unchecked") State(StructureTreeEventTrigger o) { this.tables = (Stack
) o.tables.clone(); this.inTableHeader = (Stack) o.inTableHeader.clone(); this.locales = (Stack) o.locales.clone(); } } public StructureTreeEventTrigger(StructureTreeEventHandler structureTreeEventHandler) { this.structureTreeEventHandler = structureTreeEventHandler; } @Override public void startRoot(Root root) { locales.push(root.getLocale()); } @Override public void endRoot(Root root) { locales.pop(); } @Override public void startPageSequence(PageSequence pageSeq) { if (layoutMasterSet == null) { layoutMasterSet = pageSeq.getRoot().getLayoutMasterSet(); } Locale locale = pageSeq.getLocale(); if (locale != null) { locales.push(locale); } else { locales.push(locales.peek()); } String role = pageSeq.getCommonAccessibility().getRole(); structureTreeEventHandler.startPageSequence(locale, role); } @Override public void endPageSequence(PageSequence pageSeq) { structureTreeEventHandler.endPageSequence(); locales.pop(); } @Override public void startPageNumber(PageNumber pagenum) { startElementWithID(pagenum); } @Override public void endPageNumber(PageNumber pagenum) { endElement(pagenum); } @Override public void startPageNumberCitation(PageNumberCitation pageCite) { startElementWithID(pageCite); } @Override public void endPageNumberCitation(PageNumberCitation pageCite) { endElement(pageCite); } @Override public void startPageNumberCitationLast(PageNumberCitationLast pageLast) { startElementWithID(pageLast); } @Override public void endPageNumberCitationLast(PageNumberCitationLast pageLast) { endElement(pageLast); } @Override public void startStatic(StaticContent staticContent) { AttributesImpl flowName = createFlowNameAttribute(staticContent.getFlowName()); startElement(staticContent, flowName); } private AttributesImpl createFlowNameAttribute(String flowName) { String regionName = layoutMasterSet.getDefaultRegionNameFor(flowName); AttributesImpl attribute = new AttributesImpl(); addNoNamespaceAttribute(attribute, Flow.FLOW_NAME, regionName); return attribute; } @Override public void endStatic(StaticContent staticContent) { endElement(staticContent); } @Override public void startFlow(Flow fl) { AttributesImpl flowName = createFlowNameAttribute(fl.getFlowName()); startElement(fl, flowName); } @Override public void endFlow(Flow fl) { endElement(fl); } @Override public void startBlock(Block bl) { CommonHyphenation hyphProperties = bl.getCommonHyphenation(); AttributesImpl attributes = createLangAttribute(hyphProperties); startElement(bl, attributes); } private AttributesImpl createLangAttribute(CommonHyphenation hyphProperties) { Locale locale = hyphProperties.getLocale(); AttributesImpl attributes = new AttributesImpl(); if (locale == null || locale.equals(locales.peek())) { locales.push(locales.peek()); } else { locales.push(locale); addAttribute(attributes, XMLConstants.XML_NS_URI, "lang", "xml", LanguageTags.toLanguageTag(locale)); } return attributes; } @Override public void endBlock(Block bl) { endElement(bl); locales.pop(); } @Override public void startBlockContainer(BlockContainer blc) { startElement(blc); } @Override public void endBlockContainer(BlockContainer blc) { endElement(blc); } @Override public void startInline(Inline inl) { startElement(inl); } @Override public void endInline(Inline inl) { endElement(inl); } @Override public void startTable(Table tbl) { tables.push(tbl); startElement(tbl); } @Override public void endTable(Table tbl) { endElement(tbl); tables.pop(); } @Override public void startHeader(TableHeader header) { inTableHeader.push(Boolean.TRUE); startElement(header); } @Override public void endHeader(TableHeader header) { endElement(header); inTableHeader.pop(); } @Override public void startFooter(TableFooter footer) { // TODO Shouldn't it be true? inTableHeader.push(Boolean.FALSE); startElement(footer); } @Override public void endFooter(TableFooter footer) { endElement(footer); inTableHeader.pop(); } @Override public void startBody(TableBody body) { inTableHeader.push(Boolean.FALSE); startElement(body); } @Override public void endBody(TableBody body) { endElement(body); inTableHeader.pop(); } @Override public void startRow(TableRow tr) { startElement(tr); } @Override public void endRow(TableRow tr) { endElement(tr); } @Override public void startCell(TableCell tc) { AttributesImpl attributes = new AttributesImpl(); addSpanAttribute(attributes, "number-columns-spanned", tc.getNumberColumnsSpanned()); addSpanAttribute(attributes, "number-rows-spanned", tc.getNumberRowsSpanned()); boolean rowHeader = inTableHeader.peek(); boolean columnHeader = tables.peek().getColumn(tc.getColumnNumber() - 1).isHeader(); if (rowHeader || columnHeader) { final String th = "TH"; String role = tc.getCommonAccessibility().getRole(); /* Do not override a custom role */ if (role == null) { role = th; addNoNamespaceAttribute(attributes, "role", th); } if (role.equals(th)) { if (columnHeader) { String scope = rowHeader ? "Both" : "Row"; addAttribute(attributes, InternalElementMapping.URI, InternalElementMapping.SCOPE, InternalElementMapping.STANDARD_PREFIX, scope); } } } startElement(tc, attributes); } private void addSpanAttribute(AttributesImpl attributes, String attributeName, int span) { if (span > 1) { addNoNamespaceAttribute(attributes, attributeName, Integer.toString(span)); } } @Override public void endCell(TableCell tc) { endElement(tc); } @Override public void startList(ListBlock lb) { startElement(lb); } @Override public void endList(ListBlock lb) { endElement(lb); } @Override public void startListItem(ListItem li) { startElement(li); } @Override public void endListItem(ListItem li) { endElement(li); } @Override public void startListLabel(ListItemLabel listItemLabel) { startElement(listItemLabel); } @Override public void endListLabel(ListItemLabel listItemLabel) { endElement(listItemLabel); } @Override public void startListBody(ListItemBody listItemBody) { startElement(listItemBody); } @Override public void endListBody(ListItemBody listItemBody) { endElement(listItemBody); } @Override public void startLink(BasicLink basicLink) { startElementWithIDAndAltText(basicLink, basicLink.getAltText()); } @Override public void endLink(BasicLink basicLink) { endElement(basicLink); } @Override public void image(ExternalGraphic eg) { startElementWithIDAndAltText(eg, eg.getAltText()); endElement(eg); } @Override public void startInstreamForeignObject(InstreamForeignObject ifo) { startElementWithIDAndAltText(ifo, ifo.getAltText()); } @Override public void endInstreamForeignObject(InstreamForeignObject ifo) { endElement(ifo); } @Override public void startFootnote(Footnote footnote) { startElement(footnote); } @Override public void endFootnote(Footnote footnote) { endElement(footnote); } @Override public void startFootnoteBody(FootnoteBody body) { startElement(body); } @Override public void endFootnoteBody(FootnoteBody body) { endElement(body); } @Override public void startWrapper(Wrapper wrapper) { startElement(wrapper); } @Override public void endWrapper(Wrapper wrapper) { endElement(wrapper); } @Override public void startRetrieveMarker(RetrieveMarker retrieveMarker) { startElementWithID(retrieveMarker); saveState(retrieveMarker); } void saveState(AbstractRetrieveMarker retrieveMarker) { states.put(retrieveMarker, new State(this)); } @Override public void endRetrieveMarker(RetrieveMarker retrieveMarker) { endElement(retrieveMarker); } @Override public void restoreState(RetrieveMarker retrieveMarker) { restoreRetrieveMarkerState(retrieveMarker); } @SuppressWarnings("unchecked") private void restoreRetrieveMarkerState(AbstractRetrieveMarker retrieveMarker) { State state = states.get(retrieveMarker); tables = (Stack
) state.tables.clone(); inTableHeader = (Stack) state.inTableHeader.clone(); locales = (Stack) state.locales.clone(); } @Override public void startRetrieveTableMarker(RetrieveTableMarker retrieveTableMarker) { startElementWithID(retrieveTableMarker); saveState(retrieveTableMarker); } @Override public void endRetrieveTableMarker(RetrieveTableMarker retrieveTableMarker) { endElement(retrieveTableMarker); } @Override public void restoreState(RetrieveTableMarker retrieveTableMarker) { restoreRetrieveMarkerState(retrieveTableMarker); } @Override public void character(Character c) { AttributesImpl attributes = createLangAttribute(c.getCommonHyphenation()); startElementWithID(c, attributes); endElement(c); locales.pop(); } @Override public void characters(FOText foText) { startElementWithID(foText); endElement(foText); } private StructureTreeElement startElement(FONode node) { AttributesImpl attributes = new AttributesImpl(); if (node instanceof Inline) { Inline in = (Inline)node; if (!in.getAbbreviation().equals("")) { addAttribute(attributes, ExtensionElementMapping.URI, "abbreviation", ExtensionElementMapping.STANDARD_PREFIX, in.getAbbreviation()); } } return startElement(node, attributes); } private void startElementWithID(FONode node) { startElementWithID(node, new AttributesImpl()); } private void startElementWithID(FONode node, AttributesImpl attributes) { String localName = node.getLocalName(); if (node instanceof CommonAccessibilityHolder) { addRole((CommonAccessibilityHolder) node, attributes); } node.setStructureTreeElement( structureTreeEventHandler.startReferencedNode(localName, attributes, node.getParent().getStructureTreeElement())); } private void startElementWithIDAndAltText(FObj node, String altText) { AttributesImpl attributes = new AttributesImpl(); String localName = node.getLocalName(); addRole((CommonAccessibilityHolder)node, attributes); addAttribute(attributes, ExtensionElementMapping.URI, "alt-text", ExtensionElementMapping.STANDARD_PREFIX, altText); node.setStructureTreeElement( structureTreeEventHandler.startImageNode(localName, attributes, node.getParent().getStructureTreeElement())); } private StructureTreeElement startElement(FONode node, AttributesImpl attributes) { String localName = node.getLocalName(); if (node instanceof CommonAccessibilityHolder) { addRole((CommonAccessibilityHolder) node, attributes); } return structureTreeEventHandler.startNode(localName, attributes, node.getParent().getStructureTreeElement()); } private void addNoNamespaceAttribute(AttributesImpl attributes, String name, String value) { attributes.addAttribute("", name, name, XMLUtil.CDATA, value); } private void addAttribute(AttributesImpl attributes, String namespace, String localName, String prefix, String value) { assert namespace.length() > 0 && prefix.length() > 0; String qualifiedName = prefix + ":" + localName; attributes.addAttribute(namespace, localName, qualifiedName, XMLUtil.CDATA, value); } private void addRole(CommonAccessibilityHolder node, AttributesImpl attributes) { String role = node.getCommonAccessibility().getRole(); if (role != null) { addNoNamespaceAttribute(attributes, "role", role); } } private void endElement(FONode node) { String localName = node.getLocalName(); structureTreeEventHandler.endNode(localName); } }