From 3ec57c684b6564fa7f2c920c32e94572c3543692 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Mon, 28 Sep 2009 15:27:30 +0000 Subject: [PATCH] Implemented parsing of structure trees from IF XML files. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_Accessibility@819585 13f79535-47bb-0310-9956-ffa450edef68 --- .../accessibility/ParsedStructureTree.java | 95 +++++++++++++++ .../accessibility/SimpleStructureTree.java | 113 ++++++++++++++++++ .../fop/accessibility/StructureTree.java | 64 +--------- .../TransformerNodeEndProcessing.java | 2 +- .../fop/render/intermediate/IFParser.java | 75 +++++++++--- 5 files changed, 271 insertions(+), 78 deletions(-) create mode 100644 src/java/org/apache/fop/accessibility/ParsedStructureTree.java create mode 100644 src/java/org/apache/fop/accessibility/SimpleStructureTree.java diff --git a/src/java/org/apache/fop/accessibility/ParsedStructureTree.java b/src/java/org/apache/fop/accessibility/ParsedStructureTree.java new file mode 100644 index 000000000..69b26c21c --- /dev/null +++ b/src/java/org/apache/fop/accessibility/ParsedStructureTree.java @@ -0,0 +1,95 @@ +/* + * 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); + } + +} diff --git a/src/java/org/apache/fop/accessibility/SimpleStructureTree.java b/src/java/org/apache/fop/accessibility/SimpleStructureTree.java new file mode 100644 index 000000000..e67757207 --- /dev/null +++ b/src/java/org/apache/fop/accessibility/SimpleStructureTree.java @@ -0,0 +1,113 @@ +/* + * 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(); + } + } + +} diff --git a/src/java/org/apache/fop/accessibility/StructureTree.java b/src/java/org/apache/fop/accessibility/StructureTree.java index 0be785eb2..09a33f151 100644 --- a/src/java/org/apache/fop/accessibility/StructureTree.java +++ b/src/java/org/apache/fop/accessibility/StructureTree.java @@ -19,62 +19,13 @@ 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. @@ -82,17 +33,6 @@ public final class StructureTree { * @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); - } - } } diff --git a/src/java/org/apache/fop/accessibility/TransformerNodeEndProcessing.java b/src/java/org/apache/fop/accessibility/TransformerNodeEndProcessing.java index c89f72623..00d4a5b56 100644 --- a/src/java/org/apache/fop/accessibility/TransformerNodeEndProcessing.java +++ b/src/java/org/apache/fop/accessibility/TransformerNodeEndProcessing.java @@ -78,7 +78,7 @@ class TransformerNodeEndProcessing extends TransformerNode { 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); diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java index e374f82fa..3dc440692 100644 --- a/src/java/org/apache/fop/render/intermediate/IFParser.java +++ b/src/java/org/apache/fop/render/intermediate/IFParser.java @@ -33,20 +33,19 @@ import javax.xml.transform.TransformerException; 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; @@ -60,6 +59,7 @@ import org.apache.fop.util.ContentHandlerFactory; 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; /** @@ -150,6 +150,26 @@ public class IFParser implements IFConstants { 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; @@ -173,6 +193,11 @@ public class IFParser implements IFConstants { 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) { @@ -200,19 +225,20 @@ public class IFParser implements IFConstants { } 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) { @@ -256,6 +282,25 @@ public class IFParser implements IFConstants { } } + 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 -- 2.39.5