org.apache.fop.accessibility.fo.StructureTreeEventTrigger Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.fop Show documentation
Show all versions of org.apache.fop Show documentation
The core maven build properties
The 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);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy