git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_Accessibility@819585 13f79535-47bb-0310-9956-ffa450edef68tags/fop-1_0
/* | |||||
* 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); | |||||
} | |||||
} |
/* | |||||
* 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; | 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; | import org.w3c.dom.NodeList; | ||||
/** | /** | ||||
* A reduced version of the document's FO tree, containing only its logical | * A reduced version of the document's FO tree, containing only its logical | ||||
* structure. Used by accessible output formats. | * 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. | * Returns the list of nodes that are the children of the given page sequence. | ||||
* @param number number of the page sequence, 1-based | * @param number number of the page sequence, 1-based | ||||
* @return its children nodes | * @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)); | Source src = new StreamSource(new ByteArrayInputStream(enrichedFO)); | ||||
DOMResult res = new DOMResult(); | DOMResult res = new DOMResult(); | ||||
transformer.transform(src, res); | transformer.transform(src, res); | ||||
userAgent.setStructureTree(new StructureTree(res.getNode())); | |||||
userAgent.setStructureTree(new SimpleStructureTree(res.getNode())); | |||||
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); | SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); | ||||
saxParserFactory.setNamespaceAware(true); | saxParserFactory.setNamespaceAware(true); |
import javax.xml.transform.sax.SAXResult; | import javax.xml.transform.sax.SAXResult; | ||||
import javax.xml.transform.sax.SAXTransformerFactory; | 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.DOMImplementation; | ||||
import org.w3c.dom.Document; | import org.w3c.dom.Document; | ||||
import org.xml.sax.Attributes; | import org.xml.sax.Attributes; | ||||
import org.xml.sax.ContentHandler; | import org.xml.sax.ContentHandler; | ||||
import org.xml.sax.SAXException; | import org.xml.sax.SAXException; | ||||
import org.xml.sax.helpers.AttributesImpl; | import org.xml.sax.helpers.AttributesImpl; | ||||
import org.xml.sax.helpers.DefaultHandler; | 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.xmlgraphics.util.QName; | ||||
import org.apache.fop.accessibility.ParsedStructureTree; | |||||
import org.apache.fop.apps.FOUserAgent; | import org.apache.fop.apps.FOUserAgent; | ||||
import org.apache.fop.fo.ElementMapping; | import org.apache.fop.fo.ElementMapping; | ||||
import org.apache.fop.fo.ElementMappingRegistry; | import org.apache.fop.fo.ElementMappingRegistry; | ||||
import org.apache.fop.util.ContentHandlerFactoryRegistry; | import org.apache.fop.util.ContentHandlerFactoryRegistry; | ||||
import org.apache.fop.util.DOMBuilderContentHandlerFactory; | import org.apache.fop.util.DOMBuilderContentHandlerFactory; | ||||
import org.apache.fop.util.DefaultErrorListener; | import org.apache.fop.util.DefaultErrorListener; | ||||
import org.apache.fop.util.DelegatingContentHandler; | |||||
import org.apache.fop.util.XMLUtil; | import org.apache.fop.util.XMLUtil; | ||||
/** | /** | ||||
private ContentHandler navParser; | 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, | public Handler(IFDocumentHandler documentHandler, FOUserAgent userAgent, | ||||
ElementMappingRegistry elementMappingRegistry) { | ElementMappingRegistry elementMappingRegistry) { | ||||
this.documentHandler = documentHandler; | this.documentHandler = documentHandler; | ||||
elementHandlers.put(EL_LINE, new LineHandler()); | elementHandlers.put(EL_LINE, new LineHandler()); | ||||
elementHandlers.put(EL_BORDER_RECT, new BorderRectHandler()); | elementHandlers.put(EL_BORDER_RECT, new BorderRectHandler()); | ||||
elementHandlers.put(EL_IMAGE, new ImageHandler()); | elementHandlers.put(EL_IMAGE, new ImageHandler()); | ||||
if (userAgent.isAccessibilityEnabled()) { | |||||
structureTree = new ParsedStructureTree(tFactory); | |||||
userAgent.setStructureTree(structureTree); | |||||
} | |||||
} | } | ||||
private void establishForeignAttributes(Map foreignAttributes) { | private void establishForeignAttributes(Map foreignAttributes) { | ||||
} else { | } else { | ||||
boolean handled = true; | boolean handled = true; | ||||
if (NAMESPACE.equals(uri)) { | 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 { | } else { | ||||
handled = false; | |||||
handled = startIFElement(localName, attributes); | |||||
} | } | ||||
} else if (DocumentNavigationExtensionConstants.NAMESPACE.equals(uri)) { | } else if (DocumentNavigationExtensionConstants.NAMESPACE.equals(uri)) { | ||||
if (this.navParser == null) { | 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 { | private void handleIFException(IFException ife) throws SAXException { | ||||
if (ife.getCause() instanceof SAXException) { | if (ife.getCause() instanceof SAXException) { | ||||
//unwrap | //unwrap |