--- /dev/null
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+
+import org.w3c.dom.NodeList;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+import org.apache.fop.util.DelegatingContentHandler;
+
+/**
+ * A StructureTree implementation re-created from the structure stored in an IF
+ * XML document.
+ */
+public class ParsedStructureTree implements StructureTree {
+
+ private SAXTransformerFactory factory;
+
+ private List pageSequenceStructures = new ArrayList();
+
+ /**
+ * Creates a new instance.
+ *
+ * @param factory a factory internally used to build the structures of page
+ * sequences
+ */
+ public ParsedStructureTree(SAXTransformerFactory factory) {
+ this.factory = factory;
+ }
+
+ /**
+ * Returns a ContenHandler for parsing the structure of a new page sequence.
+ * It is assumed that page sequences are being parsed in the document order.
+ * This class will automatically number the structure trees.
+ *
+ * @return a handler for parsing the <structure-tree> element and its
+ * descendants
+ * @throws SAXException if there is an error when creating the handler
+ */
+ public ContentHandler getHandlerForNextPageSequence() throws SAXException {
+ TransformerHandler structureTreeBuilder;
+ try {
+ structureTreeBuilder = factory.newTransformerHandler();
+ } catch (TransformerConfigurationException e) {
+ throw new SAXException(e);
+ }
+ final DOMResult domResult = new DOMResult();
+ structureTreeBuilder.setResult(domResult);
+ return new DelegatingContentHandler(structureTreeBuilder) {
+
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ /*
+ * There's not text node in the structure tree. This is just
+ * whitespace => ignore
+ */
+ }
+
+ public void endDocument() throws SAXException {
+ super.endDocument();
+ pageSequenceStructures.add(domResult.getNode().getFirstChild().getChildNodes());
+ }
+ };
+ }
+
+ /** {@inheritDoc} */
+ public NodeList getPageSequence(int number) {
+ return (NodeList) pageSequenceStructures.get(number - 1);
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Iterator;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * A StructureTree implementation created from the reduced FO tree, in the form
+ * of a single DOM document obtained by XSL Transformation of the original FO
+ * tree.
+ */
+final class SimpleStructureTree implements StructureTree {
+
+ private final Node reducedFOTree;
+
+ private static class NamespaceContextImpl implements NamespaceContext {
+
+ private String uri;
+ private String prefix;
+
+ public NamespaceContextImpl() {
+ }
+
+ public NamespaceContextImpl(String prefix, String uri) {
+ this.uri = uri;
+ this.prefix = prefix;
+ }
+
+ public String getNamespaceURI(String prefix) {
+ return uri;
+ }
+
+ public void setNamespaceURI(String uri) {
+ this.uri = uri;
+ }
+
+ public String getPrefix(String uri) {
+ return prefix;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public Iterator getPrefixes(String uri) {
+ return null;
+ }
+ }
+
+ SimpleStructureTree(Node reducedFOTree) {
+ this.reducedFOTree = reducedFOTree;
+ }
+
+ /** {@inheritDoc} */
+ public NodeList getPageSequence(int number) {
+ XPath xpath = XPathFactory.newInstance().newXPath();
+ NamespaceContext namespaceContext = new NamespaceContextImpl("fo",
+ "http://www.w3.org/1999/XSL/Format");
+ xpath.setNamespaceContext(namespaceContext);
+ String xpathExpr = "/fo:root/fo:page-sequence[" + Integer.toString(number) + "]/*";
+
+ try {
+ return (NodeList) xpath.evaluate(xpathExpr, reducedFOTree, XPathConstants.NODESET);
+ } catch (XPathExpressionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ try {
+ Transformer t = TransformerFactory.newInstance().newTransformer();
+ Writer str = new StringWriter();
+ t.transform(new DOMSource(reducedFOTree), new StreamResult(str));
+ return str.toString();
+ } catch (Exception e) {
+ return e.toString();
+ }
+ }
+
+}
package org.apache.fop.accessibility;
-import java.util.Iterator;
-
-import javax.xml.namespace.NamespaceContext;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
-
-import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* A reduced version of the document's FO tree, containing only its logical
* structure. Used by accessible output formats.
*/
-public final class StructureTree {
-
- private final Node reducedFOTree;
-
- private static class NamespaceContextImpl implements NamespaceContext {
-
- private String uri;
- private String prefix;
-
- public NamespaceContextImpl() {
- }
-
- public NamespaceContextImpl(String prefix, String uri) {
- this.uri = uri;
- this.prefix = prefix;
- }
-
- public String getNamespaceURI(String prefix) {
- return uri;
- }
-
- public void setNamespaceURI(String uri) {
- this.uri = uri;
- }
-
- public String getPrefix(String uri) {
- return prefix;
- }
-
- public void setPrefix(String prefix) {
- this.prefix = prefix;
- }
-
- public Iterator getPrefixes(String uri) {
- return null;
- }
- }
-
- StructureTree(Node reducedFOTree) {
- this.reducedFOTree = reducedFOTree;
- }
+public interface StructureTree {
/**
* Returns the list of nodes that are the children of the given page sequence.
* @param number number of the page sequence, 1-based
* @return its children nodes
*/
- public NodeList getPageSequence(int number) {
- XPath xpath = XPathFactory.newInstance().newXPath();
- NamespaceContext namespaceContext = new NamespaceContextImpl("fo",
- "http://www.w3.org/1999/XSL/Format");
- xpath.setNamespaceContext(namespaceContext);
- String xpathExpr = "/fo:root/fo:page-sequence[" + Integer.toString(number) + "]/*";
+ NodeList getPageSequence(int number);
- try {
- return (NodeList) xpath.evaluate(xpathExpr, reducedFOTree, XPathConstants.NODESET);
- } catch (XPathExpressionException e) {
- throw new RuntimeException(e);
- }
- }
}
Source src = new StreamSource(new ByteArrayInputStream(enrichedFO));
DOMResult res = new DOMResult();
transformer.transform(src, res);
- userAgent.setStructureTree(new StructureTree(res.getNode()));
+ userAgent.setStructureTree(new SimpleStructureTree(res.getNode()));
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setNamespaceAware(true);
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXTransformerFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
-
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
import org.apache.xmlgraphics.util.QName;
+import org.apache.fop.accessibility.ParsedStructureTree;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.ElementMapping;
import org.apache.fop.fo.ElementMappingRegistry;
import org.apache.fop.util.ContentHandlerFactoryRegistry;
import org.apache.fop.util.DOMBuilderContentHandlerFactory;
import org.apache.fop.util.DefaultErrorListener;
+import org.apache.fop.util.DelegatingContentHandler;
import org.apache.fop.util.XMLUtil;
/**
private ContentHandler navParser;
+ private ParsedStructureTree structureTree;
+
+ private ContentHandler structureTreeBuilder;
+
+ private final class StructureTreeBuilder extends DelegatingContentHandler {
+
+ private Attributes pageSequenceAttributes;
+
+ private StructureTreeBuilder(Attributes pageSequenceAttributes,
+ ParsedStructureTree structureTree) throws SAXException {
+ super(structureTree.getHandlerForNextPageSequence());
+ this.pageSequenceAttributes = new AttributesImpl(pageSequenceAttributes);
+ }
+
+ public void endDocument() throws SAXException {
+ super.endDocument();
+ startIFElement(EL_PAGE_SEQUENCE, pageSequenceAttributes);
+ }
+ }
+
public Handler(IFDocumentHandler documentHandler, FOUserAgent userAgent,
ElementMappingRegistry elementMappingRegistry) {
this.documentHandler = documentHandler;
elementHandlers.put(EL_LINE, new LineHandler());
elementHandlers.put(EL_BORDER_RECT, new BorderRectHandler());
elementHandlers.put(EL_IMAGE, new ImageHandler());
+
+ if (userAgent.isAccessibilityEnabled()) {
+ structureTree = new ParsedStructureTree(tFactory);
+ userAgent.setStructureTree(structureTree);
+ }
}
private void establishForeignAttributes(Map foreignAttributes) {
} else {
boolean handled = true;
if (NAMESPACE.equals(uri)) {
- lastAttributes = new AttributesImpl(attributes);
- ElementHandler elementHandler = (ElementHandler)elementHandlers.get(localName);
- content.setLength(0);
- ignoreCharacters = true;
- if (elementHandler != null) {
- ignoreCharacters = elementHandler.ignoreCharacters();
- try {
- elementHandler.startElement(attributes);
- } catch (IFException ife) {
- handleIFException(ife);
+ if (localName.equals(EL_PAGE_SEQUENCE) && userAgent.isAccessibilityEnabled()) {
+ structureTreeBuilder = new StructureTreeBuilder(attributes, structureTree);
+ } else if (localName.equals(EL_STRUCTURE_TREE)) {
+ if (userAgent.isAccessibilityEnabled()) {
+ delegate = structureTreeBuilder;
+ } else {
+ /* Delegate to a handler that does nothing */
+ delegate = new DefaultHandler();
}
+ delegateDepth++;
+ delegate.startDocument();
+ delegate.startElement(uri, localName, qName, attributes);
} else {
- handled = false;
+ handled = startIFElement(localName, attributes);
}
} else if (DocumentNavigationExtensionConstants.NAMESPACE.equals(uri)) {
if (this.navParser == null) {
}
}
+ private boolean startIFElement(String localName, Attributes attributes)
+ throws SAXException {
+ lastAttributes = new AttributesImpl(attributes);
+ ElementHandler elementHandler = (ElementHandler)elementHandlers.get(localName);
+ content.setLength(0);
+ ignoreCharacters = true;
+ if (elementHandler != null) {
+ ignoreCharacters = elementHandler.ignoreCharacters();
+ try {
+ elementHandler.startElement(attributes);
+ } catch (IFException ife) {
+ handleIFException(ife);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
private void handleIFException(IFException ife) throws SAXException {
if (ife.getCause() instanceof SAXException) {
//unwrap