diff options
194 files changed, 6182 insertions, 1414 deletions
@@ -548,6 +548,7 @@ list of possible build targets. <!-- General classes --> <patternset> <include name="org/apache/fop/Version.class"/> + <include name="org/apache/fop/accessibility/StructureTreeElement.class"/> <include name="org/apache/fop/apps/Fop.class"/> <include name="org/apache/fop/apps/FOPException.class"/> <include name="org/apache/fop/fo/Constants.class"/> @@ -705,6 +706,7 @@ list of possible build targets. <include name="**/*.xml"/> <include name="**/*.fo"/> <include name="**/*.afp"/> + <include name="**/*.xsl"/> </fileset> <fileset dir="${build.dir}/test-gensrc"> <include name="**/*.xml"/> diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml index 5e179955f..e5aee4279 100644 --- a/findbugs-exclude.xml +++ b/findbugs-exclude.xml @@ -1,6 +1,38 @@ <?xml version="1.0" encoding="utf-8"?> <FindBugsFilter> - <!-- The names of public methods will not be modified --> + <Match> + <Class name="org.apache.fop.render.intermediate.IFStructureTreeBuilder"/> + <Field name="delegate"/> + <Bug pattern="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR"/> + </Match> + <Match> + <Class name="org.apache.fop.render.pdf.PDFLogicalStructureHandler"/> + <Field name="currentPage"/> + <Bug pattern="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR"/> + </Match> + <Match> + <Class name="org.apache.fop.render.pdf.PDFLogicalStructureHandler"/> + <Field name="pageParentTreeArray"/> + <Bug pattern="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR"/> + </Match> + <Match> + <Class name="org.apache.fop.render.pdf.PDFStructureTreeBuilder"/> + <Field name="logicalStructureHandler"/> + <Bug pattern="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR"/> + </Match> + <Match> + <Class name="org.apache.fop.render.pdf.PDFStructureTreeBuilder"/> + <Field name="pdfFactory"/> + <Bug pattern="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR"/> + </Match> + <Match> + <Class name="org.apache.fop.accessibility.fo.FO2StructureTreeConverter$1"/> + <Bug pattern="SIC_INNER_SHOULD_BE_STATIC_ANON"/> + </Match> + <Match> + <Class name="~org.apache.fop.accessibility.fo.FOEventRecorder.*"/> + <Bug pattern="SIC_INNER_SHOULD_BE_STATIC_ANON"/> + </Match> <Match> <Class name="org.apache.fop.afp.AFPDataObjectInfo"/> <Method name="getUri"/> @@ -4789,4 +4821,4 @@ <Bug pattern="REC_CATCH_EXCEPTION"/> </Match> <!-- /Automatically generated list of exclusions on 18 February 2011 --> -</FindBugsFilter>
\ No newline at end of file +</FindBugsFilter> diff --git a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd index b6fa00bd2..86dce1a3c 100644 --- a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd +++ b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd @@ -40,10 +40,17 @@ </xs:element> <xs:element name="header"> <xs:complexType> - <xs:choice minOccurs="0" maxOccurs="unbounded"> - <!--xs:element ref="x:xmpmeta" xmlns:x="adobe:ns:meta/"/--> - <xs:any namespace="##other" processContents="lax"/> - </xs:choice> + <xs:sequence> + <xs:element name="locale" minOccurs="0" maxOccurs="1"> + <xs:complexType> + <xs:attributeGroup ref="mf:foreignAtts"/> + </xs:complexType> + </xs:element> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <!--xs:element ref="x:xmpmeta" xmlns:x="adobe:ns:meta/"/--> + <xs:any namespace="##other" processContents="lax"/> + </xs:choice> + </xs:sequence> </xs:complexType> </xs:element> <xs:element name="trailer"> diff --git a/src/java/org/apache/fop/accessibility/Accessibility.java b/src/java/org/apache/fop/accessibility/Accessibility.java index d550b433a..c842cf43f 100644 --- a/src/java/org/apache/fop/accessibility/Accessibility.java +++ b/src/java/org/apache/fop/accessibility/Accessibility.java @@ -19,18 +19,6 @@ package org.apache.fop.accessibility; -import javax.xml.transform.Source; -import javax.xml.transform.Templates; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.sax.SAXTransformerFactory; -import javax.xml.transform.sax.TransformerHandler; -import javax.xml.transform.stream.StreamSource; - -import org.xml.sax.helpers.DefaultHandler; - -import org.apache.fop.apps.FOPException; -import org.apache.fop.apps.FOUserAgent; /** * Helper class for FOP's accessibility features. @@ -40,48 +28,6 @@ public final class Accessibility { /** Constant string for the rendering options key to enable accessibility features. */ public static final String ACCESSIBILITY = "accessibility"; - // TODO what if the default factory is not a SAXTransformerFactory? - private static SAXTransformerFactory tfactory - = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); - - private static Templates addPtrTemplates; - - private static Templates reduceFOTreeTemplates; - private Accessibility() { } - /** - * Decorates the given handler so the structure tree used for accessibility - * features can be branched off the main content stream. - * @param handler the handler to decorate - * @param userAgent the user agent - * @return the decorated handler - * @throws FOPException if an error occurs setting up the decoration - */ - public static DefaultHandler decorateDefaultHandler(DefaultHandler handler, - FOUserAgent userAgent) throws FOPException { - try { - setupTemplates(); - TransformerHandler addPtr = tfactory.newTransformerHandler(addPtrTemplates); - Transformer reduceFOTree = reduceFOTreeTemplates.newTransformer(); - return new AccessibilityPreprocessor(addPtr, reduceFOTree, userAgent, handler); - } catch (TransformerConfigurationException e) { - throw new FOPException(e); - } - } - - private static synchronized void setupTemplates() throws TransformerConfigurationException { - if (addPtrTemplates == null) { - addPtrTemplates = loadTemplates("addPtr.xsl"); - } - if (reduceFOTreeTemplates == null) { - reduceFOTreeTemplates = loadTemplates("reduceFOTree.xsl"); - } - } - - private static Templates loadTemplates(String source) throws TransformerConfigurationException { - Source src = new StreamSource(Accessibility.class.getResource(source).toExternalForm()); - return tfactory.newTemplates(src); - } - } diff --git a/src/java/org/apache/fop/accessibility/AccessibilityPreprocessor.java b/src/java/org/apache/fop/accessibility/AccessibilityPreprocessor.java deleted file mode 100644 index 1958b74a8..000000000 --- a/src/java/org/apache/fop/accessibility/AccessibilityPreprocessor.java +++ /dev/null @@ -1,95 +0,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.io.ByteArrayInputStream; -import java.io.InputStream; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.dom.DOMResult; -import javax.xml.transform.sax.TransformerHandler; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; - -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import org.apache.commons.io.output.ByteArrayOutputStream; - -import org.apache.fop.apps.FOUserAgent; -import org.apache.fop.util.TransformerDefaultHandler; - -/** - * This class prepares an XSL-FO document for accessibility. It adds a unique - * identifier to every applicable FO, then creates the structure tree, before - * handing the document over to the regular handler. - */ -class AccessibilityPreprocessor extends TransformerDefaultHandler { - - private final ByteArrayOutputStream enrichedFOBuffer = new ByteArrayOutputStream(); - - private final Transformer reduceFOTree; - - private final FOUserAgent userAgent; - - private final DefaultHandler fopHandler; - - public AccessibilityPreprocessor(TransformerHandler addPtr, Transformer reduceFOTree, - FOUserAgent userAgent, DefaultHandler fopHandler) { - super(addPtr); - this.reduceFOTree = reduceFOTree; - this.userAgent = userAgent; - this.fopHandler = fopHandler; - getTransformerHandler().setResult(new StreamResult(enrichedFOBuffer)); - } - - /** {@inheritDoc} */ - public void endDocument() throws SAXException { - super.endDocument(); - // do the second transform to struct - try { - //TODO this must be optimized, no buffering (ex. SAX-based tee-proxy) - byte[] enrichedFO = enrichedFOBuffer.toByteArray(); - Source src = new StreamSource(new ByteArrayInputStream(enrichedFO)); - DOMResult res = new DOMResult(); - reduceFOTree.transform(src, res); - StructureTree structureTree = new StructureTree(); - NodeList pageSequences = res.getNode().getFirstChild().getChildNodes(); - for (int i = 0; i < pageSequences.getLength(); i++) { - structureTree.addPageSequenceStructure(pageSequences.item(i).getChildNodes()); - } - userAgent.setStructureTree(structureTree); - - SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); - saxParserFactory.setNamespaceAware(true); - saxParserFactory.setValidating(false); - SAXParser saxParser = saxParserFactory.newSAXParser(); - InputStream in = new ByteArrayInputStream(enrichedFO); - saxParser.parse(in, fopHandler); - } catch (Exception e) { - throw new SAXException(e); - } - } - -} diff --git a/src/java/org/apache/fop/accessibility/DummyStructureTreeEventHandler.java b/src/java/org/apache/fop/accessibility/DummyStructureTreeEventHandler.java new file mode 100644 index 000000000..66eaece89 --- /dev/null +++ b/src/java/org/apache/fop/accessibility/DummyStructureTreeEventHandler.java @@ -0,0 +1,63 @@ +/* + * 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.Locale; + +import org.xml.sax.Attributes; + +/** + * This implementation ignores all structure tree events. + */ +public final class DummyStructureTreeEventHandler implements StructureTreeEventHandler { + + /** The singleton instance of this class. */ + public static final StructureTreeEventHandler INSTANCE = new DummyStructureTreeEventHandler(); + + private DummyStructureTreeEventHandler() { } + + /** {@inheritDoc} */ + public void startPageSequence(Locale locale) { + } + + /** {@inheritDoc} */ + public void endPageSequence() { + } + + /** {@inheritDoc} */ + public StructureTreeElement startNode(String name, Attributes attributes) { + return null; + } + + /** {@inheritDoc} */ + public void endNode(String name) { + } + + /** {@inheritDoc} */ + public StructureTreeElement startImageNode(String name, Attributes attributes) { + return null; + } + + /** {@inheritDoc} */ + public StructureTreeElement startReferencedNode(String name, Attributes attributes) { + return null; + } + +} diff --git a/src/java/org/apache/fop/accessibility/StructureTree.java b/src/java/org/apache/fop/accessibility/StructureTree.java deleted file mode 100644 index a0fdaac21..000000000 --- a/src/java/org/apache/fop/accessibility/StructureTree.java +++ /dev/null @@ -1,102 +0,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.io.StringWriter; -import java.io.Writer; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -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 List pageSequenceStructures = new ArrayList(); - - /** - * Package-private default constructor. - */ - StructureTree() { } - - private static boolean flowOrStaticContentNodes(NodeList nodes) { - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - if (node.getNodeType() != Node.ELEMENT_NODE) { - return false; - } - String name = node.getLocalName(); - if (!(name.equals("flow") || name.equals("static-content"))) { - return false; - } - } - return true; - } - - void addPageSequenceStructure(NodeList structureTree) { - assert flowOrStaticContentNodes(structureTree); - pageSequenceStructures.add(structureTree); - } - - /** - * Returns the list of nodes that are the children of the given page sequence. - * - * @param index index of the page sequence, 0-based - * @return its children nodes - */ - public NodeList getPageSequence(int index) { - return (NodeList) pageSequenceStructures.get(index); - } - - /** - * Returns an XML-like representation of the structure trees. - * <p> - * <strong>Note:</strong> use only for debugging purpose, as this method - * performs non-trivial operations. - * </p> - * @return a string representation of this object - */ - public String toString() { - try { - Transformer t = TransformerFactory.newInstance().newTransformer(); - Writer str = new StringWriter(); - for (Iterator iter = pageSequenceStructures.iterator(); iter.hasNext();) { - NodeList nodes = (NodeList) iter.next(); - for (int i = 0, c = nodes.getLength(); i < c; i++) { - t.transform(new DOMSource(nodes.item(i)), new StreamResult(str)); - } - } - return str.toString(); - } catch (Exception e) { - return e.toString(); - } - } - -} diff --git a/src/java/org/apache/fop/accessibility/StructureTree2SAXEventAdapter.java b/src/java/org/apache/fop/accessibility/StructureTree2SAXEventAdapter.java new file mode 100644 index 000000000..79c589f9b --- /dev/null +++ b/src/java/org/apache/fop/accessibility/StructureTree2SAXEventAdapter.java @@ -0,0 +1,123 @@ +/* + * 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.Locale; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import org.apache.fop.fo.FOElementMapping; +import org.apache.fop.fo.extensions.ExtensionElementMapping; +import org.apache.fop.fo.extensions.InternalElementMapping; +import org.apache.fop.render.intermediate.IFConstants; + +/** + * Converts structure tree events to SAX events. + */ +public final class StructureTree2SAXEventAdapter implements StructureTreeEventHandler { + + private final ContentHandler contentHandler; + + private StructureTree2SAXEventAdapter(ContentHandler currentContentHandler) { + this.contentHandler = currentContentHandler; + } + + /** + * Factory method that creates a new instance. + * @param contentHandler The handler that receives SAX events + * @return - + */ + public static StructureTreeEventHandler newInstance(ContentHandler contentHandler) { + return new StructureTree2SAXEventAdapter(contentHandler); + } + + /** {@inheritDoc} */ + public void startPageSequence(Locale locale) { + try { + + contentHandler.startPrefixMapping( + InternalElementMapping.STANDARD_PREFIX, InternalElementMapping.URI); + contentHandler.startPrefixMapping( + ExtensionElementMapping.STANDARD_PREFIX, ExtensionElementMapping.URI); + contentHandler.startElement(IFConstants.NAMESPACE, + IFConstants.EL_STRUCTURE_TREE, IFConstants.EL_STRUCTURE_TREE, + new AttributesImpl()); + } catch (SAXException e) { + throw new RuntimeException(e); + } + } + + /** {@inheritDoc} */ + public void endPageSequence() { + try { + contentHandler.endElement(IFConstants.NAMESPACE, IFConstants.EL_STRUCTURE_TREE, + IFConstants.EL_STRUCTURE_TREE); + contentHandler.endPrefixMapping( + ExtensionElementMapping.STANDARD_PREFIX); + contentHandler.endPrefixMapping( + InternalElementMapping.STANDARD_PREFIX); + + } catch (SAXException e) { + throw new RuntimeException(e); + } + } + + /** {@inheritDoc} */ + public StructureTreeElement startNode(String name, Attributes attributes) { + try { + if (name.equals("#PCDATA")) { + name = "marked-content"; + contentHandler.startElement(IFConstants.NAMESPACE, name, + name, attributes); + } else { + contentHandler.startElement(FOElementMapping.URI, name, + FOElementMapping.STANDARD_PREFIX + ":" + name, + attributes); + } + return null; + } catch (SAXException e) { + throw new RuntimeException(e); + } + } + + /** {@inheritDoc} */ + public void endNode(String name) { + try { + contentHandler.endElement(FOElementMapping.URI, name, + FOElementMapping.STANDARD_PREFIX + ":" + name); + } catch (SAXException e) { + throw new RuntimeException(e); + } + } + + /** {@inheritDoc} */ + public StructureTreeElement startImageNode(String name, Attributes attributes) { + return startNode(name, attributes); + } + + /** {@inheritDoc} */ + public StructureTreeElement startReferencedNode(String name, Attributes attributes) { + return startNode(name, attributes); + } + +} diff --git a/src/java/org/apache/fop/accessibility/StructureTreeBuilder.java b/src/java/org/apache/fop/accessibility/StructureTreeBuilder.java deleted file mode 100644 index 036502e99..000000000 --- a/src/java/org/apache/fop/accessibility/StructureTreeBuilder.java +++ /dev/null @@ -1,95 +0,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 javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.dom.DOMResult; -import javax.xml.transform.sax.SAXTransformerFactory; -import javax.xml.transform.sax.TransformerHandler; - -import org.xml.sax.ContentHandler; -import org.xml.sax.SAXException; - -import org.apache.fop.util.DelegatingContentHandler; - -/** - * Helper class that re-builds a structure tree from what is stored in an - * intermediate XML file (IF XML or Area Tree XML). - */ -public final class StructureTreeBuilder { - - private final SAXTransformerFactory factory; - - private final StructureTree structureTree = new StructureTree(); - - /** - * Creates a new instance. - * - * @param factory a factory internally used to build the structures of page - * sequences - */ - public StructureTreeBuilder(SAXTransformerFactory factory) { - this.factory = factory; - } - - /** - * Returns the structure tree that will result from the parsing. - * - * @return the structure tree built by this object - */ - public StructureTree getStructureTree() { - return structureTree; - } - - /** - * 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. - * - * @return a handler for parsing the <structure-tree> or - * <structureTree> 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 no text node in the structure tree. This is just - * whitespace => ignore - */ - } - - public void endDocument() throws SAXException { - super.endDocument(); - structureTree.addPageSequenceStructure(domResult.getNode().getFirstChild() - .getChildNodes()); - } - }; - } - -} diff --git a/src/java/org/apache/fop/accessibility/StructureTreeElement.java b/src/java/org/apache/fop/accessibility/StructureTreeElement.java new file mode 100644 index 000000000..ebed2c8ff --- /dev/null +++ b/src/java/org/apache/fop/accessibility/StructureTreeElement.java @@ -0,0 +1,28 @@ +/* + * 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; + +/** + * An object that represents the structure of the document in the output format. + * In PDF, an implementation of this interface will typically result into the + * creation of a structure element dictionary (a dictionary of type StructElem). + */ +public interface StructureTreeElement { +} diff --git a/src/java/org/apache/fop/accessibility/StructureTreeEventHandler.java b/src/java/org/apache/fop/accessibility/StructureTreeEventHandler.java new file mode 100644 index 000000000..4b94d61f1 --- /dev/null +++ b/src/java/org/apache/fop/accessibility/StructureTreeEventHandler.java @@ -0,0 +1,79 @@ +/* + * 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.Locale; + +import org.xml.sax.Attributes; + +/** + * Receive notifications relating to the structure tree of an FO document. + * A structure tree is a reduced version of the document's FO tree, containing only the logical + * structure that is used by accessible output formats. + */ +public interface StructureTreeEventHandler { + + /** + * Starts a page sequence structure tree node. + * + * @param locale The locale of the page sequence + */ + void startPageSequence(Locale locale); + + /** + * Starts a structure tree node. + * + * @param name the name of the structure tree node + * @param attributes the node properties + * @return the corresponding structure tree element + */ + StructureTreeElement startNode(String name, Attributes attributes); + + /** + * Ends a structure tree node. + * + * @param name the name of the structure tree node + */ + void endNode(String name); + + /** + * Starts an image node. + * + * @param name the name of the structure tree node + * @param attributes the node properties + * @return the corresponding structure tree element + */ + StructureTreeElement startImageNode(String name, Attributes attributes); + + /** + * Starts a node that can be referenced by other nodes. This is usually a + * node that can have Marked Content References as children. + * + * @param name the name of the structure tree node + * @param attributes the node properties + * @return the corresponding structure tree element + */ + StructureTreeElement startReferencedNode(String name, Attributes attributes); + + /** + * Ends a page sequence structure tree node. + */ + void endPageSequence(); +} diff --git a/src/java/org/apache/fop/accessibility/addPtr.xsl b/src/java/org/apache/fop/accessibility/addPtr.xsl deleted file mode 100644 index b3984d426..000000000 --- a/src/java/org/apache/fop/accessibility/addPtr.xsl +++ /dev/null @@ -1,88 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - 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$ --> -<xsl:stylesheet version="1.0" - xmlns:xsl="http://www.w3.org/1999/XSL/Transform" - xmlns:fo="http://www.w3.org/1999/XSL/Format" - xmlns:foi="http://xmlgraphics.apache.org/fop/internal"> - - <xsl:template name="addPtr"> - <xsl:copy> - <xsl:apply-templates select="@*"/> - <xsl:attribute name="foi:ptr"> - <xsl:value-of select="generate-id()"/> - </xsl:attribute> - <xsl:apply-templates/> - </xsl:copy> - </xsl:template> - - <!-- Block-level Formatting Objects --> - <xsl:template match="fo:block|fo:block-container"> - <xsl:call-template name="addPtr"/> - </xsl:template> - - <!-- Inline-level Formatting Objects --> - <xsl:template match="fo:character|fo:inline|fo:inline-container"> - <xsl:call-template name="addPtr"/> - </xsl:template> - - <xsl:template match="fo:external-graphic|fo:instream-foreign-object"> - <xsl:call-template name="addPtr"/> - </xsl:template> - - <xsl:template match="fo:page-number|fo:page-number-citation|fo:page-number-citation-last"> - <xsl:call-template name="addPtr"/> - </xsl:template> - - <!-- Formatting Objects for Tables --> - <xsl:template match="fo:table-and-caption|fo:table-caption|fo:table"> - <xsl:call-template name="addPtr"/> - </xsl:template> - - <xsl:template match="fo:table-header|fo:table-footer|fo:table-body|fo:table-row|fo:table-cell"> - <xsl:call-template name="addPtr"/> - </xsl:template> - - <!-- Formatting Objects for Lists --> - <xsl:template match="fo:list-block|fo:list-item|fo:list-item-label|fo:list-item-body"> - <xsl:call-template name="addPtr"/> - </xsl:template> - - <!-- Dynamic Effects: Link and Multi Formatting Objects --> - <xsl:template match="fo:basic-link"> - <xsl:call-template name="addPtr"/> - </xsl:template> - - <!-- Out-of-Line Formatting Objects --> - <xsl:template match="fo:float|fo:footnote|fo:footnote-body"> - <xsl:call-template name="addPtr"/> - </xsl:template> - - <!-- Other Formatting Objects --> - <xsl:template match="fo:wrapper|fo:marker"> - <xsl:call-template name="addPtr"/> - </xsl:template> - - - <xsl:template match="@*|node()"> - <xsl:copy> - <xsl:apply-templates select="@*|node()"/> - </xsl:copy> - </xsl:template> - -</xsl:stylesheet> diff --git a/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java b/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java new file mode 100644 index 000000000..47c227e9a --- /dev/null +++ b/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java @@ -0,0 +1,491 @@ +/* + * 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.Stack; + +import org.xml.sax.SAXException; + +import org.apache.fop.accessibility.StructureTreeEventHandler; +import org.apache.fop.fo.DelegatingFOEventHandler; +import org.apache.fop.fo.FOEventHandler; +import org.apache.fop.fo.FOText; +import org.apache.fop.fo.extensions.ExternalDocument; +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.Leader; +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.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.TableColumn; +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.PageSequence; +import org.apache.fop.fo.pagination.Root; +import org.apache.fop.fo.pagination.StaticContent; + +/** + * Allows to create the structure tree of an FO document, by converting FO + * events into appropriate structure tree events. + */ +public class FO2StructureTreeConverter extends DelegatingFOEventHandler { + + /** The top of the {@link converters} stack. */ + private FOEventHandler converter; + + private final Stack<FOEventHandler> converters = new Stack<FOEventHandler>(); + + private final Stack<FOEventRecorder> tableFooterRecorders = new Stack<FOEventRecorder>(); + + private final FOEventHandler structureTreeEventTrigger; + + /** The descendants of some elements like fo:leader must be ignored. */ + private final FOEventHandler eventSwallower = new FOEventHandler() { + }; + + /** + * Creates a new instance. + * + * @param structureTreeEventHandler the object that will hold the structure tree + * @param delegate the FO event handler that must be wrapped by this instance + */ + public FO2StructureTreeConverter(StructureTreeEventHandler structureTreeEventHandler, + FOEventHandler delegate) { + super(delegate); + this.structureTreeEventTrigger = new StructureTreeEventTrigger(structureTreeEventHandler); + this.converter = structureTreeEventTrigger; + } + + @Override + public void startDocument() throws SAXException { + converter.startDocument(); + super.startDocument(); + } + + @Override + public void endDocument() throws SAXException { + converter.endDocument(); + super.endDocument(); + } + + @Override + public void startRoot(Root root) { + converter.startRoot(root); + super.startRoot(root); + } + + @Override + public void endRoot(Root root) { + converter.endRoot(root); + super.endRoot(root); + } + + @Override + public void startPageSequence(PageSequence pageSeq) { + converter.startPageSequence(pageSeq); + super.startPageSequence(pageSeq); + } + + @Override + public void endPageSequence(PageSequence pageSeq) { + converter.endPageSequence(pageSeq); + super.endPageSequence(pageSeq); + } + + @Override + public void startPageNumber(PageNumber pagenum) { + converter.startPageNumber(pagenum); + super.startPageNumber(pagenum); + } + + @Override + public void endPageNumber(PageNumber pagenum) { + converter.endPageNumber(pagenum); + super.endPageNumber(pagenum); + } + + @Override + public void startPageNumberCitation(PageNumberCitation pageCite) { + converter.startPageNumberCitation(pageCite); + super.startPageNumberCitation(pageCite); + } + + @Override + public void endPageNumberCitation(PageNumberCitation pageCite) { + converter.endPageNumberCitation(pageCite); + super.endPageNumberCitation(pageCite); + } + + @Override + public void startPageNumberCitationLast(PageNumberCitationLast pageLast) { + converter.startPageNumberCitationLast(pageLast); + super.startPageNumberCitationLast(pageLast); + } + + @Override + public void endPageNumberCitationLast(PageNumberCitationLast pageLast) { + converter.endPageNumberCitationLast(pageLast); + super.endPageNumberCitationLast(pageLast); + } + + @Override + public void startFlow(Flow fl) { + converter.startFlow(fl); + super.startFlow(fl); + } + + @Override + public void endFlow(Flow fl) { + converter.endFlow(fl); + super.endFlow(fl); + } + + @Override + public void startBlock(Block bl) { + converter.startBlock(bl); + super.startBlock(bl); + } + + @Override + public void endBlock(Block bl) { + converter.endBlock(bl); + super.endBlock(bl); + } + + @Override + public void startBlockContainer(BlockContainer blc) { + converter.startBlockContainer(blc); + super.startBlockContainer(blc); + } + + @Override + public void endBlockContainer(BlockContainer blc) { + converter.endBlockContainer(blc); + super.endBlockContainer(blc); + } + + @Override + public void startInline(Inline inl) { + converter.startInline(inl); + super.startInline(inl); + } + + @Override + public void endInline(Inline inl) { + converter.endInline(inl); + super.endInline(inl); + } + + @Override + public void startTable(Table tbl) { + converter.startTable(tbl); + tableFooterRecorders.push(null); + super.startTable(tbl); + } + + @Override + public void endTable(Table tbl) { + FOEventRecorder tableFooterRecorder = tableFooterRecorders.pop(); + if (tableFooterRecorder != null) { + tableFooterRecorder.replay(converter); + } + converter.endTable(tbl); + super.endTable(tbl); + } + + @Override + public void startColumn(TableColumn tc) { + converter.startColumn(tc); + super.startColumn(tc); + } + + @Override + public void endColumn(TableColumn tc) { + converter.endColumn(tc); + super.endColumn(tc); + } + + @Override + public void startHeader(TableHeader header) { + converter.startHeader(header); + super.startHeader(header); + } + + @Override + public void endHeader(TableHeader header) { + converter.endHeader(header); + super.endHeader(header); + } + + @Override + public void startFooter(TableFooter footer) { + converters.push(converter); + converter = new FOEventRecorder(); + converter.startFooter(footer); + super.startFooter(footer); + } + + @Override + public void endFooter(TableFooter footer) { + converter.endFooter(footer); + /* Replace the dummy table footer with the real one. */ + tableFooterRecorders.pop(); + tableFooterRecorders.push((FOEventRecorder) converter); + converter = converters.pop(); + super.endFooter(footer); + } + + @Override + public void startBody(TableBody body) { + converter.startBody(body); + super.startBody(body); + } + + @Override + public void endBody(TableBody body) { + converter.endBody(body); + super.endBody(body); + } + + @Override + public void startRow(TableRow tr) { + converter.startRow(tr); + super.startRow(tr); + } + + @Override + public void endRow(TableRow tr) { + converter.endRow(tr); + super.endRow(tr); + } + + @Override + public void startCell(TableCell tc) { + converter.startCell(tc); + super.startCell(tc); + } + + @Override + public void endCell(TableCell tc) { + converter.endCell(tc); + super.endCell(tc); + } + + @Override + public void startList(ListBlock lb) { + converter.startList(lb); + super.startList(lb); + } + + @Override + public void endList(ListBlock lb) { + converter.endList(lb); + super.endList(lb); + } + + @Override + public void startListItem(ListItem li) { + converter.startListItem(li); + super.startListItem(li); + } + + @Override + public void endListItem(ListItem li) { + converter.endListItem(li); + super.endListItem(li); + } + + @Override + public void startListLabel(ListItemLabel listItemLabel) { + converter.startListLabel(listItemLabel); + super.startListLabel(listItemLabel); + } + + @Override + public void endListLabel(ListItemLabel listItemLabel) { + converter.endListLabel(listItemLabel); + super.endListLabel(listItemLabel); + } + + @Override + public void startListBody(ListItemBody listItemBody) { + converter.startListBody(listItemBody); + super.startListBody(listItemBody); + } + + @Override + public void endListBody(ListItemBody listItemBody) { + converter.endListBody(listItemBody); + super.endListBody(listItemBody); + } + + @Override + public void startStatic(StaticContent staticContent) { + converter.startStatic(staticContent); + super.startStatic(staticContent); + } + + @Override + public void endStatic(StaticContent statisContent) { + converter.endStatic(statisContent); + super.endStatic(statisContent); + } + + @Override + public void startMarkup() { + converter.startMarkup(); + super.startMarkup(); + } + + @Override + public void endMarkup() { + converter.endMarkup(); + super.endMarkup(); + } + + @Override + public void startLink(BasicLink basicLink) { + converter.startLink(basicLink); + super.startLink(basicLink); + } + + @Override + public void endLink(BasicLink basicLink) { + converter.endLink(basicLink); + super.endLink(basicLink); + } + + @Override + public void image(ExternalGraphic eg) { + converter.image(eg); + super.image(eg); + } + + @Override + public void pageRef() { + converter.pageRef(); + super.pageRef(); + } + + @Override + public void startInstreamForeignObject(InstreamForeignObject ifo) { + converter.startInstreamForeignObject(ifo); + super.startInstreamForeignObject(ifo); + } + + @Override + public void endInstreamForeignObject(InstreamForeignObject ifo) { + converter.endInstreamForeignObject(ifo); + super.endInstreamForeignObject(ifo); + } + + @Override + public void startFootnote(Footnote footnote) { + converter.startFootnote(footnote); + super.startFootnote(footnote); + } + + @Override + public void endFootnote(Footnote footnote) { + converter.endFootnote(footnote); + super.endFootnote(footnote); + } + + @Override + public void startFootnoteBody(FootnoteBody body) { + converter.startFootnoteBody(body); + super.startFootnoteBody(body); + } + + @Override + public void endFootnoteBody(FootnoteBody body) { + converter.endFootnoteBody(body); + super.endFootnoteBody(body); + } + + @Override + public void startLeader(Leader l) { + converters.push(converter); + converter = eventSwallower; + converter.startLeader(l); + super.startLeader(l); + } + + @Override + public void endLeader(Leader l) { + converter.endLeader(l); + converter = converters.pop(); + super.endLeader(l); + } + + @Override + public void startWrapper(Wrapper wrapper) { + converter.startWrapper(wrapper); + super.startWrapper(wrapper); + } + + @Override + public void endWrapper(Wrapper wrapper) { + converter.endWrapper(wrapper); + super.endWrapper(wrapper); + } + + @Override + public void character(Character c) { + converter.character(c); + super.character(c); + } + + @Override + public void characters(FOText foText) { + converter.characters(foText); + super.characters(foText); + } + + @Override + public void startExternalDocument(ExternalDocument document) { + converter.startExternalDocument(document); + super.startExternalDocument(document); + } + + @Override + public void endExternalDocument(ExternalDocument document) { + converter.endExternalDocument(document); + super.endExternalDocument(document); + } + +} diff --git a/src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java b/src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java new file mode 100644 index 000000000..b2b18046d --- /dev/null +++ b/src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java @@ -0,0 +1,508 @@ +/* + * 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.ArrayList; +import java.util.List; + +import org.apache.fop.fo.FOEventHandler; +import org.apache.fop.fo.FOText; +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.Leader; +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.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.TableColumn; +import org.apache.fop.fo.flow.table.TableFooter; +import org.apache.fop.fo.flow.table.TableHeader; +import org.apache.fop.fo.flow.table.TableRow; + +final class FOEventRecorder extends FOEventHandler { + + private interface Event { + void replay(FOEventHandler target); + } + + private final List<Event> events = new ArrayList<Event>(); + + public void replay(FOEventHandler target) { + for (Event event : events) { + event.replay(target); + } + } + + @Override + public void startPageNumber(final PageNumber pagenum) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startPageNumber(pagenum); + } + }); + } + + @Override + public void endPageNumber(final PageNumber pagenum) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endPageNumber(pagenum); + } + }); + } + + @Override + public void startPageNumberCitation(final PageNumberCitation pageCite) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startPageNumberCitation(pageCite); + } + }); + } + + @Override + public void endPageNumberCitation(final PageNumberCitation pageCite) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endPageNumberCitation(pageCite); + } + }); + } + + @Override + public void startPageNumberCitationLast(final PageNumberCitationLast pageLast) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startPageNumberCitationLast(pageLast); + } + }); + } + + @Override + public void endPageNumberCitationLast(final PageNumberCitationLast pageLast) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endPageNumberCitationLast(pageLast); + } + }); + } + + @Override + public void startBlock(final Block bl) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startBlock(bl); + } + }); + } + + @Override + public void endBlock(final Block bl) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endBlock(bl); + } + }); + } + + @Override + public void startBlockContainer(final BlockContainer blc) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startBlockContainer(blc); + } + }); + } + + @Override + public void endBlockContainer(final BlockContainer blc) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endBlockContainer(blc); + } + }); + } + + @Override + public void startInline(final Inline inl) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startInline(inl); + } + }); + } + + @Override + public void endInline(final Inline inl) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endInline(inl); + } + }); + } + + @Override + public void startTable(final Table tbl) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startTable(tbl); + } + }); + } + + @Override + public void endTable(final Table tbl) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endTable(tbl); + } + }); + } + + @Override + public void startColumn(final TableColumn tc) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startColumn(tc); + } + }); + } + + @Override + public void endColumn(final TableColumn tc) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endColumn(tc); + } + }); + } + + @Override + public void startHeader(final TableHeader header) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startHeader(header); + } + }); + } + + @Override + public void endHeader(final TableHeader header) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endHeader(header); + } + }); + } + + @Override + public void startFooter(final TableFooter footer) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startFooter(footer); + } + }); + } + + @Override + public void endFooter(final TableFooter footer) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endFooter(footer); + } + }); + } + + @Override + public void startBody(final TableBody body) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startBody(body); + } + }); + } + + @Override + public void endBody(final TableBody body) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endBody(body); + } + }); + } + + @Override + public void startRow(final TableRow tr) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startRow(tr); + } + }); + } + + @Override + public void endRow(final TableRow tr) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endRow(tr); + } + }); + } + + @Override + public void startCell(final TableCell tc) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startCell(tc); + } + }); + } + + @Override + public void endCell(final TableCell tc) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endCell(tc); + } + }); + } + + @Override + public void startList(final ListBlock lb) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startList(lb); + } + }); + } + + @Override + public void endList(final ListBlock lb) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endList(lb); + } + }); + } + + @Override + public void startListItem(final ListItem li) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startListItem(li); + } + }); + } + + @Override + public void endListItem(final ListItem li) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endListItem(li); + } + }); + } + + @Override + public void startListLabel(final ListItemLabel listItemLabel) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startListLabel(listItemLabel); + } + }); + } + + @Override + public void endListLabel(final ListItemLabel listItemLabel) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endListLabel(listItemLabel); + } + }); + } + + @Override + public void startListBody(final ListItemBody listItemBody) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startListBody(listItemBody); + } + }); + } + + @Override + public void endListBody(final ListItemBody listItemBody) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endListBody(listItemBody); + } + }); + } + + @Override + public void startLink(final BasicLink basicLink) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startLink(basicLink); + } + }); + } + + @Override + public void endLink(final BasicLink basicLink) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endLink(basicLink); + } + }); + } + + @Override + public void image(final ExternalGraphic eg) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.image(eg); + } + }); + } + + @Override + public void startInstreamForeignObject(final InstreamForeignObject ifo) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startInstreamForeignObject(ifo); + } + }); + } + + @Override + public void endInstreamForeignObject(final InstreamForeignObject ifo) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endInstreamForeignObject(ifo); + } + }); + } + + @Override + public void startFootnote(final Footnote footnote) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startFootnote(footnote); + } + }); + } + + @Override + public void endFootnote(final Footnote footnote) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endFootnote(footnote); + } + }); + } + + @Override + public void startFootnoteBody(final FootnoteBody body) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startFootnoteBody(body); + } + }); + } + + @Override + public void endFootnoteBody(final FootnoteBody body) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endFootnoteBody(body); + } + }); + } + + @Override + public void startLeader(final Leader l) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startLeader(l); + } + }); + } + + @Override + public void endLeader(final Leader l) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endLeader(l); + } + }); + } + + @Override + public void startWrapper(final Wrapper wrapper) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.startWrapper(wrapper); + } + }); + } + + @Override + public void endWrapper(final Wrapper wrapper) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.endWrapper(wrapper); + } + }); + } + + @Override + public void character(final Character c) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.character(c); + } + }); + } + + @Override + public void characters(final FOText foText) { + events.add(new Event() { + public void replay(FOEventHandler target) { + target.characters(foText); + } + }); + } + +} diff --git a/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java b/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java new file mode 100644 index 000000000..957f40870 --- /dev/null +++ b/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java @@ -0,0 +1,410 @@ +/* + * 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.Locale; + +import org.xml.sax.SAXException; +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.extensions.ExtensionElementMapping; +import org.apache.fop.fo.flow.AbstractGraphics; +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.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.PageSequence; +import org.apache.fop.fo.pagination.StaticContent; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; +import org.apache.fop.util.XMLUtil; + +/** + * A bridge between {@link FOEventHandler} and {@link StructureTreeEventHandler}. + */ +class StructureTreeEventTrigger extends FOEventHandler { + + private StructureTreeEventHandler structureTreeEventHandler; + + public StructureTreeEventTrigger(StructureTreeEventHandler structureTreeEventHandler) { + this.structureTreeEventHandler = structureTreeEventHandler; + } + + @Override + public void startDocument() throws SAXException { + } + + @Override + public void endDocument() throws SAXException { + } + + @Override + public void startPageSequence(PageSequence pageSeq) { + Locale locale = null; + if (pageSeq.getLanguage() != null) { + if (pageSeq.getCountry() != null) { + locale = new Locale(pageSeq.getLanguage(), pageSeq.getCountry()); + } else { + locale = new Locale(pageSeq.getLanguage()); + } + } + structureTreeEventHandler.startPageSequence(locale); + } + + @Override + public void endPageSequence(PageSequence pageSeq) { + structureTreeEventHandler.endPageSequence(); + } + + @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 startFlow(Flow fl) { + startElement(fl); + } + + @Override + public void endFlow(Flow fl) { + endElement(fl); + } + + @Override + public void startBlock(Block bl) { + startElement(bl); + } + + @Override + public void endBlock(Block bl) { + endElement(bl); + } + + @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) { + startElement(tbl); + } + + @Override + public void endTable(Table tbl) { + endElement(tbl); + } + + @Override + public void startHeader(TableHeader header) { + startElement(header); + } + + @Override + public void endHeader(TableHeader header) { + endElement(header); + } + + @Override + public void startFooter(TableFooter footer) { + startElement(footer); + } + + @Override + public void endFooter(TableFooter footer) { + endElement(footer); + } + + @Override + public void startBody(TableBody body) { + startElement(body); + } + + @Override + public void endBody(TableBody body) { + endElement(body); + } + + @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(); + int colSpan = tc.getNumberColumnsSpanned(); + if (colSpan > 1) { + addNoNamespaceAttribute(attributes, "number-columns-spanned", + Integer.toString(colSpan)); + } + startElement(tc, attributes); + } + + @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 startStatic(StaticContent staticContent) { + startElement(staticContent); + } + + @Override + public void endStatic(StaticContent statisContent) { + endElement(statisContent); + } + + @Override + public void startLink(BasicLink basicLink) { + startElementWithID(basicLink); + } + + @Override + public void endLink(BasicLink basicLink) { + endElement(basicLink); + } + + @Override + public void image(ExternalGraphic eg) { + startElementWithIDAndAltText(eg); + endElement(eg); + } + + @Override + public void startInstreamForeignObject(InstreamForeignObject ifo) { + startElementWithIDAndAltText(ifo); + } + + @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 character(Character c) { + startElementWithID(c); + endElement(c); + } + + @Override + public void characters(FOText foText) { + startElementWithID(foText); + endElement(foText); + } + + + private void startElement(FONode node) { + startElement(node, new AttributesImpl()); + } + + private void startElementWithID(FONode node) { + AttributesImpl attributes = new AttributesImpl(); + String localName = node.getLocalName(); + if (node instanceof CommonAccessibilityHolder) { + addRole((CommonAccessibilityHolder) node, attributes); + } + node.setStructureTreeElement( + structureTreeEventHandler.startReferencedNode(localName, attributes)); + } + + private void startElementWithIDAndAltText(AbstractGraphics node) { + AttributesImpl attributes = new AttributesImpl(); + String localName = node.getLocalName(); + addRole(node, attributes); + addAttribute(attributes, ExtensionElementMapping.URI, "alt-text", + ExtensionElementMapping.STANDARD_PREFIX, node.getAltText()); + node.setStructureTreeElement( + structureTreeEventHandler.startImageNode(localName, attributes)); + } + + private StructureTreeElement startElement(FONode node, AttributesImpl attributes) { + String localName = node.getLocalName(); + if (node instanceof CommonAccessibilityHolder) { + addRole((CommonAccessibilityHolder) node, attributes); + } + return structureTreeEventHandler.startNode(localName, attributes); + } + + 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); + } + +} diff --git a/src/java/org/apache/fop/apps/FOUserAgent.java b/src/java/org/apache/fop/apps/FOUserAgent.java index 071066e04..7d1736586 100644 --- a/src/java/org/apache/fop/apps/FOUserAgent.java +++ b/src/java/org/apache/fop/apps/FOUserAgent.java @@ -39,7 +39,8 @@ import org.apache.xmlgraphics.util.UnitConv; import org.apache.fop.Version; import org.apache.fop.accessibility.Accessibility; -import org.apache.fop.accessibility.StructureTree; +import org.apache.fop.accessibility.DummyStructureTreeEventHandler; +import org.apache.fop.accessibility.StructureTreeEventHandler; import org.apache.fop.events.DefaultEventBroadcaster; import org.apache.fop.events.Event; import org.apache.fop.events.EventBroadcaster; @@ -101,8 +102,8 @@ public class FOUserAgent { private boolean locatorEnabled = true; // true by default (for error messages). private boolean conserveMemoryPolicy = false; private EventBroadcaster eventBroadcaster = new FOPEventBroadcaster(); - - private StructureTree structureTree; + private StructureTreeEventHandler structureTreeEventHandler + = DummyStructureTreeEventHandler.INSTANCE; /** Producer: Metadata element for the system/software that produces * the document. (Some renderers can store this in the document.) @@ -173,6 +174,9 @@ public class FOUserAgent { * @param documentHandler the document handler instance to use */ public void setDocumentHandlerOverride(IFDocumentHandler documentHandler) { + if (isAccessibilityEnabled()) { + setStructureTreeEventHandler(documentHandler.getStructureTreeEventHandler()); + } this.documentHandlerOverride = documentHandler; } @@ -674,24 +678,23 @@ public class FOUserAgent { } /** - * Sets the document's structure tree, for use by accessible output formats. + * Sets the document's structure tree event handler, for use by accessible + * output formats. * - * @param structureTree a simplified version of the FO tree, retaining only - * its logical structure + * @param structureTreeEventHandler The structure tree event handler to set */ - public void setStructureTree(StructureTree structureTree) { - this.structureTree = structureTree; + public void setStructureTreeEventHandler(StructureTreeEventHandler structureTreeEventHandler) { + this.structureTreeEventHandler = structureTreeEventHandler; } /** - * Returns the document's structure tree, for use by accessible output - * formats. + * Returns the document's structure tree event handler, for use by + * accessible output formats. * - * @return a simplified version of the FO tree, retaining only its logical - * structure + * @return The structure tree event handler */ - public StructureTree getStructureTree() { - return this.structureTree; + public StructureTreeEventHandler getStructureTreeEventHandler() { + return this.structureTreeEventHandler; } } diff --git a/src/java/org/apache/fop/apps/Fop.java b/src/java/org/apache/fop/apps/Fop.java index 07fd4c0a4..0527ea290 100644 --- a/src/java/org/apache/fop/apps/Fop.java +++ b/src/java/org/apache/fop/apps/Fop.java @@ -24,7 +24,6 @@ import java.io.OutputStream; import org.xml.sax.helpers.DefaultHandler; -import org.apache.fop.accessibility.Accessibility; import org.apache.fop.fo.FOTreeBuilder; /** @@ -111,11 +110,7 @@ public class Fop { if (foTreeBuilder == null) { createDefaultHandler(); } - if (this.foUserAgent.isAccessibilityEnabled()) { - return Accessibility.decorateDefaultHandler(this.foTreeBuilder, foUserAgent); - } else { - return this.foTreeBuilder; - } + return this.foTreeBuilder; } /** diff --git a/src/java/org/apache/fop/area/AreaTreeHandler.java b/src/java/org/apache/fop/area/AreaTreeHandler.java index f45245bfe..6448e7c5d 100644 --- a/src/java/org/apache/fop/area/AreaTreeHandler.java +++ b/src/java/org/apache/fop/area/AreaTreeHandler.java @@ -22,6 +22,7 @@ package org.apache.fop.area; // Java import java.io.OutputStream; import java.util.List; +import java.util.Locale; import org.xml.sax.SAXException; @@ -182,6 +183,14 @@ public class AreaTreeHandler extends FOEventHandler { } } + @Override + public void startRoot(Root root) { + Locale locale = root.getLocale(); + if (locale != null) { + model.setDocumentLocale(locale); + } + } + /** * finish the previous pageSequence */ diff --git a/src/java/org/apache/fop/area/AreaTreeModel.java b/src/java/org/apache/fop/area/AreaTreeModel.java index 2a1f14ab5..941f6cea2 100644 --- a/src/java/org/apache/fop/area/AreaTreeModel.java +++ b/src/java/org/apache/fop/area/AreaTreeModel.java @@ -21,6 +21,7 @@ package org.apache.fop.area; // Java import java.util.List; +import java.util.Locale; import org.xml.sax.SAXException; @@ -123,4 +124,11 @@ public class AreaTreeModel { public PageViewport getPage(int seq, int count) { return pageSequenceList.get(seq - 1).getPage(count); } + + /** + * + * @param locale The locale of the document + */ + public void setDocumentLocale(Locale locale) { + } } diff --git a/src/java/org/apache/fop/area/AreaTreeParser.java b/src/java/org/apache/fop/area/AreaTreeParser.java index d2f52f991..d8c22f2e8 100644 --- a/src/java/org/apache/fop/area/AreaTreeParser.java +++ b/src/java/org/apache/fop/area/AreaTreeParser.java @@ -58,8 +58,6 @@ import org.apache.xmlgraphics.image.loader.ImageSessionContext; import org.apache.xmlgraphics.util.QName; import org.apache.fop.ResourceEventProducer; -import org.apache.fop.accessibility.AccessibilityEventProducer; -import org.apache.fop.accessibility.StructureTreeBuilder; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.area.Trait.Background; import org.apache.fop.area.Trait.InternalLink; @@ -69,11 +67,11 @@ import org.apache.fop.area.inline.Image; import org.apache.fop.area.inline.InlineArea; import org.apache.fop.area.inline.InlineBlockParent; import org.apache.fop.area.inline.InlineParent; +import org.apache.fop.area.inline.InlineViewport; import org.apache.fop.area.inline.Leader; import org.apache.fop.area.inline.Space; import org.apache.fop.area.inline.SpaceArea; import org.apache.fop.area.inline.TextArea; -import org.apache.fop.area.inline.InlineViewport; import org.apache.fop.area.inline.WordArea; import org.apache.fop.fo.ElementMappingRegistry; import org.apache.fop.fo.expr.PropertyException; @@ -86,7 +84,6 @@ import org.apache.fop.util.ContentHandlerFactory; import org.apache.fop.util.ContentHandlerFactoryRegistry; import org.apache.fop.util.ConversionUtils; import org.apache.fop.util.DefaultErrorListener; -import org.apache.fop.util.DelegatingContentHandler; import org.apache.fop.util.XMLConstants; import org.apache.fop.util.XMLUtil; @@ -166,27 +163,6 @@ public class AreaTreeParser { private DOMImplementation domImplementation; private Locator locator; - - private StructureTreeBuilder structureTreeBuilder; - - private ContentHandler structureTreeBuilderWrapper; - - private Attributes pageSequenceAttributes; - - private final class StructureTreeBuilderWrapper extends DelegatingContentHandler { - - private StructureTreeBuilderWrapper() - throws SAXException { - super(structureTreeBuilder.getHandlerForNextPageSequence()); - } - - public void endDocument() throws SAXException { - super.endDocument(); - startAreaTreeElement("pageSequence", pageSequenceAttributes); - pageSequenceAttributes = null; - } - } - public Handler(AreaTreeModel treeModel, FOUserAgent userAgent, ElementMappingRegistry elementMappingRegistry) { this.treeModel = treeModel; @@ -223,11 +199,6 @@ public class AreaTreeParser { makers.put("bookmarkTree", new BookmarkTreeMaker()); makers.put("bookmark", new BookmarkMaker()); makers.put("destination", new DestinationMaker()); - - if (userAgent.isAccessibilityEnabled()) { - structureTreeBuilder = new StructureTreeBuilder(tFactory); - userAgent.setStructureTree(structureTreeBuilder.getStructureTree()); - } } private Area findAreaType(Class clazz) { @@ -308,32 +279,15 @@ public class AreaTreeParser { } else { boolean handled = true; if ("".equals(uri)) { - if (localName.equals("pageSequence") && userAgent.isAccessibilityEnabled()) { - structureTreeBuilderWrapper = new StructureTreeBuilderWrapper(); - pageSequenceAttributes = new AttributesImpl(attributes); - } else if (localName.equals("structureTree")) { - if (userAgent.isAccessibilityEnabled()) { - delegate = structureTreeBuilderWrapper; - } else { - /* Delegate to a handler that does nothing */ - delegate = new DefaultHandler(); - } + if (localName.equals("structureTree")) { + + /* The area tree parser no longer supports the structure tree. */ + delegate = new DefaultHandler(); + delegateStack.push(qName); delegate.startDocument(); delegate.startElement(uri, localName, qName, attributes); } else { - if (pageSequenceAttributes != null) { - /* - * This means that no structure-element tag was - * found in the XML, otherwise a - * StructureTreeBuilderWrapper object would have - * been created, which would have reset the - * pageSequenceAttributes field. - */ - AccessibilityEventProducer.Provider - .get(userAgent.getEventBroadcaster()) - .noStructureTreeInXML(this); - } handled = startAreaTreeElement(localName, attributes); } } else { @@ -772,7 +726,6 @@ public class AreaTreeParser { setTraits(attributes, ip, SUBSET_BOX); setTraits(attributes, ip, SUBSET_COLOR); setTraits(attributes, ip, SUBSET_LINK); - setPtr(ip, attributes); Area parent = (Area)areaStack.peek(); parent.addChildArea(ip); areaStack.push(ip); @@ -821,7 +774,6 @@ public class AreaTreeParser { "tlsadjust", 0)); text.setTextWordSpaceAdjust(XMLUtil.getAttributeAsInt(attributes, "twsadjust", 0)); - setPtr(text, attributes); Area parent = (Area)areaStack.peek(); parent.addChildArea(text); areaStack.push(text); @@ -914,7 +866,6 @@ public class AreaTreeParser { viewport.setContentPosition(XMLUtil.getAttributeAsRectangle2D(attributes, "pos")); viewport.setClip(XMLUtil.getAttributeAsBoolean(attributes, "clip", false)); viewport.setOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0)); - setPtr(viewport, attributes); Area parent = (Area)areaStack.peek(); parent.addChildArea(viewport); areaStack.push(viewport); @@ -933,7 +884,6 @@ public class AreaTreeParser { transferForeignObjects(attributes, image); setAreaAttributes(attributes, image); setTraits(attributes, image, SUBSET_COMMON); - setPtr(image, attributes); getCurrentViewport().setContent(image); } } @@ -1218,13 +1168,6 @@ public class AreaTreeParser { } } - private void setPtr(Area area, Attributes attributes) { - String ptr = attributes.getValue("ptr"); - if (ptr != null) { - area.addTrait(Trait.PTR, ptr); - } - } - /** {@inheritDoc} */ public void characters(char[] ch, int start, int length) throws SAXException { if (delegate != null) { diff --git a/src/java/org/apache/fop/area/RenderPagesModel.java b/src/java/org/apache/fop/area/RenderPagesModel.java index afec850f8..cef2552c8 100644 --- a/src/java/org/apache/fop/area/RenderPagesModel.java +++ b/src/java/org/apache/fop/area/RenderPagesModel.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; import java.util.List; +import java.util.Locale; import org.xml.sax.SAXException; @@ -83,6 +84,11 @@ public class RenderPagesModel extends AreaTreeModel { } } + @Override + public void setDocumentLocale(Locale locale) { + renderer.setDocumentLocale(locale); + } + /** {@inheritDoc} */ @Override public void startPageSequence(PageSequence pageSequence) { diff --git a/src/java/org/apache/fop/area/Trait.java b/src/java/org/apache/fop/area/Trait.java index 042b65337..d9194559d 100644 --- a/src/java/org/apache/fop/area/Trait.java +++ b/src/java/org/apache/fop/area/Trait.java @@ -153,8 +153,8 @@ public final class Trait implements Serializable { /** Trait for color of linethrough decorations when rendering inline parent. */ public static final Integer LINETHROUGH_COLOR = 36; - /** The ptr trait. Used for accessibility */ - public static final Integer PTR = 37; + /** For navigation in the document structure. */ + public static final Integer STRUCTURE_TREE_ELEMENT = 37; /** Maximum value used by trait keys */ public static final int MAX_TRAIT_KEY = 37; @@ -186,7 +186,7 @@ public final class Trait implements Serializable { static { // Create a hashmap mapping trait code to name for external representation //put(ID_LINK, new TraitInfo("id-link", String.class)); - put(PTR, new TraitInfo("ptr", String.class)); + put(STRUCTURE_TREE_ELEMENT, new TraitInfo("structure-tree-element", String.class)); put(INTERNAL_LINK, new TraitInfo("internal-link", InternalLink.class)); put(EXTERNAL_LINK, new TraitInfo("external-link", ExternalLink.class)); put(FONT, new TraitInfo("font", FontTriplet.class)); diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java index 726d4d89e..a2b982269 100644 --- a/src/java/org/apache/fop/fo/Constants.java +++ b/src/java/org/apache/fop/fo/Constants.java @@ -771,18 +771,16 @@ public interface Constants { * multi-column layouts. */ int PR_X_DISABLE_COLUMN_BALANCING = 273; - /** Property constant - FOP proprietary: FOP internal use for accessibility */ - int PR_X_PTR = 274; /** * Property constant - FOP proprietary: alternative text for e-g and i-f-o. * Used for accessibility. */ - int PR_X_ALT_TEXT = 275; + int PR_X_ALT_TEXT = 274; /** Property constant - FOP proprietary prototype (in XSL-FO 2.0 Requirements) */ - int PR_X_XML_BASE = 276; + int PR_X_XML_BASE = 275; /** Number of property constants defined */ - int PROPERTY_COUNT = 276; + int PROPERTY_COUNT = 275; // compound property constants diff --git a/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java b/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java new file mode 100644 index 000000000..e64106a80 --- /dev/null +++ b/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java @@ -0,0 +1,404 @@ +/* + * 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.fo; + +import org.xml.sax.SAXException; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.extensions.ExternalDocument; +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.Leader; +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.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.TableColumn; +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.PageSequence; +import org.apache.fop.fo.pagination.Root; +import org.apache.fop.fo.pagination.StaticContent; +import org.apache.fop.fonts.FontInfo; + +/** + * This class delegates all FO events to another FOEventHandler instance. + */ +public abstract class DelegatingFOEventHandler extends FOEventHandler { + + private final FOEventHandler delegate; + + /** + * Creates a new instance that delegates events to the given object. + * + * @param delegate the object to which all FO events will be forwarded + */ + public DelegatingFOEventHandler(FOEventHandler delegate) { + super(delegate.getUserAgent()); + this.delegate = delegate; + } + + @Override + public FOUserAgent getUserAgent() { + return delegate.getUserAgent(); + } + + @Override + public FontInfo getFontInfo() { + return delegate.getFontInfo(); + } + + @Override + public void startDocument() throws SAXException { + delegate.startDocument(); + } + + @Override + public void endDocument() throws SAXException { + delegate.endDocument(); + } + + @Override + public void startRoot(Root root) { + delegate.startRoot(root); + } + + @Override + public void endRoot(Root root) { + delegate.endRoot(root); + } + + @Override + public void startPageSequence(PageSequence pageSeq) { + delegate.startPageSequence(pageSeq); + } + + @Override + public void endPageSequence(PageSequence pageSeq) { + delegate.endPageSequence(pageSeq); + } + + @Override + public void startPageNumber(PageNumber pagenum) { + delegate.startPageNumber(pagenum); + } + + @Override + public void endPageNumber(PageNumber pagenum) { + delegate.endPageNumber(pagenum); + } + + @Override + public void startPageNumberCitation(PageNumberCitation pageCite) { + delegate.startPageNumberCitation(pageCite); + } + + @Override + public void endPageNumberCitation(PageNumberCitation pageCite) { + delegate.endPageNumberCitation(pageCite); + } + + @Override + public void startPageNumberCitationLast(PageNumberCitationLast pageLast) { + delegate.startPageNumberCitationLast(pageLast); + } + + @Override + public void endPageNumberCitationLast(PageNumberCitationLast pageLast) { + delegate.endPageNumberCitationLast(pageLast); + } + + @Override + public void startFlow(Flow fl) { + delegate.startFlow(fl); + } + + @Override + public void endFlow(Flow fl) { + delegate.endFlow(fl); + } + + @Override + public void startBlock(Block bl) { + delegate.startBlock(bl); + } + + @Override + public void endBlock(Block bl) { + delegate.endBlock(bl); + } + + @Override + public void startBlockContainer(BlockContainer blc) { + delegate.startBlockContainer(blc); + } + + @Override + public void endBlockContainer(BlockContainer blc) { + delegate.endBlockContainer(blc); + } + + @Override + public void startInline(Inline inl) { + delegate.startInline(inl); + } + + @Override + public void endInline(Inline inl) { + delegate.endInline(inl); + } + + @Override + public void startTable(Table tbl) { + delegate.startTable(tbl); + } + + @Override + public void endTable(Table tbl) { + delegate.endTable(tbl); + } + + @Override + public void startColumn(TableColumn tc) { + delegate.startColumn(tc); + } + + @Override + public void endColumn(TableColumn tc) { + delegate.endColumn(tc); + } + + @Override + public void startHeader(TableHeader header) { + delegate.startHeader(header); + } + + @Override + public void endHeader(TableHeader header) { + delegate.endHeader(header); + } + + @Override + public void startFooter(TableFooter footer) { + delegate.startFooter(footer); + } + + @Override + public void endFooter(TableFooter footer) { + delegate.endFooter(footer); + } + + @Override + public void startBody(TableBody body) { + delegate.startBody(body); + } + + @Override + public void endBody(TableBody body) { + delegate.endBody(body); + } + + @Override + public void startRow(TableRow tr) { + delegate.startRow(tr); + } + + @Override + public void endRow(TableRow tr) { + delegate.endRow(tr); + } + + @Override + public void startCell(TableCell tc) { + delegate.startCell(tc); + } + + @Override + public void endCell(TableCell tc) { + delegate.endCell(tc); + } + + @Override + public void startList(ListBlock lb) { + delegate.startList(lb); + } + + @Override + public void endList(ListBlock lb) { + delegate.endList(lb); + } + + @Override + public void startListItem(ListItem li) { + delegate.startListItem(li); + } + + @Override + public void endListItem(ListItem li) { + delegate.endListItem(li); + } + + @Override + public void startListLabel(ListItemLabel listItemLabel) { + delegate.startListLabel(listItemLabel); + } + + @Override + public void endListLabel(ListItemLabel listItemLabel) { + delegate.endListLabel(listItemLabel); + } + + @Override + public void startListBody(ListItemBody listItemBody) { + delegate.startListBody(listItemBody); + } + + @Override + public void endListBody(ListItemBody listItemBody) { + delegate.endListBody(listItemBody); + } + + @Override + public void startStatic(StaticContent staticContent) { + delegate.startStatic(staticContent); + } + + @Override + public void endStatic(StaticContent statisContent) { + delegate.endStatic(statisContent); + } + + @Override + public void startMarkup() { + delegate.startMarkup(); + } + + @Override + public void endMarkup() { + delegate.endMarkup(); + } + + @Override + public void startLink(BasicLink basicLink) { + delegate.startLink(basicLink); + } + + @Override + public void endLink(BasicLink basicLink) { + delegate.endLink(basicLink); + } + + @Override + public void image(ExternalGraphic eg) { + delegate.image(eg); + } + + @Override + public void pageRef() { + delegate.pageRef(); + } + + @Override + public void startInstreamForeignObject(InstreamForeignObject ifo) { + delegate.startInstreamForeignObject(ifo); + } + + @Override + public void endInstreamForeignObject(InstreamForeignObject ifo) { + delegate.endInstreamForeignObject(ifo); + } + + @Override + public void startFootnote(Footnote footnote) { + delegate.startFootnote(footnote); + } + + @Override + public void endFootnote(Footnote footnote) { + delegate.endFootnote(footnote); + } + + @Override + public void startFootnoteBody(FootnoteBody body) { + delegate.startFootnoteBody(body); + } + + @Override + public void endFootnoteBody(FootnoteBody body) { + delegate.endFootnoteBody(body); + } + + @Override + public void startLeader(Leader l) { + delegate.startLeader(l); + } + + @Override + public void endLeader(Leader l) { + delegate.endLeader(l); + } + + @Override + public void startWrapper(Wrapper wrapper) { + delegate.startWrapper(wrapper); + } + + @Override + public void endWrapper(Wrapper wrapper) { + delegate.endWrapper(wrapper); + } + + @Override + public void character(Character c) { + delegate.character(c); + } + + @Override + public void characters(FOText foText) { + delegate.characters(foText); + } + + @Override + public void startExternalDocument(ExternalDocument document) { + delegate.startExternalDocument(document); + } + + @Override + public void endExternalDocument(ExternalDocument document) { + delegate.endExternalDocument(document); + } + +} diff --git a/src/java/org/apache/fop/fo/FOElementMapping.java b/src/java/org/apache/fop/fo/FOElementMapping.java index 495cd274b..b087e1d9c 100644 --- a/src/java/org/apache/fop/fo/FOElementMapping.java +++ b/src/java/org/apache/fop/fo/FOElementMapping.java @@ -34,6 +34,9 @@ public class FOElementMapping extends ElementMapping { /** The XSL-FO namespace URI */ public static final String URI = "http://www.w3.org/1999/XSL/Format"; + /** Standard prefix */ + public static final String STANDARD_PREFIX = "fo"; + /** * Basic constructor; inititializes the namespace URI for the fo: namespace */ @@ -143,7 +146,7 @@ public class FOElementMapping extends ElementMapping { /** {@inheritDoc} */ public String getStandardPrefix() { - return "fo"; + return STANDARD_PREFIX; } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/fo/FOEventHandler.java b/src/java/org/apache/fop/fo/FOEventHandler.java index e280af2d7..b7bcd4215 100644 --- a/src/java/org/apache/fop/fo/FOEventHandler.java +++ b/src/java/org/apache/fop/fo/FOEventHandler.java @@ -35,9 +35,12 @@ import org.apache.fop.fo.flow.InstreamForeignObject; import org.apache.fop.fo.flow.Leader; 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.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; @@ -47,6 +50,8 @@ 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.PageSequence; +import org.apache.fop.fo.pagination.Root; +import org.apache.fop.fo.pagination.StaticContent; import org.apache.fop.fonts.FontEventAdapter; import org.apache.fop.fonts.FontInfo; @@ -83,6 +88,10 @@ public abstract class FOEventHandler { this.fontInfo.setEventListener(new FontEventAdapter(foUserAgent.getEventBroadcaster())); } + /** Constructor for sub-classes that do not need an {@link FOUserAgent} instance. */ + protected FOEventHandler() { + } + /** * Returns the User Agent object associated with this FOEventHandler. * @return the User Agent object @@ -113,6 +122,14 @@ public abstract class FOEventHandler { public void endDocument() throws SAXException { } + /** {@inheritDoc} */ + public void startRoot(Root root) { + } + + /** {@inheritDoc} */ + public void endRoot(Root root) { + } + /** * * @param pageSeq PageSequence that is starting. @@ -359,39 +376,45 @@ public abstract class FOEventHandler { /** * Process start of a ListLabel. + * @param listItemLabel ListItemLabel that is starting */ - public void startListLabel() { + public void startListLabel(ListItemLabel listItemLabel) { } /** * Process end of a ListLabel. + * @param listItemLabel ListItemLabel that is ending */ - public void endListLabel() { + public void endListLabel(ListItemLabel listItemLabel) { } /** * Process start of a ListBody. + * @param listItemBody ListItemBody that is starting */ - public void startListBody() { + public void startListBody(ListItemBody listItemBody) { } /** * Process end of a ListBody. + * @param listItemBody ListItemBody that is ending */ - public void endListBody() { + public void endListBody(ListItemBody listItemBody) { } // Static Regions /** * Process start of a Static. + * @param staticContent StaticContent that is starting */ - public void startStatic() { + public void startStatic(StaticContent staticContent) { } /** * Process end of a Static. + * @param statisContent StaticContent that is ending */ - public void endStatic() { + public void endStatic(StaticContent statisContent) { } @@ -409,15 +432,16 @@ public abstract class FOEventHandler { /** * Process start of a Link. - * @param basicLink BasicLink that is ending + * @param basicLink BasicLink that is starting */ public void startLink(BasicLink basicLink) { } /** * Process end of a Link. + * @param basicLink BasicLink that is ending */ - public void endLink() { + public void endLink(BasicLink basicLink) { } /** @@ -434,10 +458,17 @@ public abstract class FOEventHandler { } /** - * Process an InstreamForeignObject. - * @param ifo InstreamForeignObject to process. + * Process the start of an InstreamForeignObject. + * @param ifo InstreamForeignObject that is starting + */ + public void startInstreamForeignObject(InstreamForeignObject ifo) { + } + + /** + * Process the end of an InstreamForeignObject. + * @param ifo InstreamForeignObject that is ending */ - public void foreignObject(InstreamForeignObject ifo) { + public void endInstreamForeignObject(InstreamForeignObject ifo) { } /** @@ -469,10 +500,33 @@ public abstract class FOEventHandler { } /** - * Process a Leader. - * @param l Leader to process. + * Process the start of a Leader. + * @param l Leader that is starting + */ + public void startLeader(Leader l) { + } + + /** + * Process the end of a Leader. + * @param l Leader that is ending + */ + public void endLeader(Leader l) { + } + + /** + * Process the start of a wrapper. + * + * @param wrapper wrapper that is starting + */ + public void startWrapper(Wrapper wrapper) { + } + + /** + * Process the ending of a wrapper. + * + * @param wrapper wrapper that is ending */ - public void leader(Leader l) { + public void endWrapper(Wrapper wrapper) { } /** @@ -484,11 +538,9 @@ public abstract class FOEventHandler { /** * Process character data. - * @param data Array of characters to process. - * @param start Offset for characters to process. - * @param length Portion of array to process. + * @param foText text to process */ - public void characters(char[] data, int start, int length) { + public void characters(FOText foText) { } /** diff --git a/src/java/org/apache/fop/fo/FONode.java b/src/java/org/apache/fop/fo/FONode.java index c9c04aa4e..b63c49ae2 100644 --- a/src/java/org/apache/fop/fo/FONode.java +++ b/src/java/org/apache/fop/fo/FONode.java @@ -32,6 +32,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.util.QName; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fo.extensions.ExtensionAttachment; @@ -912,4 +913,13 @@ public abstract class FONode implements Cloneable { } + /** + * Sets the structure tree element. + * + * @param structureTreeElement set. + */ + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + throw new UnsupportedOperationException(); + } + } diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index cf4eef3a5..172aa5329 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -2533,12 +2533,6 @@ public final class FOPropertyMapping implements Constants { m.setDefault(""); addPropertyMaker("id", m); - // foi:ptr, used for accessibility - m = new StringProperty.Maker(PR_X_PTR); - m.setInherited(false); - m.setDefault(""); - addPropertyMaker("foi:ptr", m); - // fox:alt-text, used for accessibility m = new StringProperty.Maker(PR_X_ALT_TEXT); m.setInherited(false); diff --git a/src/java/org/apache/fop/fo/FOText.java b/src/java/org/apache/fop/fo/FOText.java index 5db11f731..43b58685f 100644 --- a/src/java/org/apache/fop/fo/FOText.java +++ b/src/java/org/apache/fop/fo/FOText.java @@ -25,6 +25,7 @@ import java.util.NoSuchElementException; import org.xml.sax.Locator; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.flow.Block; @@ -79,6 +80,8 @@ public class FOText extends FONode implements CharSequence { /** Holds the text decoration values. May be null */ private CommonTextDecoration textDecoration; + private StructureTreeElement structureTreeElement; + private static final int IS_WORD_CHAR_FALSE = 0; private static final int IS_WORD_CHAR_TRUE = 1; private static final int IS_WORD_CHAR_MAYBE = 2; @@ -115,25 +118,14 @@ public class FOText extends FONode implements CharSequence { /** * Return the array of characters for this instance. * - * @return a char array containing the text + * @return a char sequence containing the text */ - public char[] getCharArray() { - + public CharSequence getCharSequence() { if (this.charBuffer == null) { return null; } - - if (this.charBuffer.hasArray()) { - return this.charBuffer.array(); - } - - // only if the buffer implementation has - // no accessible backing array, return a new one - char[] ca = new char[this.charBuffer.limit()]; this.charBuffer.rewind(); - this.charBuffer.get(ca); - return ca; - + return this.charBuffer.asReadOnlyBuffer().subSequence(0, this.charBuffer.limit()); } /** {@inheritDoc} */ @@ -176,8 +168,7 @@ public class FOText extends FONode implements CharSequence { /** {@inheritDoc} */ protected void endOfNode() throws FOPException { super.endOfNode(); - getFOEventHandler().characters( - this.getCharArray(), 0, this.charBuffer.limit()); + getFOEventHandler().characters(this); } /** {@inheritDoc} */ @@ -670,4 +661,15 @@ public class FOText extends FONode implements CharSequence { this.charBuffer.rewind(); } } + + @Override + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; + } + + /** @return the structure tree element. */ + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; + } + } diff --git a/src/java/org/apache/fop/fo/FOTreeBuilder.java b/src/java/org/apache/fop/fo/FOTreeBuilder.java index 629e87e6b..38f74d8fd 100644 --- a/src/java/org/apache/fop/fo/FOTreeBuilder.java +++ b/src/java/org/apache/fop/fo/FOTreeBuilder.java @@ -33,6 +33,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.util.QName; +import org.apache.fop.accessibility.fo.FO2StructureTreeConverter; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.FormattingResults; @@ -104,6 +105,10 @@ public class FOTreeBuilder extends DefaultHandler { //one of the RTF-, MIF- etc. Handlers. foEventHandler = foUserAgent.getRendererFactory().createFOEventHandler( foUserAgent, outputFormat, stream); + if (userAgent.isAccessibilityEnabled()) { + foEventHandler = new FO2StructureTreeConverter( + foUserAgent.getStructureTreeEventHandler(), foEventHandler); + } builderContext = new FOTreeBuilderContext(); builderContext.setPropertyListMaker(new PropertyListMaker() { public PropertyList make(FObj fobj, PropertyList parentPropertyList) { diff --git a/src/java/org/apache/fop/fo/PropertyList.java b/src/java/org/apache/fop/fo/PropertyList.java index a1634e868..65ebd40a2 100644 --- a/src/java/org/apache/fop/fo/PropertyList.java +++ b/src/java/org/apache/fop/fo/PropertyList.java @@ -30,7 +30,6 @@ import org.apache.xmlgraphics.util.QName; import org.apache.fop.apps.FopFactory; import org.apache.fop.fo.expr.PropertyException; import org.apache.fop.fo.properties.CommonAbsolutePosition; -import org.apache.fop.fo.properties.CommonAccessibility; import org.apache.fop.fo.properties.CommonAural; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonFont; @@ -601,15 +600,6 @@ public abstract class PropertyList { } /** - * Constructs a CommonAccessibility object. - * @return the CommonAccessibility object - * @throws PropertyException if there's a problem while processing the properties - */ - public CommonAccessibility getAccessibilityProps() throws PropertyException { - return new CommonAccessibility(this); - } - - /** * Constructs a CommonAural object. * @return the CommonAural object * @throws PropertyException if there's a problem while processing the properties diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java index 80a569c90..f0e03399f 100644 --- a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java @@ -37,6 +37,9 @@ public class ExtensionElementMapping extends ElementMapping { /** The FOP extension namespace URI */ public static final String URI = "http://xmlgraphics.apache.org/fop/extensions"; + /** The standard XML prefix for elements and attributes in this namespace. */ + public static final String STANDARD_PREFIX = "fox"; + private static final Set<String> PROPERTY_ATTRIBUTES = new java.util.HashSet<String>(); @@ -85,7 +88,7 @@ public class ExtensionElementMapping extends ElementMapping { /** {@inheritDoc} */ public String getStandardPrefix() { - return "fox"; + return STANDARD_PREFIX; } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java b/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java index 4798f5500..687952d25 100644 --- a/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java @@ -34,11 +34,21 @@ public class InternalElementMapping extends ElementMapping { /** The FOP extension namespace URI */ public static final String URI = "http://xmlgraphics.apache.org/fop/internal"; + /** The standard XML prefix for elements and attributes in this namespace. */ + public static final String STANDARD_PREFIX = "foi"; + + /** The "struct-id" attribute, to identify a structure tree element. */ + public static final String STRUCT_ID = "struct-id"; + + /** The "struct-ref" attribute, to refer to a structure tree element. */ + public static final String STRUCT_REF = "struct-ref"; + private static final Set<String> PROPERTY_ATTRIBUTES = new java.util.HashSet<String>(); static { //These are FOP's extension properties for accessibility - PROPERTY_ATTRIBUTES.add("ptr"); + PROPERTY_ATTRIBUTES.add(STRUCT_ID); + PROPERTY_ATTRIBUTES.add(STRUCT_REF); } /** @@ -59,7 +69,7 @@ public class InternalElementMapping extends ElementMapping { /** {@inheritDoc} */ public String getStandardPrefix() { - return "foi"; + return STANDARD_PREFIX; } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/fo/flow/AbstractGraphics.java b/src/java/org/apache/fop/fo/flow/AbstractGraphics.java index eeb06c1df..d5fe2b500 100644 --- a/src/java/org/apache/fop/fo/flow/AbstractGraphics.java +++ b/src/java/org/apache/fop/fo/flow/AbstractGraphics.java @@ -19,17 +19,20 @@ package org.apache.fop.fo.flow; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.GraphicsProperties; import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.LengthRangeProperty; import org.apache.fop.fo.properties.SpaceProperty; -import org.apache.fop.fo.properties.StructurePointerPropertySet; +import org.apache.fop.fo.properties.StructureTreeElementHolder; /** * Common base class for the <a href="http://www.w3.org/TR/xsl/#fo_instream-foreign-object"> @@ -38,10 +41,11 @@ import org.apache.fop.fo.properties.StructurePointerPropertySet; * <code>fo:external-graphic</code></a> flow formatting objects. */ public abstract class AbstractGraphics extends FObj - implements GraphicsProperties, StructurePointerPropertySet { + implements GraphicsProperties, StructureTreeElementHolder, CommonAccessibilityHolder { // The value of properties relevant for fo:instream-foreign-object // and external-graphics. + private CommonAccessibility commonAccessibility; private CommonBorderPaddingBackground commonBorderPaddingBackground; private Length alignmentAdjust; private int alignmentBaseline; @@ -62,7 +66,8 @@ public abstract class AbstractGraphics extends FObj private int scaling; private int textAlign; private Length width; - private String ptr; // used for accessibility + private String altText; + private StructureTreeElement structureTreeElement; // Unused but valid items, commented out for performance: // private CommonAccessibility commonAccessibility; // private CommonAural commonAural; @@ -85,6 +90,7 @@ public abstract class AbstractGraphics extends FObj /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { + commonAccessibility = CommonAccessibility.getInstance(pList); commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps(); alignmentAdjust = pList.get(PR_ALIGNMENT_ADJUST).getLength(); alignmentBaseline = pList.get(PR_ALIGNMENT_BASELINE).getEnum(); @@ -97,7 +103,6 @@ public abstract class AbstractGraphics extends FObj dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum(); height = pList.get(PR_HEIGHT).getLength(); id = pList.get(PR_ID).getString(); - ptr = pList.get(PR_X_PTR).getString(); // used for accessibility inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange(); keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep(); keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep(); @@ -107,13 +112,18 @@ public abstract class AbstractGraphics extends FObj textAlign = pList.get(PR_TEXT_ALIGN).getEnum(); width = pList.get(PR_WIDTH).getLength(); if (getUserAgent().isAccessibilityEnabled()) { - String altText = pList.get(PR_X_ALT_TEXT).getString(); + altText = pList.get(PR_X_ALT_TEXT).getString(); if (altText.equals("")) { getFOValidationEventProducer().altTextMissing(this, getLocalName(), getLocator()); } } } + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + /** * @return the "id" property. */ @@ -217,9 +227,19 @@ public abstract class AbstractGraphics extends FObj return keepWithPrevious; } + @Override + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; + } + /** {@inheritDoc} */ - public String getPtr() { - return ptr; + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; + } + + /** @return the alternative text property. */ + public String getAltText() { + return altText; } /** @return the graphic's intrinsic width in millipoints */ diff --git a/src/java/org/apache/fop/fo/flow/AbstractListItemPart.java b/src/java/org/apache/fop/fo/flow/AbstractListItemPart.java index b0e93e9c6..0e99bfe96 100644 --- a/src/java/org/apache/fop/fo/flow/AbstractListItemPart.java +++ b/src/java/org/apache/fop/fo/flow/AbstractListItemPart.java @@ -26,6 +26,8 @@ import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; import org.apache.fop.fo.properties.KeepProperty; /** @@ -33,8 +35,10 @@ import org.apache.fop.fo.properties.KeepProperty; * <code>fo:list-item-label</code></a> and <a href="http://www.w3.org/TR/xsl/#fo_list-item-body"> * <code>fo:list-item-body</code></a>. */ -public abstract class AbstractListItemPart extends FObj { - // The value of properties relevant for fo:list-item-label and fo:list-item-body. +public abstract class AbstractListItemPart extends FObj implements CommonAccessibilityHolder { + + private CommonAccessibility commonAccessibility; + private KeepProperty keepTogether; // Valid properties, commented out for performance: // private CommonAccessibility commonAccessibility; @@ -55,6 +59,7 @@ public abstract class AbstractListItemPart extends FObj { /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); keepTogether = pList.get(PR_KEEP_TOGETHER).getKeep(); } @@ -86,6 +91,11 @@ public abstract class AbstractListItemPart extends FObj { } } + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + /** @return the "keep-together" property. */ public KeepProperty getKeepTogether() { return keepTogether; diff --git a/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java b/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java index 7bc0ff00c..5a6893b55 100644 --- a/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java +++ b/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java @@ -24,6 +24,7 @@ import java.awt.Color; import org.xml.sax.Attributes; import org.xml.sax.Locator; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.Constants; @@ -31,11 +32,13 @@ import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonFont; import org.apache.fop.fo.properties.CommonTextDecoration; import org.apache.fop.fo.properties.SpaceProperty; -import org.apache.fop.fo.properties.StructurePointerPropertySet; +import org.apache.fop.fo.properties.StructureTreeElementHolder; /** * Common base class for the <a href="http://www.w3.org/TR/xsl/#fo_page-number-citation"> @@ -44,16 +47,17 @@ import org.apache.fop.fo.properties.StructurePointerPropertySet; * <code>fo:page-number-citation-last</code></a> objects. */ public abstract class AbstractPageNumberCitation extends FObj - implements StructurePointerPropertySet { + implements StructureTreeElementHolder, CommonAccessibilityHolder { // The value of properties relevant for fo:page-number-citation(-last). + private CommonAccessibility commonAccessibility; private CommonBorderPaddingBackground commonBorderPaddingBackground; private CommonFont commonFont; private Length alignmentAdjust; private int alignmentBaseline; private Length baselineShift; private int dominantBaseline; - private String ptr; // used for accessibility + private StructureTreeElement structureTreeElement; // private ToBeImplementedProperty letterSpacing; private SpaceProperty lineHeight; private String refId; @@ -61,7 +65,6 @@ public abstract class AbstractPageNumberCitation extends FObj private CommonTextDecoration textDecoration; // private ToBeImplementedProperty textShadow; // Unused but valid items, commented out for performance: - // private CommonAccessibility commonAccessibility; // private CommonAural commonAural; // private CommonMarginInline commonMarginInline; // private CommonRelativePosition commonRelativePosition; @@ -91,6 +94,7 @@ public abstract class AbstractPageNumberCitation extends FObj /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps(); commonFont = pList.getFontProps(); alignmentAdjust = pList.get(PR_ALIGNMENT_ADJUST).getLength(); @@ -99,7 +103,6 @@ public abstract class AbstractPageNumberCitation extends FObj dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum(); // letterSpacing = pList.get(PR_LETTER_SPACING); lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); - ptr = pList.get(PR_X_PTR).getString(); // used for accessibility refId = pList.get(PR_REF_ID).getString(); textDecoration = pList.getTextDecorationProps(); // textShadow = pList.get(PR_TEXT_SHADOW); @@ -129,6 +132,11 @@ public abstract class AbstractPageNumberCitation extends FObj } } + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + /** @return the {@link CommonFont} */ public CommonFont getCommonFont() { return commonFont; @@ -144,9 +152,14 @@ public abstract class AbstractPageNumberCitation extends FObj return textDecoration; } + @Override + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; + } + /** {@inheritDoc} */ - public String getPtr() { - return ptr; + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; } /** @return the "alignment-adjust" property */ diff --git a/src/java/org/apache/fop/fo/flow/BasicLink.java b/src/java/org/apache/fop/fo/flow/BasicLink.java index c3a735db6..0d6d5d9b4 100644 --- a/src/java/org/apache/fop/fo/flow/BasicLink.java +++ b/src/java/org/apache/fop/fo/flow/BasicLink.java @@ -21,12 +21,13 @@ package org.apache.fop.fo.flow; import org.xml.sax.Locator; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.FONode; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; -import org.apache.fop.fo.properties.StructurePointerPropertySet; +import org.apache.fop.fo.properties.StructureTreeElementHolder; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_basic-link"> @@ -36,14 +37,14 @@ import org.apache.fop.fo.properties.StructurePointerPropertySet; * and whether that link is external (uses a URI) or internal (an id * reference). */ -public class BasicLink extends InlineLevel implements StructurePointerPropertySet { +public class BasicLink extends InlineLevel implements StructureTreeElementHolder { // The value of properties relevant for fo:basic-link. private Length alignmentAdjust; private int alignmentBaseline; private Length baselineShift; private int dominantBaseline; - private String ptr; + private StructureTreeElement structureTreeElement; // private ToBeImplementedProperty destinationPlacementOffset; private String externalDestination; // private ToBeImplementedProperty indicateDestination; @@ -76,7 +77,6 @@ public class BasicLink extends InlineLevel implements StructurePointerPropertySe alignmentBaseline = pList.get(PR_ALIGNMENT_BASELINE).getEnum(); baselineShift = pList.get(PR_BASELINE_SHIFT).getLength(); dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum(); - ptr = pList.get(PR_X_PTR).getString(); // used for accessibility // destinationPlacementOffset = pList.get(PR_DESTINATION_PLACEMENT_OFFSET); externalDestination = pList.get(PR_EXTERNAL_DESTINATION).getString(); // indicateDestination = pList.get(PR_INDICATE_DESTINATION); @@ -104,7 +104,7 @@ public class BasicLink extends InlineLevel implements StructurePointerPropertySe /** {@inheritDoc} */ protected void endOfNode() throws FOPException { super.endOfNode(); - getFOEventHandler().endLink(); + getFOEventHandler().endLink(this); } /** {@inheritDoc} */ @@ -143,9 +143,14 @@ public class BasicLink extends InlineLevel implements StructurePointerPropertySe return dominantBaseline; } + @Override + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; + } + /** {@inheritDoc} */ - public String getPtr() { - return ptr; + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; } /** diff --git a/src/java/org/apache/fop/fo/flow/Block.java b/src/java/org/apache/fop/fo/flow/Block.java index 66e06db83..84f1abc1b 100644 --- a/src/java/org/apache/fop/fo/flow/Block.java +++ b/src/java/org/apache/fop/fo/flow/Block.java @@ -33,6 +33,8 @@ import org.apache.fop.fo.NullCharIterator; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; import org.apache.fop.fo.properties.BreakPropertySet; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonFont; import org.apache.fop.fo.properties.CommonHyphenation; @@ -40,19 +42,20 @@ import org.apache.fop.fo.properties.CommonMarginBlock; import org.apache.fop.fo.properties.CommonRelativePosition; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.SpaceProperty; -import org.apache.fop.fo.properties.StructurePointerPropertySet; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_block"> * <code>fo:block object</code></a>. */ -public class Block extends FObjMixed implements BreakPropertySet, StructurePointerPropertySet { +public class Block extends FObjMixed implements BreakPropertySet, + CommonAccessibilityHolder { // used for FO validation private boolean blockOrInlineItemFound = false; private boolean initialPropertySetFound = false; // The value of properties relevant for fo:block. + private CommonAccessibility commonAccessibility; private CommonBorderPaddingBackground commonBorderPaddingBackground; private CommonFont commonFont; private CommonHyphenation commonHyphenation; @@ -73,7 +76,6 @@ public class Block extends FObjMixed implements BreakPropertySet, StructurePoint private int lineHeightShiftAdjustment; private int lineStackingStrategy; private Numeric orphans; - private String ptr; //used for accessibility private int whiteSpaceTreatment; private int span; private int textAlign; @@ -104,6 +106,7 @@ public class Block extends FObjMixed implements BreakPropertySet, StructurePoint /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps(); commonFont = pList.getFontProps(); commonHyphenation = pList.getHyphenationProps(); @@ -125,7 +128,6 @@ public class Block extends FObjMixed implements BreakPropertySet, StructurePoint lineHeightShiftAdjustment = pList.get(PR_LINE_HEIGHT_SHIFT_ADJUSTMENT).getEnum(); lineStackingStrategy = pList.get(PR_LINE_STACKING_STRATEGY).getEnum(); orphans = pList.get(PR_ORPHANS).getNumeric(); - ptr = pList.get(PR_X_PTR).getString(); //used for accessibility whiteSpaceTreatment = pList.get(PR_WHITE_SPACE_TREATMENT).getEnum(); span = pList.get(PR_SPAN).getEnum(); textAlign = pList.get(PR_TEXT_ALIGN).getEnum(); @@ -149,6 +151,11 @@ public class Block extends FObjMixed implements BreakPropertySet, StructurePoint getFOEventHandler().endBlock(this); } + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + /** @return the {@link CommonMarginBlock} */ public CommonMarginBlock getCommonMarginBlock() { return commonMarginBlock; @@ -175,11 +182,6 @@ public class Block extends FObjMixed implements BreakPropertySet, StructurePoint return breakAfter; } - /** {@inheritDoc} */ - public String getPtr() { - return ptr; - } - /** @return the "break-before" property. */ public int getBreakBefore() { return breakBefore; diff --git a/src/java/org/apache/fop/fo/flow/Character.java b/src/java/org/apache/fop/fo/flow/Character.java index 7328b5644..c4de9fb72 100644 --- a/src/java/org/apache/fop/fo/flow/Character.java +++ b/src/java/org/apache/fop/fo/flow/Character.java @@ -24,6 +24,7 @@ import java.util.NoSuchElementException; import org.xml.sax.Locator; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.CharIterator; @@ -38,13 +39,13 @@ import org.apache.fop.fo.properties.CommonTextDecoration; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.Property; import org.apache.fop.fo.properties.SpaceProperty; -import org.apache.fop.fo.properties.StructurePointerPropertySet; +import org.apache.fop.fo.properties.StructureTreeElementHolder; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_character"> * <code>fo:character</code></a> object. */ -public class Character extends FObj implements StructurePointerPropertySet { +public class Character extends FObj implements StructureTreeElementHolder { // The value of properties relevant for fo:character. private CommonBorderPaddingBackground commonBorderPaddingBackground; private CommonFont commonFont; @@ -63,7 +64,7 @@ public class Character extends FObj implements StructurePointerPropertySet { private CommonTextDecoration textDecoration; // private ToBeImplementedProperty textShadow; private Property wordSpacing; - private String ptr; // used for accessibility + private StructureTreeElement structureTreeElement; // Unused but valid items, commented out for performance: // private CommonAural commonAural; // private CommonMarginInline commonMarginInline; @@ -110,7 +111,6 @@ public class Character extends FObj implements StructurePointerPropertySet { lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); textDecoration = pList.getTextDecorationProps(); wordSpacing = pList.get(PR_WORD_SPACING); - ptr = pList.get(PR_X_PTR).getString(); // used for accessibility } /** {@inheritDoc} */ @@ -210,9 +210,14 @@ public class Character extends FObj implements StructurePointerPropertySet { return keepWithPrevious; } + @Override + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; + } + /** {@inheritDoc} */ - public String getPtr() { - return ptr; + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/fo/flow/Footnote.java b/src/java/org/apache/fop/fo/flow/Footnote.java index 51d2581cd..5953363ed 100644 --- a/src/java/org/apache/fop/fo/flow/Footnote.java +++ b/src/java/org/apache/fop/fo/flow/Footnote.java @@ -26,15 +26,16 @@ import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_footnote"> * <code>fo:footnote</code></a> object. */ -public class Footnote extends FObj { - // The value of properties relevant for fo:footnote (commented out for performance). - // private CommonAccessibility commonAccessibility; - // End of property values +public class Footnote extends FObj implements CommonAccessibilityHolder { + + private CommonAccessibility commonAccessibility; private Inline footnoteCitation = null; private FootnoteBody footnoteBody; @@ -51,7 +52,7 @@ public class Footnote extends FObj { /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { - // No active properties -> do nothing. + commonAccessibility = CommonAccessibility.getInstance(pList); } /** {@inheritDoc} */ @@ -110,6 +111,11 @@ public class Footnote extends FObj { } } + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + /** * Public accessor for inline FO * diff --git a/src/java/org/apache/fop/fo/flow/FootnoteBody.java b/src/java/org/apache/fop/fo/flow/FootnoteBody.java index eb26a2009..1c103b6b5 100644 --- a/src/java/org/apache/fop/fo/flow/FootnoteBody.java +++ b/src/java/org/apache/fop/fo/flow/FootnoteBody.java @@ -27,15 +27,17 @@ import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_footnote-body"> * <code>fo:footnote-body</code></a> object. */ -public class FootnoteBody extends FObj { - // The value of properties relevant for fo:footnote-body (commented out for perforance). - // private CommonAccessibility commonAccessibility; - // End of property values +public class FootnoteBody extends FObj implements CommonAccessibilityHolder { + + /** {@inheritDoc} */ + private CommonAccessibility commonAccessibility; /** * Base constructor @@ -48,6 +50,7 @@ public class FootnoteBody extends FObj { /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { + commonAccessibility = CommonAccessibility.getInstance(pList); } /** {@inheritDoc} */ @@ -93,4 +96,10 @@ public class FootnoteBody extends FObj { public int getNameId() { return FO_FOOTNOTE_BODY; } + + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + } diff --git a/src/java/org/apache/fop/fo/flow/InitialPropertySet.java b/src/java/org/apache/fop/fo/flow/InitialPropertySet.java index 0df7379f2..b41021b75 100644 --- a/src/java/org/apache/fop/fo/flow/InitialPropertySet.java +++ b/src/java/org/apache/fop/fo/flow/InitialPropertySet.java @@ -27,19 +27,22 @@ import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; import org.apache.fop.fo.properties.SpaceProperty; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_initial-property-set"> * <code>fo:initial-property-set</code></a> object. */ -public class InitialPropertySet extends FObj { - // The value of properties relevant for fo:initial-property-set. +public class InitialPropertySet extends FObj implements CommonAccessibilityHolder { + + private CommonAccessibility commonAccessibility; + // private ToBeImplementedProperty letterSpacing; private SpaceProperty lineHeight; // private ToBeImplementedProperty textShadow; // Unused but valid items, commented out for performance: - // private CommonAccessibility commonAccessibility; // private CommonAural commonAural; // private CommonBorderPaddingBackground commonBorderPaddingBackground; // private CommonFont commonFont; @@ -63,6 +66,7 @@ public class InitialPropertySet extends FObj { /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); // letterSpacing = pList.get(PR_LETTER_SPACING); lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); // textShadow = pList.get(PR_TEXT_SHADOW); @@ -96,4 +100,10 @@ public class InitialPropertySet extends FObj { public int getNameId() { return FO_INITIAL_PROPERTY_SET; } + + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + } diff --git a/src/java/org/apache/fop/fo/flow/Inline.java b/src/java/org/apache/fop/fo/flow/Inline.java index d9ae63ec8..debf6bbf6 100644 --- a/src/java/org/apache/fop/fo/flow/Inline.java +++ b/src/java/org/apache/fop/fo/flow/Inline.java @@ -26,19 +26,17 @@ import org.apache.fop.datatypes.Length; import org.apache.fop.fo.FONode; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; -import org.apache.fop.fo.properties.StructurePointerPropertySet; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_inline"> * <code>fo:inline</code></a> formatting object. */ -public class Inline extends InlineLevel implements StructurePointerPropertySet { +public class Inline extends InlineLevel { // The value of properties relevant for fo:inline. // See also superclass InlineLevel private Length alignmentAdjust; private int alignmentBaseline; private Length baselineShift; - private String ptr; // used for accessibility private int dominantBaseline; // Unused but valid items, commented out for performance: // private CommonRelativePosition commonRelativePosition; @@ -68,7 +66,6 @@ public class Inline extends InlineLevel implements StructurePointerPropertySet { alignmentBaseline = pList.get(PR_ALIGNMENT_BASELINE).getEnum(); baselineShift = pList.get(PR_BASELINE_SHIFT).getLength(); dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum(); - ptr = pList.get(PR_X_PTR).getString(); // used for accessibility } /** {@inheritDoc} */ @@ -149,11 +146,6 @@ public class Inline extends InlineLevel implements StructurePointerPropertySet { } /** {@inheritDoc} */ - public String getPtr() { - return ptr; - } - - /** {@inheritDoc} */ public String getLocalName() { return "inline"; } diff --git a/src/java/org/apache/fop/fo/flow/InlineLevel.java b/src/java/org/apache/fop/fo/flow/InlineLevel.java index 5410e1ca7..0da769080 100644 --- a/src/java/org/apache/fop/fo/flow/InlineLevel.java +++ b/src/java/org/apache/fop/fo/flow/InlineLevel.java @@ -25,6 +25,8 @@ import org.apache.fop.apps.FOPException; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObjMixed; import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonFont; import org.apache.fop.fo.properties.CommonMarginInline; @@ -35,9 +37,10 @@ import org.apache.fop.fo.properties.SpaceProperty; * Class modelling the commonalities of several inline-level * formatting objects. */ -public abstract class InlineLevel extends FObjMixed { +public abstract class InlineLevel extends FObjMixed implements CommonAccessibilityHolder { // The value of properties relevant for inline-level FOs. + private CommonAccessibility commonAccessibility; private CommonBorderPaddingBackground commonBorderPaddingBackground; private CommonMarginInline commonMarginInline; private CommonFont commonFont; @@ -59,6 +62,7 @@ public abstract class InlineLevel extends FObjMixed { /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps(); commonMarginInline = pList.getMarginInlineProps(); commonFont = pList.getFontProps(); @@ -68,6 +72,11 @@ public abstract class InlineLevel extends FObjMixed { lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); } + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + /** @return the {@link CommonMarginInline} */ public CommonMarginInline getCommonMarginInline() { return commonMarginInline; diff --git a/src/java/org/apache/fop/fo/flow/InstreamForeignObject.java b/src/java/org/apache/fop/fo/flow/InstreamForeignObject.java index 02d25f8dd..33164188f 100644 --- a/src/java/org/apache/fop/fo/flow/InstreamForeignObject.java +++ b/src/java/org/apache/fop/fo/flow/InstreamForeignObject.java @@ -59,6 +59,12 @@ public class InstreamForeignObject extends AbstractGraphics { super(parent); } + @Override + protected void startOfNode() throws FOPException { + super.startOfNode(); + getFOEventHandler().startInstreamForeignObject(this); + } + /** * Make sure content model satisfied, if so then tell the * {@link org.apache.fop.fo.FOEventHandler} that we are at @@ -69,7 +75,7 @@ public class InstreamForeignObject extends AbstractGraphics { if (firstChild == null) { missingChildElementError("one (1) non-XSL namespace child"); } - getFOEventHandler().foreignObject(this); + getFOEventHandler().endInstreamForeignObject(this); } /** diff --git a/src/java/org/apache/fop/fo/flow/Leader.java b/src/java/org/apache/fop/fo/flow/Leader.java index 54027514c..f12930dde 100644 --- a/src/java/org/apache/fop/fo/flow/Leader.java +++ b/src/java/org/apache/fop/fo/flow/Leader.java @@ -158,4 +158,16 @@ public class Leader extends InlineLevel { public int getNameId() { return FO_LEADER; } + + @Override + protected void startOfNode() throws FOPException { + super.startOfNode(); + getFOEventHandler().startLeader(this); + } + + @Override + protected void endOfNode() throws FOPException { + super.endOfNode(); + getFOEventHandler().endLeader(this); + } } diff --git a/src/java/org/apache/fop/fo/flow/ListBlock.java b/src/java/org/apache/fop/fo/flow/ListBlock.java index 8f72ded67..35288ec80 100644 --- a/src/java/org/apache/fop/fo/flow/ListBlock.java +++ b/src/java/org/apache/fop/fo/flow/ListBlock.java @@ -28,6 +28,8 @@ import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; import org.apache.fop.fo.properties.BreakPropertySet; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonMarginBlock; import org.apache.fop.fo.properties.KeepProperty; @@ -36,8 +38,9 @@ import org.apache.fop.fo.properties.KeepProperty; * Class modelling the <a href=http://www.w3.org/TR/xsl/#fo_list-block"> * <code>fo:list-block</code></a> object. */ -public class ListBlock extends FObj implements BreakPropertySet { +public class ListBlock extends FObj implements BreakPropertySet, CommonAccessibilityHolder { // The value of properties relevant for fo:list-block. + private CommonAccessibility commonAccessibility; private CommonBorderPaddingBackground commonBorderPaddingBackground; private CommonMarginBlock commonMarginBlock; private int breakAfter; @@ -46,7 +49,6 @@ public class ListBlock extends FObj implements BreakPropertySet { private KeepProperty keepWithNext; private KeepProperty keepWithPrevious; // Unused but valid items, commented out for performance: - // private CommonAccessibility commonAccessibility; // private CommonAural commonAural; // private CommonRelativePosition commonRelativePosition; // private int intrusionDisplace; @@ -73,6 +75,7 @@ public class ListBlock extends FObj implements BreakPropertySet { /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps(); commonMarginBlock = pList.getMarginBlockProps(); breakAfter = pList.get(PR_BREAK_AFTER).getEnum(); @@ -123,6 +126,11 @@ public class ListBlock extends FObj implements BreakPropertySet { } } + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + /** @return the {@link CommonMarginBlock} */ public CommonMarginBlock getCommonMarginBlock() { return commonMarginBlock; diff --git a/src/java/org/apache/fop/fo/flow/ListItem.java b/src/java/org/apache/fop/fo/flow/ListItem.java index c4a2aa64b..aa177777c 100644 --- a/src/java/org/apache/fop/fo/flow/ListItem.java +++ b/src/java/org/apache/fop/fo/flow/ListItem.java @@ -27,6 +27,8 @@ import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; import org.apache.fop.fo.properties.BreakPropertySet; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonMarginBlock; import org.apache.fop.fo.properties.KeepProperty; @@ -35,8 +37,9 @@ import org.apache.fop.fo.properties.KeepProperty; * Class modelling the <a href=http://www.w3.org/TR/xsl/#fo_list-item"> * <code>fo:list-item</code></a> object. */ -public class ListItem extends FObj implements BreakPropertySet { +public class ListItem extends FObj implements BreakPropertySet, CommonAccessibilityHolder { // The value of properties relevant for fo:list-item. + private CommonAccessibility commonAccessibility; private CommonBorderPaddingBackground commonBorderPaddingBackground; private CommonMarginBlock commonMarginBlock; private int breakAfter; @@ -45,7 +48,6 @@ public class ListItem extends FObj implements BreakPropertySet { private KeepProperty keepWithNext; private KeepProperty keepWithPrevious; // Unused but valid items, commented out for performance: - // private CommonAccessibility commonAccessibility; // private CommonAural commonAural; // private CommonRelativePosition commonRelativePosition; // private int intrusionDisplace; @@ -67,6 +69,7 @@ public class ListItem extends FObj implements BreakPropertySet { /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps(); commonMarginBlock = pList.getMarginBlockProps(); breakAfter = pList.get(PR_BREAK_AFTER).getEnum(); @@ -134,6 +137,11 @@ public class ListItem extends FObj implements BreakPropertySet { } } + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + /** @return the {@link CommonMarginBlock} */ public CommonMarginBlock getCommonMarginBlock() { return commonMarginBlock; diff --git a/src/java/org/apache/fop/fo/flow/ListItemBody.java b/src/java/org/apache/fop/fo/flow/ListItemBody.java index 853beb20b..3007f9ba3 100644 --- a/src/java/org/apache/fop/fo/flow/ListItemBody.java +++ b/src/java/org/apache/fop/fo/flow/ListItemBody.java @@ -38,13 +38,13 @@ public class ListItemBody extends AbstractListItemPart { /** {@inheritDoc} */ protected void startOfNode() throws FOPException { super.startOfNode(); - getFOEventHandler().startListBody(); + getFOEventHandler().startListBody(this); } /** {@inheritDoc} */ protected void endOfNode() throws FOPException { super.endOfNode(); - getFOEventHandler().endListBody(); + getFOEventHandler().endListBody(this); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/fo/flow/ListItemLabel.java b/src/java/org/apache/fop/fo/flow/ListItemLabel.java index 1c757686c..10436afa6 100644 --- a/src/java/org/apache/fop/fo/flow/ListItemLabel.java +++ b/src/java/org/apache/fop/fo/flow/ListItemLabel.java @@ -40,13 +40,13 @@ public class ListItemLabel extends AbstractListItemPart { /** {@inheritDoc} */ protected void startOfNode() throws FOPException { super.startOfNode(); - getFOEventHandler().startListLabel(); + getFOEventHandler().startListLabel(this); } /** {@inheritDoc} */ protected void endOfNode() throws FOPException { super.endOfNode(); - getFOEventHandler().endListLabel(); + getFOEventHandler().endListLabel(this); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/fo/flow/PageNumber.java b/src/java/org/apache/fop/fo/flow/PageNumber.java index 34d267a64..59169c2d3 100644 --- a/src/java/org/apache/fop/fo/flow/PageNumber.java +++ b/src/java/org/apache/fop/fo/flow/PageNumber.java @@ -23,6 +23,7 @@ import java.awt.Color; import org.xml.sax.Locator; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.Constants; @@ -30,32 +31,35 @@ import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonFont; import org.apache.fop.fo.properties.CommonTextDecoration; import org.apache.fop.fo.properties.SpaceProperty; -import org.apache.fop.fo.properties.StructurePointerPropertySet; +import org.apache.fop.fo.properties.StructureTreeElementHolder; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_page-number"> * <code>fo:page-number</code></a> object. */ -public class PageNumber extends FObj implements StructurePointerPropertySet { +public class PageNumber extends FObj + implements StructureTreeElementHolder, CommonAccessibilityHolder { // The value of properties relevant for fo:page-number. + private CommonAccessibility commonAccessibility; private CommonBorderPaddingBackground commonBorderPaddingBackground; private CommonFont commonFont; private Length alignmentAdjust; private int alignmentBaseline; private Length baselineShift; private int dominantBaseline; - private String ptr; // used for accessibility + private StructureTreeElement structureTreeElement; // private ToBeImplementedProperty letterSpacing; private SpaceProperty lineHeight; /** Holds the text decoration values. May be null */ private CommonTextDecoration textDecoration; // private ToBeImplementedProperty textShadow; // Unused but valid items, commented out for performance: - // private CommonAccessibility commonAccessibility; // private CommonAural commonAural; // private CommonMarginInline commonMarginInline; // private CommonRelativePosition commonRelativePosition; @@ -85,6 +89,7 @@ public class PageNumber extends FObj implements StructurePointerPropertySet { /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps(); commonFont = pList.getFontProps(); alignmentAdjust = pList.get(PR_ALIGNMENT_ADJUST).getLength(); @@ -94,7 +99,6 @@ public class PageNumber extends FObj implements StructurePointerPropertySet { // letterSpacing = pList.get(PR_LETTER_SPACING); lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); textDecoration = pList.getTextDecorationProps(); - ptr = pList.get(PR_X_PTR).getString(); // used for accessibility // textShadow = pList.get(PR_TEXT_SHADOW); // implicit properties @@ -123,6 +127,11 @@ public class PageNumber extends FObj implements StructurePointerPropertySet { } } + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + /** @return the Common Font Properties. */ public CommonFont getCommonFont() { return commonFont; @@ -168,9 +177,14 @@ public class PageNumber extends FObj implements StructurePointerPropertySet { return lineHeight; } + @Override + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; + } + /** {@inheritDoc} */ - public String getPtr() { - return ptr; + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/fo/flow/Wrapper.java b/src/java/org/apache/fop/fo/flow/Wrapper.java index 7fb412521..6c5619252 100644 --- a/src/java/org/apache/fop/fo/flow/Wrapper.java +++ b/src/java/org/apache/fop/fo/flow/Wrapper.java @@ -51,6 +51,18 @@ public class Wrapper extends FObjMixed { super(parent); } + @Override + protected void startOfNode() throws FOPException { + super.startOfNode(); + getFOEventHandler().startWrapper(this); + } + + @Override + protected void endOfNode() throws FOPException { + super.endOfNode(); + getFOEventHandler().endWrapper(this); + } + /** * {@inheritDoc} * <br>XSL Content Model: marker* (#PCDATA|%inline;|%block;)* diff --git a/src/java/org/apache/fop/fo/flow/table/Table.java b/src/java/org/apache/fop/fo/flow/table/Table.java index 86196cb29..c78f9f959 100644 --- a/src/java/org/apache/fop/fo/flow/table/Table.java +++ b/src/java/org/apache/fop/fo/flow/table/Table.java @@ -32,6 +32,8 @@ import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.StaticPropertyList; import org.apache.fop.fo.ValidationException; import org.apache.fop.fo.properties.BreakPropertySet; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonMarginBlock; import org.apache.fop.fo.properties.KeepProperty; @@ -43,9 +45,11 @@ import org.apache.fop.fo.properties.TableColLength; * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_table"> * <code>fo:table</code></a> object. */ -public class Table extends TableFObj implements ColumnNumberManagerHolder, BreakPropertySet { +public class Table extends TableFObj implements ColumnNumberManagerHolder, BreakPropertySet, + CommonAccessibilityHolder { /** properties */ + private CommonAccessibility commonAccessibility; private CommonBorderPaddingBackground commonBorderPaddingBackground; private CommonMarginBlock commonMarginBlock; private LengthRangeProperty blockProgressionDimension; @@ -61,7 +65,6 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder, Break private int tableOmitFooterAtBreak; private int tableOmitHeaderAtBreak; // Unused but valid items, commented out for performance: - // private CommonAccessibility commonAccessibility; // private CommonAural commonAural; // private CommonRelativePosition commonRelativePosition; // private int intrusionDisplace; @@ -112,6 +115,7 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder, Break */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps(); commonMarginBlock = pList.getMarginBlockProps(); blockProgressionDimension = pList.get(PR_BLOCK_PROGRESSION_DIMENSION).getLengthRange(); @@ -302,6 +306,11 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder, Break } /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + + /** {@inheritDoc} */ public Table getTable() { return this; } diff --git a/src/java/org/apache/fop/fo/flow/table/TableAndCaption.java b/src/java/org/apache/fop/fo/flow/table/TableAndCaption.java index 634460ec4..51db27e4e 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableAndCaption.java +++ b/src/java/org/apache/fop/fo/flow/table/TableAndCaption.java @@ -25,17 +25,22 @@ import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; +import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_table-and-caption"> * <code>fo:table-and-caption</code></a> property. * TODO needs implementation */ -public class TableAndCaption extends FObj /*implements BreakPropertySet*/ { +public class TableAndCaption extends FObj implements CommonAccessibilityHolder { + + private CommonAccessibility commonAccessibility; + // The value of properties relevant for fo:table-and-caption. // Unused but valid items, commented out for performance: - // private CommonAccessibility commonAccessibility; // private CommonAural commonAural; // private CommonBorderPaddingBackground commonBorderPaddingBackground; // private CommonMarginBlock commonMarginBlock; @@ -50,7 +55,7 @@ public class TableAndCaption extends FObj /*implements BreakPropertySet*/ { // private int textAlign; // End of property values - private static boolean notImplementedWarningGiven = false; + static boolean notImplementedWarningGiven = false; // CSOK: VisibilityModifier /** used for FO validation */ private boolean tableCaptionFound = false; @@ -71,6 +76,12 @@ public class TableAndCaption extends FObj /*implements BreakPropertySet*/ { } } + @Override + public void bind(PropertyList pList) throws FOPException { + super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); + } + /** * Make sure content model satisfied, if so then tell the * FOEventHandler that we are at the end of the flow. @@ -128,5 +139,11 @@ public class TableAndCaption extends FObj /*implements BreakPropertySet*/ { public int getNameId() { return FO_TABLE_AND_CAPTION; } + + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + } diff --git a/src/java/org/apache/fop/fo/flow/table/TableCaption.java b/src/java/org/apache/fop/fo/flow/table/TableCaption.java index fc5d3d5b7..380b27f34 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableCaption.java +++ b/src/java/org/apache/fop/fo/flow/table/TableCaption.java @@ -27,13 +27,18 @@ import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_table-caption"> * <code>fo:table-caption</code></a> object. */ -public class TableCaption extends FObj { +public class TableCaption extends FObj implements CommonAccessibilityHolder { + + private CommonAccessibility commonAccessibility; + // The value of properties relevant for fo:table-caption. // Unused but valid items, commented out for performance: // private CommonAural commonAural; @@ -49,7 +54,7 @@ public class TableCaption extends FObj { /** used for FO validation */ private boolean blockItemFound = false; - private static boolean notImplementedWarningGiven = false; + static boolean notImplementedWarningGiven = false; // CSOK: VisibilityModifier /** * Create a TableCaption instance with the given {@link FONode} @@ -69,6 +74,7 @@ public class TableCaption extends FObj { /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); } /** {@inheritDoc} */ @@ -109,5 +115,11 @@ public class TableCaption extends FObj { public int getNameId() { return FO_TABLE_CAPTION; } + + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + } diff --git a/src/java/org/apache/fop/fo/flow/table/TableCell.java b/src/java/org/apache/fop/fo/flow/table/TableCell.java index 637ee2a9a..c4f9c2aa6 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableCell.java +++ b/src/java/org/apache/fop/fo/flow/table/TableCell.java @@ -26,6 +26,8 @@ import org.apache.fop.datatypes.Length; import org.apache.fop.fo.FONode; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.LengthRangeProperty; @@ -33,8 +35,9 @@ import org.apache.fop.fo.properties.LengthRangeProperty; * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_table-cell"> * <code>fo:table-cell</code></a> object. */ -public class TableCell extends TableFObj { +public class TableCell extends TableFObj implements CommonAccessibilityHolder { // The value of properties relevant for fo:table-cell. + private CommonAccessibility commonAccessibility; private CommonBorderPaddingBackground commonBorderPaddingBackground; private LengthRangeProperty blockProgressionDimension; private int columnNumber; @@ -46,7 +49,6 @@ public class TableCell extends TableFObj { private int startsRow; private Length width; // Unused but valid items, commented out for performance: - // private CommonAccessibility commonAccessibility; // private CommonAural commonAural; // private CommonRelativePosition commonRelativePosition; // private int relativeAlign; @@ -74,6 +76,7 @@ public class TableCell extends TableFObj { */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps(); blockProgressionDimension = pList.get(PR_BLOCK_PROGRESSION_DIMENSION).getLengthRange(); displayAlign = pList.get(PR_DISPLAY_ALIGN).getEnum(); @@ -145,6 +148,11 @@ public class TableCell extends TableFObj { return true; } + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + /** * Get the {@link CommonBorderPaddingBackground} instance * attached to this TableCell. diff --git a/src/java/org/apache/fop/fo/flow/table/TableCellContainer.java b/src/java/org/apache/fop/fo/flow/table/TableCellContainer.java index 41eab578f..89ecd159a 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableCellContainer.java +++ b/src/java/org/apache/fop/fo/flow/table/TableCellContainer.java @@ -24,11 +24,17 @@ import java.util.List; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; /** * A common class for fo:table-body and fo:table-row which both can contain fo:table-cell. */ -public abstract class TableCellContainer extends TableFObj implements ColumnNumberManagerHolder { +public abstract class TableCellContainer extends TableFObj + implements ColumnNumberManagerHolder, CommonAccessibilityHolder { + + private CommonAccessibility commonAccessibility; /** list of pending spans */ protected List pendingSpans; @@ -44,6 +50,12 @@ public abstract class TableCellContainer extends TableFObj implements ColumnNumb super(parent); } + @Override + public void bind(PropertyList pList) throws FOPException { + super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); + } + /** * Add cell to current row. * @param cell a table cell to add @@ -116,4 +128,9 @@ public abstract class TableCellContainer extends TableFObj implements ColumnNumb return columnNumberManager; } + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + } diff --git a/src/java/org/apache/fop/fo/flow/table/TableFObj.java b/src/java/org/apache/fop/fo/flow/table/TableFObj.java index fce82dcff..2506c26b4 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableFObj.java +++ b/src/java/org/apache/fop/fo/flow/table/TableFObj.java @@ -36,19 +36,17 @@ import org.apache.fop.fo.properties.EnumProperty; import org.apache.fop.fo.properties.NumberProperty; import org.apache.fop.fo.properties.Property; import org.apache.fop.fo.properties.PropertyMaker; -import org.apache.fop.fo.properties.StructurePointerPropertySet; import org.apache.fop.layoutmgr.table.CollapsingBorderModel; /** * Common base class for table-related FOs */ -public abstract class TableFObj extends FObj implements StructurePointerPropertySet { +public abstract class TableFObj extends FObj { private Numeric borderAfterPrecedence; private Numeric borderBeforePrecedence; private Numeric borderEndPrecedence; private Numeric borderStartPrecedence; - private String ptr; ConditionalBorder borderBefore; // CSOK: VisibilityModifier ConditionalBorder borderAfter; // CSOK: VisibilityModifier @@ -74,7 +72,6 @@ public abstract class TableFObj extends FObj implements StructurePointerProperty borderBeforePrecedence = pList.get(PR_BORDER_BEFORE_PRECEDENCE).getNumeric(); borderEndPrecedence = pList.get(PR_BORDER_END_PRECEDENCE).getNumeric(); borderStartPrecedence = pList.get(PR_BORDER_START_PRECEDENCE).getNumeric(); - ptr = pList.get(PR_X_PTR).getString(); if (getNameId() != FO_TABLE //Separate check for fo:table in Table.java && getNameId() != FO_TABLE_CELL && getCommonBorderPaddingBackground().hasPadding( @@ -241,11 +238,6 @@ public abstract class TableFObj extends FObj implements StructurePointerProperty } } - /** {@inheritDoc} */ - public String getPtr() { - return ptr; - } - /** * Prepares the borders of this element if the collapsing-border model is in use. * Conflict resolution with parent elements is done where applicable. diff --git a/src/java/org/apache/fop/fo/pagination/Root.java b/src/java/org/apache/fop/fo/pagination/Root.java index fb69dc2f1..1a2ce718b 100644 --- a/src/java/org/apache/fop/fo/pagination/Root.java +++ b/src/java/org/apache/fop/fo/pagination/Root.java @@ -21,6 +21,7 @@ package org.apache.fop.fo.pagination; // java import java.util.List; +import java.util.Locale; import org.xml.sax.Locator; @@ -33,22 +34,26 @@ import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; import org.apache.fop.fo.extensions.destination.Destination; import org.apache.fop.fo.pagination.bookmarks.BookmarkTree; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; /** * Class modeling the <a href="http://www.w3.org/TR/xsl/#fo_root"> * <code>fo:root</code></a> formatting object. * Contains page masters, page-sequences. */ -public class Root extends FObj { - // The value of properties relevant for fo:root. +public class Root extends FObj implements CommonAccessibilityHolder { + + private CommonAccessibility commonAccessibility; + private int mediaUsage; - // End of property values private LayoutMasterSet layoutMasterSet; private Declarations declarations; private BookmarkTree bookmarkTree = null; private List<Destination> destinationList; private List<PageSequence> pageSequences; + private Locale locale; // temporary until above list populated private boolean pageSequenceFound = false; @@ -83,7 +88,26 @@ public class Root extends FObj { /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); mediaUsage = pList.get(PR_MEDIA_USAGE).getEnum(); + String language = pList.get(PR_LANGUAGE).getString(); + String country = pList.get(PR_COUNTRY).getString(); + if (isLocalePropertySet(language)) { + if (isLocalePropertySet(country)) { + locale = new Locale(language, country); + } else { + locale = new Locale(language); + } + } + } + + private boolean isLocalePropertySet(String property) { + return property != null && !property.equals("none"); + } + + /** {@inheritDoc} */ + protected void startOfNode() throws FOPException { + foEventHandler.startRoot(this); } /** {@inheritDoc} */ @@ -92,6 +116,7 @@ public class Root extends FObj { missingChildElementError("(layout-master-set, declarations?, " + "bookmark-tree?, (page-sequence|fox:external-document)+)"); } + foEventHandler.endRoot(this); } /** @@ -156,6 +181,11 @@ public class Root extends FObj { } } + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + /** * Sets the FOEventHandler object that this Root is attached to * @param foEventHandler the FOEventHandler object @@ -335,4 +365,10 @@ public class Root extends FObj { return FO_ROOT; } + + /** @return locale proprty. */ + public Locale getLocale() { + return locale; + } + } diff --git a/src/java/org/apache/fop/fo/pagination/StaticContent.java b/src/java/org/apache/fop/fo/pagination/StaticContent.java index 4084a250a..e70c80c5d 100644 --- a/src/java/org/apache/fop/fo/pagination/StaticContent.java +++ b/src/java/org/apache/fop/fo/pagination/StaticContent.java @@ -44,7 +44,7 @@ public class StaticContent extends Flow { if (getFlowName() == null || getFlowName().equals("")) { missingPropertyError("flow-name"); } - getFOEventHandler().startFlow(this); + getFOEventHandler().startStatic(this); } /** @@ -56,7 +56,7 @@ public class StaticContent extends Flow { if (firstChild == null && getUserAgent().validateStrictly()) { missingChildElementError("(%block;)+"); } - getFOEventHandler().endFlow(this); + getFOEventHandler().endStatic(this); } /** diff --git a/src/java/org/apache/fop/fo/pagination/bookmarks/Bookmark.java b/src/java/org/apache/fop/fo/pagination/bookmarks/Bookmark.java index 5969729d4..9e7d80cea 100644 --- a/src/java/org/apache/fop/fo/pagination/bookmarks/Bookmark.java +++ b/src/java/org/apache/fop/fo/pagination/bookmarks/Bookmark.java @@ -22,11 +22,14 @@ package org.apache.fop.fo.pagination.bookmarks; import java.util.ArrayList; import org.xml.sax.Locator; + import org.apache.fop.apps.FOPException; -import org.apache.fop.fo.FObj; import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; /** @@ -34,11 +37,12 @@ import org.apache.fop.fo.ValidationException; * <code>fo:bookmark</code></a> object, first introduced in the * XSL 1.1 WD. */ -public class Bookmark extends FObj { +public class Bookmark extends FObj implements CommonAccessibilityHolder { private BookmarkTitle bookmarkTitle; private ArrayList childBookmarks = new ArrayList(); // The value of properties relevant for this FO + private CommonAccessibility commonAccessibility; private String internalDestination; private String externalDestination; private boolean bShow = true; // from starting-state property @@ -59,6 +63,7 @@ public class Bookmark extends FObj { /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { + commonAccessibility = CommonAccessibility.getInstance(pList); externalDestination = pList.get(PR_EXTERNAL_DESTINATION).getString(); internalDestination = pList.get(PR_INTERNAL_DESTINATION).getString(); bShow = (pList.get(PR_STARTING_STATE).getEnum() == EN_SHOW); @@ -112,6 +117,11 @@ public class Bookmark extends FObj { } } + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + /** * Get the bookmark title for this bookmark * diff --git a/src/java/org/apache/fop/fo/pagination/bookmarks/BookmarkTitle.java b/src/java/org/apache/fop/fo/pagination/bookmarks/BookmarkTitle.java index 028cdc7df..1db7d84ca 100644 --- a/src/java/org/apache/fop/fo/pagination/bookmarks/BookmarkTitle.java +++ b/src/java/org/apache/fop/fo/pagination/bookmarks/BookmarkTitle.java @@ -21,17 +21,23 @@ package org.apache.fop.fo.pagination.bookmarks; import org.xml.sax.Locator; +import org.apache.fop.apps.FOPException; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.properties.CommonAccessibility; +import org.apache.fop.fo.properties.CommonAccessibilityHolder; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_bookmark-title"> * <code>fo:bookmark-title</code></a> object, first introduced in the * XSL 1.1 WD. */ -public class BookmarkTitle extends FObj { +public class BookmarkTitle extends FObj implements CommonAccessibilityHolder { + + private CommonAccessibility commonAccessibility; + private String title = ""; /** @@ -44,6 +50,12 @@ public class BookmarkTitle extends FObj { super(parent); } + @Override + public void bind(PropertyList pList) throws FOPException { + super.bind(pList); + commonAccessibility = CommonAccessibility.getInstance(pList); + } + /** * Add the characters to this BookmarkTitle. * The text data inside the BookmarkTitle xml element @@ -72,6 +84,11 @@ public class BookmarkTitle extends FObj { } } + /** {@inheritDoc} */ + public CommonAccessibility getCommonAccessibility() { + return commonAccessibility; + } + /** * Get the title for this BookmarkTitle. * diff --git a/src/java/org/apache/fop/fo/properties/CommonAccessibility.java b/src/java/org/apache/fop/fo/properties/CommonAccessibility.java index dc17228d2..85183b01f 100644 --- a/src/java/org/apache/fop/fo/properties/CommonAccessibility.java +++ b/src/java/org/apache/fop/fo/properties/CommonAccessibility.java @@ -24,37 +24,62 @@ import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.expr.PropertyException; /** - * Store all common accessibility properties. - * See Sec 7.4 of the XSL-FO Standard. - * Public "structure" allows direct member access. + * The "role" and "source-document" properties, see Section 7.5 of the XSL-FO 1.1 + * Recommendation. */ -public class CommonAccessibility { - /** - * The "source-doc" property. - */ - public String sourceDoc = null; // CSOK: VisibilityModifier +public final class CommonAccessibility { - /** - * The "role" property. - */ - public String role = null; // CSOK: VisibilityModifier + private static final CommonAccessibility DEFAULT_INSTANCE = new CommonAccessibility(null, null); + + private final String sourceDocument; + + private final String role; + + private CommonAccessibility(String sourceDocument, String role) { + this.sourceDocument = sourceDocument; + this.role = role; + } /** - * Create a <code>CommonAccessibility</code> object. - * @param pList The PropertyList with propery values. - * @throws PropertyException if a property exception is raised + * Returns an instance that matches the values (if any) in the given property list. + * + * @param propertyList a list from which to retrieve the accessibility properties + * @return the corresponding instance + * @throws PropertyException if a problem occurs while retrieving the properties */ - public CommonAccessibility(PropertyList pList) throws PropertyException { - sourceDoc = pList.get(Constants.PR_SOURCE_DOCUMENT).getString(); - if ("none".equals(sourceDoc)) { - sourceDoc = null; + public static CommonAccessibility getInstance(PropertyList propertyList) + throws PropertyException { + String sourceDocument = propertyList.get(Constants.PR_SOURCE_DOCUMENT).getString(); + if ("none".equals(sourceDocument)) { + sourceDocument = null; } - role = pList.get(Constants.PR_ROLE).getString(); + String role = propertyList.get(Constants.PR_ROLE).getString(); if ("none".equals(role)) { role = null; } + if (sourceDocument == null && role == null) { + return DEFAULT_INSTANCE; + } else { + return new CommonAccessibility(sourceDocument, role); + } + } + /** + * Returns the value of the source-document property. + * + * @return the source document, or null if the property was set to "none" + */ + public String getSourceDocument() { + return sourceDocument; } + /** + * Returns the value of the role property. + * + * @return the role, or null if the property was set to "none" + */ + public String getRole() { + return role; + } } diff --git a/src/java/org/apache/fop/fo/properties/StructurePointerPropertySet.java b/src/java/org/apache/fop/fo/properties/CommonAccessibilityHolder.java index 5cce2822e..745c0cf0e 100644 --- a/src/java/org/apache/fop/fo/properties/StructurePointerPropertySet.java +++ b/src/java/org/apache/fop/fo/properties/CommonAccessibilityHolder.java @@ -20,15 +20,16 @@ package org.apache.fop.fo.properties; /** - * Defines property access methods for internal structure pointer extension properties. + * Classes that implement this interface support the common accessibility properties. + * + * @see CommonAccessibility */ -public interface StructurePointerPropertySet { +public interface CommonAccessibilityHolder { /** - * Returns the value of the "foi:ptr" property, the internal structure pointer used - * for tagged PDF and other formats that support a structure tree in addition to paged content. - * @return the "foi:ptr" property + * Returns the accessibility properties. + * + * @return the accessibility properties */ - String getPtr(); - + CommonAccessibility getCommonAccessibility(); } diff --git a/src/java/org/apache/fop/fo/properties/StructureTreeElementHolder.java b/src/java/org/apache/fop/fo/properties/StructureTreeElementHolder.java new file mode 100644 index 000000000..6fbb608de --- /dev/null +++ b/src/java/org/apache/fop/fo/properties/StructureTreeElementHolder.java @@ -0,0 +1,38 @@ +/* + * 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.fo.properties; + +import org.apache.fop.accessibility.StructureTreeElement; + +/** + * Implementations of this interface can return the element in the document's + * structure tree that they resulted into. Used for tagged PDF and other formats + * that support a structure tree in addition to paged content. + */ +public interface StructureTreeElementHolder { + + /** + * Returns the element in the document's structure tree that corresponds to this instance. + * + * @return a structure tree element + */ + StructureTreeElement getStructureTreeElement(); + +} diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java index b748c2152..ed2ea4ee9 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java @@ -329,7 +329,6 @@ public class BlockLayoutManager extends BlockStackingLayoutManager addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); - TraitSetter.addPtr(curBlockArea, getBlockFO().getPtr()); // used for accessibility TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(), effSpaceBefore, effSpaceAfter); flush(); diff --git a/src/java/org/apache/fop/layoutmgr/TraitSetter.java b/src/java/org/apache/fop/layoutmgr/TraitSetter.java index c0e451577..0ae499478 100644 --- a/src/java/org/apache/fop/layoutmgr/TraitSetter.java +++ b/src/java/org/apache/fop/layoutmgr/TraitSetter.java @@ -22,6 +22,7 @@ package org.apache.fop.layoutmgr; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.area.Area; import org.apache.fop.area.Trait; import org.apache.fop.datatypes.LengthBase; @@ -29,9 +30,9 @@ import org.apache.fop.datatypes.PercentBaseContext; import org.apache.fop.datatypes.SimplePercentBaseContext; import org.apache.fop.fo.Constants; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; import org.apache.fop.fo.properties.CommonMarginBlock; import org.apache.fop.fo.properties.CommonTextDecoration; -import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; import org.apache.fop.fonts.Font; import org.apache.fop.traits.BorderProps; import org.apache.fop.traits.MinOptMax; @@ -591,13 +592,15 @@ public final class TraitSetter { } /** - * Adds the ptr trait to the area. + * Sets the structure tree element associated to the given area. + * * @param area the area to set the traits on - * @param ptr string + * @param structureTreeElement the element the area is associated to in the document structure */ - public static void addPtr(Area area, String ptr) { - if (ptr != null && ptr.length() > 0) { - area.addTrait(Trait.PTR, ptr); + public static void addStructureTreeElement(Area area, + StructureTreeElement structureTreeElement) { + if (structureTreeElement != null) { + area.addTrait(Trait.STRUCTURE_TREE_ELEMENT, structureTreeElement); } } diff --git a/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java index 218497a53..8c797a48c 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java @@ -85,7 +85,7 @@ public abstract class AbstractGraphicsLayoutManager extends LeafNodeLayoutManage transferForeignAttributes(viewportArea); InlineViewport vp = new InlineViewport(viewportArea); - TraitSetter.addPtr(vp, fobj.getPtr()); // used for accessibility + TraitSetter.addStructureTreeElement(vp, fobj.getStructureTreeElement()); TraitSetter.setProducerID(vp, fobj.getId()); vp.setIPD(imageLayout.getViewportSize().width); vp.setBPD(imageLayout.getViewportSize().height); diff --git a/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java index e090fbae6..8c769924a 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java @@ -136,7 +136,7 @@ public abstract class AbstractPageNumberCitationLayoutManager extends LeafNodeLa text.setBaselineOffset(font.getAscender()); TraitSetter.addFontTraits(text, font); text.addTrait(Trait.COLOR, fobj.getColor()); - TraitSetter.addPtr(text, fobj.getPtr()); // used for accessibility + TraitSetter.addStructureTreeElement(text, fobj.getStructureTreeElement()); TraitSetter.addTextDecoration(text, fobj.getTextDecoration()); } diff --git a/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java index 40c9a324e..1390c04d8 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java @@ -59,7 +59,7 @@ public class BasicLinkLayoutManager extends InlineLayoutManager { private void setupBasicLinkArea(InlineArea area) { BasicLink fobj = (BasicLink) this.fobj; // internal destinations take precedence: - TraitSetter.addPtr(area, fobj.getPtr()); // used for accessibility + TraitSetter.addStructureTreeElement(area, fobj.getStructureTreeElement()); if (fobj.hasInternalDestination()) { String idref = fobj.getInternalDestination(); PageSequenceLayoutManager pslm = getPSLM(); diff --git a/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java index 2178b2e77..4877ff9bd 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java @@ -87,7 +87,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { } TraitSetter.setProducerID(text, node.getId()); TraitSetter.addTextDecoration(text, node.getTextDecoration()); - TraitSetter.addPtr(text, node.getPtr()); // used for accessibility + TraitSetter.addStructureTreeElement(text, node.getStructureTreeElement()); return text; } diff --git a/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java index d8cfe6cda..4b7289b37 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java @@ -19,13 +19,13 @@ package org.apache.fop.layoutmgr.inline; +import org.apache.fop.area.Trait; +import org.apache.fop.area.inline.InlineArea; +import org.apache.fop.area.inline.TextArea; import org.apache.fop.fo.flow.PageNumber; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; -import org.apache.fop.area.inline.InlineArea; -import org.apache.fop.area.inline.TextArea; -import org.apache.fop.area.Trait; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.traits.MinOptMax; @@ -85,7 +85,7 @@ public class PageNumberLayoutManager extends LeafNodeLayoutManager { text.setBaselineOffset(font.getAscender()); TraitSetter.addFontTraits(text, font); text.addTrait(Trait.COLOR, fobj.getColor()); - TraitSetter.addPtr(text, fobj.getPtr()); // used for accessibility + TraitSetter.addStructureTreeElement(text, fobj.getStructureTreeElement()); TraitSetter.addTextDecoration(text, fobj.getTextDecoration()); return text; diff --git a/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java index 81240dec0..8767fe296 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java @@ -31,8 +31,6 @@ import org.apache.fop.area.Trait; import org.apache.fop.area.inline.TextArea; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FOText; -import org.apache.fop.fo.FObj; -import org.apache.fop.fo.properties.StructurePointerPropertySet; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontSelector; import org.apache.fop.layoutmgr.InlineKnuthSequence; @@ -438,7 +436,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { setText(); TraitSetter.addFontTraits(textArea, font); textArea.addTrait(Trait.COLOR, foText.getColor()); - TraitSetter.addPtr(textArea, getPtr()); // used for accessibility + TraitSetter.addStructureTreeElement(textArea, foText.getStructureTreeElement()); TraitSetter.addTextDecoration(textArea, foText.getTextDecoration()); TraitSetter.addFontTraits(textArea, font); return textArea; @@ -577,20 +575,6 @@ public class TextLayoutManager extends LeafNodeLayoutManager { } } - /** - * used for accessibility - * @return ptr of fobj - */ - private String getPtr() { - FObj fobj = parentLayoutManager.getFObj(); - if (fobj instanceof StructurePointerPropertySet) { - return (((StructurePointerPropertySet) fobj).getPtr()); - } else { - //No structure pointer applicable - return null; - } - } - private AreaInfo getAreaInfo(int index) { return (AreaInfo) areaInfos.get(index); } diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java index 543d1a45f..256efd658 100644 --- a/src/java/org/apache/fop/pdf/PDFDocument.java +++ b/src/java/org/apache/fop/pdf/PDFDocument.java @@ -350,25 +350,6 @@ public class PDFDocument { } /** - * Makes sure a Lang entry has been set on the document catalog, setting it - * to a default value if necessary. When accessibility is enabled the - * language must be specified for any text element in the document. - */ - public void enforceLanguageOnRoot() { - if (root.getLanguage() == null) { - String fallbackLanguage; - if (getProfile().getPDFAMode().isPDFA1LevelA()) { - //According to Annex B of ISO-19005-1:2005(E), section B.2 - fallbackLanguage = "x-unknown"; - } else { - //No language has been set on the first page-sequence, so fall back to "en". - fallbackLanguage = "en"; - } - root.setLanguage(fallbackLanguage); - } - } - - /** * Get the {@link PDFInfo} object for this document. * * @return the {@link PDFInfo} object diff --git a/src/java/org/apache/fop/pdf/PDFProfile.java b/src/java/org/apache/fop/pdf/PDFProfile.java index fb4575105..b4b43c422 100644 --- a/src/java/org/apache/fop/pdf/PDFProfile.java +++ b/src/java/org/apache/fop/pdf/PDFProfile.java @@ -133,8 +133,12 @@ public class PDFProfile { //---------=== Info and validation methods ===--------- + private String format(String pattern, Object[] args) { + return MessageFormat.format(pattern, args); + } + private String format(String pattern, Object arg) { - return MessageFormat.format(pattern, new Object[] {arg}); + return format(pattern, new Object[] {arg}); } /** Checks if encryption is allowed. */ diff --git a/src/java/org/apache/fop/pdf/PDFRoot.java b/src/java/org/apache/fop/pdf/PDFRoot.java index f71841005..76f2587de 100644 --- a/src/java/org/apache/fop/pdf/PDFRoot.java +++ b/src/java/org/apache/fop/pdf/PDFRoot.java @@ -21,6 +21,9 @@ package org.apache.fop.pdf; import java.io.IOException; import java.io.OutputStream; +import java.util.Locale; + +import org.apache.fop.util.LanguageTags; /** * Class representing a Root (/Catalog) object. @@ -69,6 +72,7 @@ public class PDFRoot extends PDFDictionary { setObjectNumber(objnum); put("Type", new PDFName("Catalog")); setRootPages(pages); + setLanguage("x-unknown"); } /** {@inheritDoc} */ @@ -251,13 +255,17 @@ public class PDFRoot extends PDFDictionary { } /** - * Sets the language identifier of the document. - * @param lang the language identifier of the document. + * Sets the locale of the document. + * @param locale the locale of the document. */ - public void setLanguage(String lang) { - if (lang == null) { - throw new NullPointerException("lang must not be null"); + public void setLanguage(Locale locale) { + if (locale == null) { + throw new NullPointerException("locale must not be null"); } + setLanguage(LanguageTags.toLanguageTag(locale)); + } + + private void setLanguage(String lang) { put("Lang", lang); } diff --git a/src/java/org/apache/fop/pdf/PDFStructElem.java b/src/java/org/apache/fop/pdf/PDFStructElem.java index 4fb8cbcd5..160d55384 100644 --- a/src/java/org/apache/fop/pdf/PDFStructElem.java +++ b/src/java/org/apache/fop/pdf/PDFStructElem.java @@ -19,18 +19,29 @@ package org.apache.fop.pdf; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; -import org.apache.fop.util.XMLUtil; +import org.apache.fop.accessibility.StructureTreeElement; +import org.apache.fop.util.LanguageTags; /** * Class representing a PDF Structure Element. */ -public class PDFStructElem extends PDFDictionary { +public class PDFStructElem extends PDFDictionary implements StructureTreeElement { private PDFStructElem parentElement; /** + * Elements to be added to the kids array. + */ + protected List<PDFObject> kids; + + /** * Creates a new structure element. * * @param parent parent of this element @@ -57,21 +68,12 @@ public class PDFStructElem extends PDFDictionary { /** {@inheritDoc} */ public void setParent(PDFObject parent) { - if (parent != null) { + if (parent != null && parent.hasObjectNumber()) { put("P", new PDFReference(parent)); } } /** - * Returns the kids of this structure element. - * - * @return the value of the K entry - */ - private PDFArray getKids() { - return (PDFArray) get("K"); - } - - /** * Add a kid to this structure element. This element will then add itself to * its parent structure element if it has not already, and so will the * parent, and so on. @@ -79,24 +81,10 @@ public class PDFStructElem extends PDFDictionary { * @param kid element to be added */ public void addKid(PDFObject kid) { - PDFArray kids = getKids(); if (kids == null) { - kids = new PDFArray(); - put("K", kids); + kids = new ArrayList<PDFObject>(); } kids.add(kid); - joinHierarchy(); - } - - private boolean containsKid(PDFObject kid) { - PDFArray kids = getKids(); - return kids != null && kids.contains(kid); - } - - private void joinHierarchy() { - if (parentElement != null && !parentElement.containsKid(this)) { - parentElement.addKid(this); - } } /** @@ -109,7 +97,6 @@ public class PDFStructElem extends PDFDictionary { */ public void setMCIDKid(int mcid) { put("K", mcid); - joinHierarchy(); } /** @@ -127,7 +114,7 @@ public class PDFStructElem extends PDFDictionary { * @return the value of the S entry */ public PDFName getStructureType() { - return (PDFName)get("S"); + return (PDFName) get("S"); } /** @@ -145,7 +132,7 @@ public class PDFStructElem extends PDFDictionary { * @param language a value for the Lang entry */ public void setLanguage(Locale language) { - setLanguage(XMLUtil.toRFC3066(language)); + setLanguage(LanguageTags.toLanguageTag(language)); } /** @@ -154,6 +141,71 @@ public class PDFStructElem extends PDFDictionary { * @return the value of the Lang entry (<code>null</code> if no language was specified) */ public String getLanguage() { - return (String)get("Lang"); + return (String) get("Lang"); + } + + @Override + protected void writeDictionary(OutputStream out, StringBuilder textBuffer) throws IOException { + attachKids(); + super.writeDictionary(out, textBuffer); } + + /** + * Attaches all valid kids to the kids array. + * + * @return true iff 1+ kids were added to the kids array + */ + protected boolean attachKids() { + List<PDFObject> validKids = new ArrayList<PDFObject>(); + if (kids != null) { + for (PDFObject kid : kids) { + if (kid instanceof Placeholder) { + if (((Placeholder) kid).attachKids()) { + validKids.add(kid); + } + } else { + validKids.add(kid); + } + } + } + boolean kidsAttached = !validKids.isEmpty(); + if (kidsAttached) { + PDFArray array = new PDFArray(); + for (PDFObject ob : validKids) { + array.add(ob); + } + put("K", array); + } + return kidsAttached; + } + + /** + * Class representing a placeholder for a PDF Structure Element. + */ + public static class Placeholder extends PDFStructElem { + + @Override + public void outputInline(OutputStream out, StringBuilder textBuffer) throws IOException { + if (kids != null) { + assert kids.size() > 0; + for (int i = 0; i < kids.size(); i++) { + if (i > 0) { + textBuffer.append(' '); + } + Object obj = kids.get(i); + formatObject(obj, out, textBuffer); + } + } + } + + /** + * Constructor + * @param parent - + * @param name - + */ + public Placeholder(PDFObject parent, String name) { + super(parent, new PDFName(name)); + } + } + } diff --git a/src/java/org/apache/fop/render/AbstractRenderer.java b/src/java/org/apache/fop/render/AbstractRenderer.java index 659748df9..3c07c0006 100644 --- a/src/java/org/apache/fop/render/AbstractRenderer.java +++ b/src/java/org/apache/fop/render/AbstractRenderer.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Set; import org.w3c.dom.Document; @@ -152,6 +153,10 @@ public abstract class AbstractRenderer return false; } + /** {@inheritDoc} */ + public void setDocumentLocale(Locale locale) { + } + /** * {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/Renderer.java b/src/java/org/apache/fop/render/Renderer.java index 5f5596dfb..3ff6ffb2a 100644 --- a/src/java/org/apache/fop/render/Renderer.java +++ b/src/java/org/apache/fop/render/Renderer.java @@ -22,6 +22,7 @@ package org.apache.fop.render; // Java import java.io.IOException; import java.io.OutputStream; +import java.util.Locale; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; @@ -109,6 +110,12 @@ public interface Renderer { boolean supportsOutOfOrder(); /** + * + * @param locale Locale of the language + */ + void setDocumentLocale(Locale locale); + + /** * Tells the renderer to process an item not explicitly placed on the * document (e.g., PDF bookmarks). Note - not all renderers will process * all off-document items. diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java b/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java index b156b6c3a..ad4e6ad0e 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java @@ -19,6 +19,13 @@ package org.apache.fop.render.intermediate; +import java.util.Locale; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.accessibility.DummyStructureTreeEventHandler; +import org.apache.fop.accessibility.StructureTreeEventHandler; import org.apache.fop.apps.FOUserAgent; /** @@ -53,6 +60,11 @@ public abstract class AbstractIFDocumentHandler implements IFDocumentHandler { } /** {@inheritDoc} */ + public StructureTreeEventHandler getStructureTreeEventHandler() { + return DummyStructureTreeEventHandler.INSTANCE; + } + + /** {@inheritDoc} */ public IFDocumentNavigationHandler getDocumentNavigationHandler() { return null; //By default, this is not supported } @@ -66,6 +78,10 @@ public abstract class AbstractIFDocumentHandler implements IFDocumentHandler { } /** {@inheritDoc} */ + public void setDocumentLocale(Locale locale) { + } + + /** {@inheritDoc} */ public void startDocumentHeader() throws IFException { //nop } @@ -104,5 +120,4 @@ public abstract class AbstractIFDocumentHandler implements IFDocumentHandler { public void endPageTrailer() throws IFException { //nop } - } diff --git a/src/java/org/apache/fop/render/intermediate/IFConstants.java b/src/java/org/apache/fop/render/intermediate/IFConstants.java index 2b16b343d..c7bf13e31 100644 --- a/src/java/org/apache/fop/render/intermediate/IFConstants.java +++ b/src/java/org/apache/fop/render/intermediate/IFConstants.java @@ -39,6 +39,8 @@ public interface IFConstants extends XMLConstants { String EL_HEADER = "header"; /** element name trailer */ String EL_TRAILER = "trailer"; + /** element name locale */ + String EL_LOCALE = "locale"; /** element name page-sequence */ String EL_PAGE_SEQUENCE = "page-sequence"; /** element name page */ diff --git a/src/java/org/apache/fop/render/intermediate/IFContext.java b/src/java/org/apache/fop/render/intermediate/IFContext.java index b8be97253..c59a02ba8 100644 --- a/src/java/org/apache/fop/render/intermediate/IFContext.java +++ b/src/java/org/apache/fop/render/intermediate/IFContext.java @@ -25,6 +25,7 @@ import java.util.Map; import org.apache.xmlgraphics.util.QName; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOUserAgent; /** @@ -46,7 +47,7 @@ public class IFContext { private Locale language; - private String structurePointer; + private StructureTreeElement structureTreeElement; private String id = ""; @@ -132,29 +133,31 @@ public class IFContext { } /** - * Sets the structure pointer for the following painted marks. This method is used when - * accessibility features are enabled. - * @param ptr the structure pointer + * Sets the structure tree element to which the subsequently painted marks + * will correspond. This method is used when accessibility features are + * enabled. + * + * @param structureTreeElement the structure tree element */ - public void setStructurePointer(String ptr) { - this.structurePointer = ptr; + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; } /** - * Resets the current structure pointer. - * @see #setStructurePointer(String) + * Resets the current structure tree element. + * @see #setStructureTreeElement(String) */ - public void resetStructurePointer() { - setStructurePointer(null); + public void resetStructureTreeElement() { + setStructureTreeElement(null); } /** - * Returns the current structure pointer. - * @return the structure pointer (or null if no pointer is active) - * @see #setStructurePointer(String) + * Returns the current structure tree element. + * @return the structure tree element (or null if no element is active) + * @see #setStructureTreeElement(String) */ - public String getStructurePointer() { - return this.structurePointer; + public StructureTreeElement getStructureTreeElement() { + return this.structureTreeElement; } /** diff --git a/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java b/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java index af1451fe3..6cb8f2795 100644 --- a/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java +++ b/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java @@ -20,9 +20,11 @@ package org.apache.fop.render.intermediate; import java.awt.Dimension; +import java.util.Locale; import javax.xml.transform.Result; +import org.apache.fop.accessibility.StructureTreeEventHandler; import org.apache.fop.fonts.FontInfo; /** @@ -32,6 +34,7 @@ import org.apache.fop.fonts.FontInfo; * <p> * <pre> * startDocument() + * [setDocumentLocale()] * startDocumentHeader() * [handleExtension()]* * endDocumentHeader() @@ -118,6 +121,11 @@ public interface IFDocumentHandler { IFDocumentHandlerConfigurator getConfigurator(); /** + * @return the structure tree builder + */ + StructureTreeEventHandler getStructureTreeEventHandler(); + + /** * Returns a document navigation handler if this feature is supported. * @return the document navigation handler or null if not supported */ @@ -152,6 +160,11 @@ public interface IFDocumentHandler { void endDocument() throws IFException; /** + * @param locale Locale of the document. + */ + void setDocumentLocale(Locale locale); + + /** * Indicates the start of the document header. This method is called right after the * {@link #startDocument()} method. Extensions sent to this painter between * {@link #startDocumentHeader()} and {@link #endDocumentHeader()} apply to the document as @@ -261,7 +274,4 @@ public interface IFDocumentHandler { * @throws IFException if an error occurs while handling this event */ void handleExtensionObject(Object extension) throws IFException; - - //TODO Prototype the following: - //ContentHandler handleExtension() throws Exception } diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java index 313cee685..36f1fd841 100644 --- a/src/java/org/apache/fop/render/intermediate/IFParser.java +++ b/src/java/org/apache/fop/render/intermediate/IFParser.java @@ -25,6 +25,7 @@ import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -48,11 +49,13 @@ import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.util.QName; import org.apache.fop.accessibility.AccessibilityEventProducer; -import org.apache.fop.accessibility.StructureTreeBuilder; +import org.apache.fop.accessibility.StructureTreeElement; +import org.apache.fop.accessibility.StructureTreeEventHandler; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fo.ElementMapping; import org.apache.fop.fo.ElementMappingRegistry; import org.apache.fop.fo.expr.PropertyException; +import org.apache.fop.fo.extensions.InternalElementMapping; import org.apache.fop.render.intermediate.extensions.DocumentNavigationExtensionConstants; import org.apache.fop.render.intermediate.extensions.DocumentNavigationHandler; import org.apache.fop.traits.BorderProps; @@ -62,7 +65,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.LanguageTags; import org.apache.fop.util.XMLUtil; /** @@ -153,24 +156,59 @@ public class IFParser implements IFConstants { private ContentHandler navParser; - private StructureTreeBuilder structureTreeBuilder; - - private ContentHandler structureTreeBuilderWrapper; + private ContentHandler structureTreeHandler; private Attributes pageSequenceAttributes; - private final class StructureTreeBuilderWrapper extends DelegatingContentHandler { + private Map<String, StructureTreeElement> structureTreeElements + = new HashMap<String, StructureTreeElement>(); - private StructureTreeBuilderWrapper() - throws SAXException { - super(structureTreeBuilder.getHandlerForNextPageSequence()); + private final class StructureTreeHandler extends DefaultHandler { + + private final StructureTreeEventHandler structureTreeEventHandler; + + private StructureTreeHandler(StructureTreeEventHandler structureTreeEventHandler, + Locale pageSequenceLanguage) throws SAXException { + this.structureTreeEventHandler = structureTreeEventHandler; + structureTreeEventHandler.startPageSequence(pageSequenceLanguage); } public void endDocument() throws SAXException { - super.endDocument(); startIFElement(EL_PAGE_SEQUENCE, pageSequenceAttributes); pageSequenceAttributes = null; } + + @Override + public void startElement(String uri, String localName, String qName, + Attributes attributes) throws SAXException { + if (!"structure-tree".equals(localName)) { + if (localName.equals("marked-content")) { + localName = "#PCDATA"; + } + String structID = attributes.getValue(InternalElementMapping.URI, + InternalElementMapping.STRUCT_ID); + if (structID == null) { + structureTreeEventHandler.startNode(localName, attributes); + } else if (localName.equals("external-graphic") + || localName.equals("instream-foreign-object")) { + StructureTreeElement structureTreeElement + = structureTreeEventHandler.startImageNode(localName, attributes); + structureTreeElements.put(structID, structureTreeElement); + } else { + StructureTreeElement structureTreeElement = structureTreeEventHandler + .startReferencedNode(localName, attributes); + structureTreeElements.put(structID, structureTreeElement); + } + } + } + + @Override + public void endElement(String uri, String localName, String arqNameg2) + throws SAXException { + if (!"structure-tree".equals(localName)) { + structureTreeEventHandler.endNode(localName); + } + } } public Handler(IFDocumentHandler documentHandler, FOUserAgent userAgent, @@ -180,6 +218,7 @@ public class IFParser implements IFConstants { this.elementMappingRegistry = elementMappingRegistry; elementHandlers.put(EL_DOCUMENT, new DocumentHandler()); elementHandlers.put(EL_HEADER, new DocumentHeaderHandler()); + elementHandlers.put(EL_LOCALE, new LocaleHandler()); elementHandlers.put(EL_TRAILER, new DocumentTrailerHandler()); elementHandlers.put(EL_PAGE_SEQUENCE, new PageSequenceHandler()); elementHandlers.put(EL_PAGE, new PageHandler()); @@ -197,11 +236,6 @@ 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()) { - structureTreeBuilder = new StructureTreeBuilder(tFactory); - userAgent.setStructureTree(structureTreeBuilder.getStructureTree()); - } } private void establishForeignAttributes(Map<QName, String> foreignAttributes) { @@ -212,14 +246,6 @@ public class IFParser implements IFConstants { documentHandler.getContext().resetForeignAttributes(); } - private void establishStructurePointer(String ptr) { - documentHandler.getContext().setStructurePointer(ptr); - } - - private void resetStructurePointer() { - documentHandler.getContext().resetStructurePointer(); - } - /** {@inheritDoc} */ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { @@ -231,10 +257,13 @@ public class IFParser implements IFConstants { if (NAMESPACE.equals(uri)) { if (localName.equals(EL_PAGE_SEQUENCE) && userAgent.isAccessibilityEnabled()) { pageSequenceAttributes = new AttributesImpl(attributes); - structureTreeBuilderWrapper = new StructureTreeBuilderWrapper(); + Locale language = getLanguage(attributes); + structureTreeHandler = new StructureTreeHandler( + userAgent.getStructureTreeEventHandler(), language); + } else if (localName.equals(EL_STRUCTURE_TREE)) { if (userAgent.isAccessibilityEnabled()) { - delegate = structureTreeBuilderWrapper; + delegate = structureTreeHandler; } else { /* Delegate to a handler that does nothing */ delegate = new DefaultHandler(); @@ -260,7 +289,8 @@ public class IFParser implements IFConstants { } else if (DocumentNavigationExtensionConstants.NAMESPACE.equals(uri)) { if (this.navParser == null) { this.navParser = new DocumentNavigationHandler( - this.documentHandler.getDocumentNavigationHandler()); + this.documentHandler.getDocumentNavigationHandler(), + structureTreeElements); } delegate = this.navParser; delegateDepth++; @@ -299,6 +329,11 @@ public class IFParser implements IFConstants { } } + private static Locale getLanguage(Attributes attributes) { + String xmllang = attributes.getValue(XML_NAMESPACE, "lang"); + return (xmllang == null) ? null : LanguageTags.toLocale(xmllang); + } + private boolean startIFElement(String localName, Attributes attributes) throws SAXException { lastAttributes = new AttributesImpl(attributes); @@ -413,6 +448,12 @@ public class IFParser implements IFConstants { } + private class LocaleHandler extends AbstractElementHandler { + public void startElement(Attributes attributes) throws IFException { + documentHandler.setDocumentLocale(getLanguage(attributes)); + } + } + private class DocumentTrailerHandler extends AbstractElementHandler { public void startElement(Attributes attributes) throws IFException { @@ -429,10 +470,9 @@ public class IFParser implements IFConstants { public void startElement(Attributes attributes) throws IFException { String id = attributes.getValue("id"); - String xmllang = attributes.getValue(XML_NAMESPACE, "lang"); - if (xmllang != null) { - documentHandler.getContext().setLanguage( - XMLUtil.convertRFC3066ToLocale(xmllang)); + Locale language = getLanguage(attributes); + if (language != null) { + documentHandler.getContext().setLanguage(language); } Map<QName, String> foreignAttributes = getForeignAttributes(lastAttributes); establishForeignAttributes(foreignAttributes); @@ -578,9 +618,9 @@ public class IFParser implements IFConstants { s = lastAttributes.getValue("word-spacing"); int wordSpacing = (s != null ? Integer.parseInt(s) : 0); int[] dx = XMLUtil.getAttributeAsIntArray(lastAttributes, "dx"); - setStructurePointer(lastAttributes); + establishStructureTreeElement(lastAttributes); painter.drawText(x, y, letterSpacing, wordSpacing, dx, content.toString()); - resetStructurePointer(); + resetStructureTreeElement(); } public boolean ignoreCharacters() { @@ -675,7 +715,7 @@ public class IFParser implements IFConstants { int height = Integer.parseInt(lastAttributes.getValue("height")); Map<QName, String> foreignAttributes = getForeignAttributes(lastAttributes); establishForeignAttributes(foreignAttributes); - setStructurePointer(lastAttributes); + establishStructureTreeElement(lastAttributes); if (foreignObject != null) { painter.drawImage(foreignObject, new Rectangle(x, y, width, height)); @@ -689,7 +729,7 @@ public class IFParser implements IFConstants { painter.drawImage(uri, new Rectangle(x, y, width, height)); } resetForeignAttributes(); - resetStructurePointer(); + resetStructureTreeElement(); inForeignObject = false; } @@ -743,13 +783,20 @@ public class IFParser implements IFConstants { return foreignAttributes; } - private void setStructurePointer(Attributes attributes) { - String ptr = attributes.getValue("ptr"); - if (ptr != null && ptr.length() > 0) { - establishStructurePointer(ptr); + private void establishStructureTreeElement(Attributes attributes) { + String structRef = attributes.getValue(InternalElementMapping.URI, + InternalElementMapping.STRUCT_REF); + if (structRef != null && structRef.length() > 0) { + assert structureTreeElements.containsKey(structRef); + StructureTreeElement structureTreeElement = structureTreeElements.get(structRef); + documentHandler.getContext().setStructureTreeElement(structureTreeElement); } } + private void resetStructureTreeElement() { + documentHandler.getContext().resetStructureTreeElement(); + } + /** {@inheritDoc} */ public void characters(char[] ch, int start, int length) throws SAXException { if (delegate != null) { diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java index 9d8fb6e4b..4fec6c623 100644 --- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java +++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java @@ -50,6 +50,7 @@ import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter; import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema; import org.apache.fop.Version; +import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.MimeConstants; @@ -231,7 +232,11 @@ public class IFRenderer extends AbstractPathOrientedRenderer { */ protected IFDocumentHandler createDefaultDocumentHandler() { IFSerializer serializer = new IFSerializer(); - serializer.setContext(new IFContext(getUserAgent())); + FOUserAgent userAgent = getUserAgent(); + serializer.setContext(new IFContext(userAgent)); + if (userAgent.isAccessibilityEnabled()) { + userAgent.setStructureTreeEventHandler(serializer.getStructureTreeEventHandler()); + } return serializer; } @@ -298,6 +303,11 @@ public class IFRenderer extends AbstractPathOrientedRenderer { log.debug("Rendering finished."); } + @Override + public void setDocumentLocale(Locale locale) { + documentHandler.setDocumentLocale(locale); + } + /** {@inheritDoc} */ public void processOffDocumentItem(OffDocumentItem odi) { if (odi instanceof DestinationData) { @@ -623,12 +633,12 @@ public class IFRenderer extends AbstractPathOrientedRenderer { documentHandler.getContext().resetForeignAttributes(); } - private void establishStructurePointer(String ptr) { - documentHandler.getContext().setStructurePointer(ptr); + private void establishStructureTreeElement(StructureTreeElement structureTreeElement) { + documentHandler.getContext().setStructureTreeElement(structureTreeElement); } private void resetStructurePointer() { - documentHandler.getContext().resetStructurePointer(); + documentHandler.getContext().resetStructureTreeElement(); } /** {@inheritDoc} */ @@ -845,8 +855,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer { /** {@inheritDoc} */ public void renderInlineViewport(InlineViewport viewport) { - String ptr = (String) viewport.getTrait(Trait.PTR); - establishStructurePointer(ptr); + StructureTreeElement structElem + = (StructureTreeElement) viewport.getTrait(Trait.STRUCTURE_TREE_ELEMENT); + establishStructureTreeElement(structElem); pushdID(viewport); Dimension dim = new Dimension(viewport.getIPD(), viewport.getBPD()); viewportDimensionStack.push(dim); @@ -906,7 +917,6 @@ public class IFRenderer extends AbstractPathOrientedRenderer { // stuff we only need if a link must be created: Rectangle ipRect = null; AbstractAction action = null; - String ptr = (String) ip.getTrait(Trait.PTR); // used for accessibility // make sure the rect is determined *before* calling super! int ipp = currentIPPosition; int bpp = currentBPPosition + ip.getOffset(); @@ -950,7 +960,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer { // warn if link trait found but not allowed, else create link if (linkTraitFound) { - action.setStructurePointer(ptr); // used for accessibility + StructureTreeElement structElem + = (StructureTreeElement) ip.getTrait(Trait.STRUCTURE_TREE_ELEMENT); + action.setStructureTreeElement(structElem); Link link = new Link(action, ipRect); this.deferredLinks.add(link); } @@ -1003,8 +1015,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer { String fontName = getInternalFontNameForArea(text); int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue(); - String ptr = (String)text.getTrait(Trait.PTR); // used for accessibility - establishStructurePointer(ptr); + StructureTreeElement structElem + = (StructureTreeElement) text.getTrait(Trait.STRUCTURE_TREE_ELEMENT); + establishStructureTreeElement(structElem); // This assumes that *all* CIDFonts use a /ToUnicode mapping Typeface tf = getTypeface(fontName); diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java index 471a982ca..7b9d95849 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java @@ -31,19 +31,18 @@ import java.util.Locale; import java.util.Map; import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; import org.apache.xmlgraphics.util.QName; import org.apache.xmlgraphics.util.XMLizable; -import org.apache.fop.accessibility.StructureTree; +import org.apache.fop.accessibility.StructureTreeEventHandler; +import org.apache.fop.fo.extensions.InternalElementMapping; import org.apache.fop.fonts.FontInfo; import org.apache.fop.render.PrintRendererConfigurator; import org.apache.fop.render.RenderingContext; +import org.apache.fop.render.intermediate.IFStructureTreeBuilder.IFStructureTreeElement; import org.apache.fop.render.intermediate.extensions.AbstractAction; import org.apache.fop.render.intermediate.extensions.Bookmark; import org.apache.fop.render.intermediate.extensions.BookmarkTree; @@ -54,9 +53,11 @@ import org.apache.fop.traits.BorderProps; import org.apache.fop.traits.RuleStyle; import org.apache.fop.util.ColorUtil; import org.apache.fop.util.DOM2SAX; +import org.apache.fop.util.LanguageTags; import org.apache.fop.util.XMLConstants; import org.apache.fop.util.XMLUtil; + /** * IFPainter implementation that serializes the intermediate format to XML. */ @@ -71,11 +72,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler private String currentID = ""; - /** - * Default constructor. - */ - public IFSerializer() { - } + private IFStructureTreeBuilder structureTreeBuilder; /** {@inheritDoc} */ @Override @@ -150,6 +147,14 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } } + @Override + public StructureTreeEventHandler getStructureTreeEventHandler() { + if (structureTreeBuilder == null) { + structureTreeBuilder = new IFStructureTreeBuilder(); + } + return structureTreeBuilder; + } + /** {@inheritDoc} */ @Override public void startDocument() throws IFException { @@ -160,12 +165,27 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler handler.startPrefixMapping(XLINK_PREFIX, XLINK_NAMESPACE); handler.startPrefixMapping(DocumentNavigationExtensionConstants.PREFIX, DocumentNavigationExtensionConstants.NAMESPACE); + handler.startPrefixMapping(InternalElementMapping.STANDARD_PREFIX, + InternalElementMapping.URI); handler.startElement(EL_DOCUMENT); } catch (SAXException e) { throw new IFException("SAX error in startDocument()", e); } } + @Override + public void setDocumentLocale(Locale locale) { + AttributesImpl atts = new AttributesImpl(); + atts.addAttribute(XML_NAMESPACE, "lang", "xml:lang", XMLUtil.CDATA, + LanguageTags.toLanguageTag(locale)); + try { + handler.startElement(EL_LOCALE, atts); + handler.endElement(EL_LOCALE); + } catch (SAXException e) { + throw new RuntimeException("Unable to create the " + EL_LOCALE + " element.", e); + } + } + /** {@inheritDoc} */ @Override public void startDocumentHeader() throws IFException { @@ -227,20 +247,14 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler Locale lang = getContext().getLanguage(); if (lang != null) { atts.addAttribute(XML_NAMESPACE, "lang", "xml:lang", XMLUtil.CDATA, - XMLUtil.toRFC3066(lang)); + LanguageTags.toLanguageTag(lang)); } XMLUtil.addAttribute(atts, XMLConstants.XML_SPACE, "preserve"); addForeignAttributes(atts); handler.startElement(EL_PAGE_SEQUENCE, atts); if (this.getUserAgent().isAccessibilityEnabled()) { - StructureTree structureTree = getUserAgent().getStructureTree(); - handler.startElement(EL_STRUCTURE_TREE); // add structure tree - NodeList nodes = structureTree.getPageSequence(pageSequenceIndex++); - for (int i = 0, n = nodes.getLength(); i < n; i++) { - Node node = nodes.item(i); - new DOM2SAX(handler).writeFragment(node); - } - handler.endElement(EL_STRUCTURE_TREE); + assert (structureTreeBuilder != null); + structureTreeBuilder.replayEventsForPageSequence(handler, pageSequenceIndex++); } } catch (SAXException e) { throw new IFException("SAX error in startPageSequence()", e); @@ -250,6 +264,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler /** {@inheritDoc} */ public void endPageSequence() throws IFException { try { + handler.endElement(EL_PAGE_SEQUENCE); } catch (SAXException e) { throw new IFException("SAX error in endPageSequence()", e); @@ -428,7 +443,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler addAttribute(atts, "width", Integer.toString(rect.width)); addAttribute(atts, "height", Integer.toString(rect.height)); addForeignAttributes(atts); - addStructurePointerAttribute(atts); + addStructureReference(atts); handler.element(EL_IMAGE, atts); } catch (SAXException e) { throw new IFException("SAX error in startGroup()", e); @@ -456,7 +471,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler addAttribute(atts, "width", Integer.toString(rect.width)); addAttribute(atts, "height", Integer.toString(rect.height)); addForeignAttributes(atts); - addStructurePointerAttribute(atts); + addStructureReference(atts); handler.startElement(EL_IMAGE, atts); new DOM2SAX(handler).writeDocument(doc, true); handler.endElement(EL_IMAGE); @@ -571,7 +586,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler if (dx != null) { addAttribute(atts, "dx", IFUtil.toString(dx)); } - addStructurePointerAttribute(atts); + addStructureReference(atts); handler.startElement(EL_TEXT, atts); char[] chars = text.toCharArray(); handler.characters(chars, 0, chars.length); @@ -671,13 +686,22 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler XMLUtil.addAttribute(atts, localName, value); } - private void addStructurePointerAttribute(AttributesImpl atts) { - String ptr = getContext().getStructurePointer(); - if (ptr != null) { - addAttribute(atts, "ptr", ptr); + private void addStructureReference(AttributesImpl atts) { + IFStructureTreeElement structureTreeElement + = (IFStructureTreeElement) getContext().getStructureTreeElement(); + if (structureTreeElement != null) { + addStructRefAttribute(atts, structureTreeElement.getId()); } } + private void addStructRefAttribute(AttributesImpl atts, String id) { + atts.addAttribute(InternalElementMapping.URI, + InternalElementMapping.STRUCT_REF, + InternalElementMapping.STANDARD_PREFIX + ":" + InternalElementMapping.STRUCT_REF, + XMLConstants.CDATA, + id); + } + private void addID() throws SAXException { String id = getContext().getID(); if (!currentID.equals(id)) { @@ -762,7 +786,8 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler atts.addAttribute(null, "rect", "rect", XMLConstants.CDATA, IFUtil.toString(link.getTargetRect())); if (getUserAgent().isAccessibilityEnabled()) { - addAttribute(atts, "ptr", link.getAction().getStructurePointer()); + addStructRefAttribute(atts, + ((IFStructureTreeElement) link.getAction().getStructureTreeElement()).getId()); } try { handler.startElement(DocumentNavigationExtensionConstants.LINK, atts); @@ -806,5 +831,4 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler throw new IFException("SAX error serializing object", e); } } - } diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializerMaker.java b/src/java/org/apache/fop/render/intermediate/IFSerializerMaker.java index 699fd0549..c0060ab8e 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializerMaker.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializerMaker.java @@ -31,6 +31,9 @@ public class IFSerializerMaker extends AbstractIFDocumentHandlerMaker { public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) { IFSerializer handler = new IFSerializer(); handler.setContext(new IFContext(ua)); + if (ua.isAccessibilityEnabled()) { + ua.setStructureTreeEventHandler(handler.getStructureTreeEventHandler()); + } return handler; } diff --git a/src/java/org/apache/fop/render/intermediate/IFStructureTreeBuilder.java b/src/java/org/apache/fop/render/intermediate/IFStructureTreeBuilder.java new file mode 100644 index 000000000..3d9885914 --- /dev/null +++ b/src/java/org/apache/fop/render/intermediate/IFStructureTreeBuilder.java @@ -0,0 +1,239 @@ +/* + * 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.render.intermediate; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +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.fop.accessibility.StructureTree2SAXEventAdapter; +import org.apache.fop.accessibility.StructureTreeElement; +import org.apache.fop.accessibility.StructureTreeEventHandler; +import org.apache.fop.fo.extensions.InternalElementMapping; +import org.apache.fop.util.XMLUtil; + +/** + * Saves structure tree events as SAX events in order to replay them when it's + * time to stream the structure tree to the output. + */ +final class IFStructureTreeBuilder implements StructureTreeEventHandler { + + static final class IFStructureTreeElement implements StructureTreeElement { + + private final String id; + + IFStructureTreeElement() { + this.id = null; + } + + IFStructureTreeElement(String id) { + this.id = id; + } + + public String getId() { + return id; + } + } + + /** A SAX handler that records events to replay them later. */ + static class SAXEventRecorder extends DefaultHandler { + + private final List<SAXEventRecorder.Event> events = new ArrayList<SAXEventRecorder.Event>(); + + private abstract static class Event { + abstract void replay(ContentHandler handler) throws SAXException; + } + + private abstract static class Element extends SAXEventRecorder.Event { + + protected final String uri; + protected final String localName; + protected final String qName; + + private Element(String uri, String localName, String qName) { + this.uri = uri; + this.localName = localName; + this.qName = qName; + } + } + + private static final class StartElement extends SAXEventRecorder.Element { + + private final Attributes attributes; + + private StartElement(String uri, String localName, String qName, + Attributes attributes) { + super(uri, localName, qName); + this.attributes = attributes; + } + + @Override + void replay(ContentHandler handler) throws SAXException { + handler.startElement(uri, localName, qName, attributes); + } + } + + private static final class EndElement extends SAXEventRecorder.Element { + + private EndElement(String uri, String localName, String qName) { + super(uri, localName, qName); + } + + @Override + void replay(ContentHandler handler) throws SAXException { + handler.endElement(uri, localName, qName); + } + } + + private static final class StartPrefixMapping extends SAXEventRecorder.Event { + + private final String prefix; + private final String uri; + + private StartPrefixMapping(String prefix, String uri) { + this.prefix = prefix; + this.uri = uri; + } + + @Override + void replay(ContentHandler handler) throws SAXException { + handler.startPrefixMapping(prefix, uri); + } + } + + private static final class EndPrefixMapping extends SAXEventRecorder.Event { + + private final String prefix; + + private EndPrefixMapping(String prefix) { + this.prefix = prefix; + } + + @Override + void replay(ContentHandler handler) throws SAXException { + handler.endPrefixMapping(prefix); + } + } + + @Override + public void startElement(String uri, String localName, String qName, + Attributes attributes) throws SAXException { + events.add(new StartElement(uri, localName, qName, attributes)); + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + events.add(new EndElement(uri, localName, qName)); + } + + @Override + public void startPrefixMapping(String prefix, String uri) throws SAXException { + events.add(new StartPrefixMapping(prefix, uri)); + } + + @Override + public void endPrefixMapping(String prefix) throws SAXException { + events.add(new EndPrefixMapping(prefix)); + } + + /** + * Replays the recorded events. + * + * @param handler {@code ContentHandler} to replay events on + */ + public void replay(ContentHandler handler) throws SAXException { + for (SAXEventRecorder.Event e : events) { + e.replay(handler); + } + } + } + + private StructureTreeEventHandler delegate; + + private final List<SAXEventRecorder> pageSequenceEventRecorders + = new ArrayList<SAXEventRecorder>(); + + private int idCounter; + + /** + * Replay SAX events for a page sequence. + * @param handler The handler that receives SAX events + * @param pageSequenceIndex The index of the page sequence + * @throws SAXException + */ + public void replayEventsForPageSequence(ContentHandler handler, + int pageSequenceIndex) throws SAXException { + pageSequenceEventRecorders.get(pageSequenceIndex).replay(handler); + } + + public void startPageSequence(Locale locale) { + SAXEventRecorder eventRecorder = new SAXEventRecorder(); + pageSequenceEventRecorders.add(eventRecorder); + delegate = StructureTree2SAXEventAdapter.newInstance(eventRecorder); + delegate.startPageSequence(locale); + } + + public void endPageSequence() { + delegate.endPageSequence(); + } + + public StructureTreeElement startNode(String name, Attributes attributes) { + delegate.startNode(name, attributes); + return new IFStructureTreeElement(); + } + + public void endNode(String name) { + delegate.endNode(name); + } + + public StructureTreeElement startImageNode(String name, Attributes attributes) { + String id = getNextID(); + AttributesImpl atts = addIDAttribute(attributes, id); + delegate.startImageNode(name, atts); + return new IFStructureTreeElement(id); + } + + public StructureTreeElement startReferencedNode(String name, Attributes attributes) { + String id = getNextID(); + AttributesImpl atts = addIDAttribute(attributes, id); + delegate.startReferencedNode(name, atts); + return new IFStructureTreeElement(id); + } + + private String getNextID() { + return Integer.toHexString(idCounter++); + } + + private AttributesImpl addIDAttribute(Attributes attributes, String id) { + AttributesImpl atts = new AttributesImpl(attributes); + atts.addAttribute(InternalElementMapping.URI, + InternalElementMapping.STRUCT_ID, + InternalElementMapping.STANDARD_PREFIX + ":" + InternalElementMapping.STRUCT_ID, + XMLUtil.CDATA, + id); + return atts; + } +} diff --git a/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java b/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java index 340b2e068..a2595d320 100644 --- a/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java +++ b/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java @@ -21,13 +21,15 @@ package org.apache.fop.render.intermediate.extensions; import org.apache.xmlgraphics.util.XMLizable; +import org.apache.fop.accessibility.StructureTreeElement; + /** * Abstract base class for document actions, like "go-to" actions with absolute page coordinates. */ public abstract class AbstractAction implements XMLizable { private String id; - private String structurePointer; + private StructureTreeElement structureTreeElement; /** * Sets an ID to make the action referencable. @@ -47,18 +49,18 @@ public abstract class AbstractAction implements XMLizable { /** * Sets the structure element corresponding to this action. - * @param structurePointer a reference to the structure element + * @param structureTreeElement a reference to the structure element */ - public void setStructurePointer(String structurePointer) { - this.structurePointer = structurePointer; + public void setStructureTreeElement(StructureTreeElement structureTreeElement) { + this.structureTreeElement = structureTreeElement; } /** * Returns the structure element corresponding to this action. * @return the reference to the structure element */ - public String getStructurePointer() { - return structurePointer; + public StructureTreeElement getStructureTreeElement() { + return structureTreeElement; } /** diff --git a/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java b/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java index 1e613d7eb..c174a74a9 100644 --- a/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java +++ b/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java @@ -21,6 +21,7 @@ package org.apache.fop.render.intermediate.extensions; import java.awt.Point; import java.awt.Rectangle; +import java.util.Map; import java.util.Stack; import org.xml.sax.Attributes; @@ -30,6 +31,8 @@ import org.xml.sax.helpers.DefaultHandler; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fop.accessibility.StructureTreeElement; +import org.apache.fop.fo.extensions.InternalElementMapping; import org.apache.fop.render.intermediate.IFDocumentNavigationHandler; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.util.XMLUtil; @@ -48,14 +51,20 @@ public class DocumentNavigationHandler extends DefaultHandler private IFDocumentNavigationHandler navHandler; - private String structurePointer; + private StructureTreeElement structureTreeElement; + + private Map<String, StructureTreeElement> structureTreeElements; /** * Main constructor. * @param navHandler the navigation handler that will receive the events + * @param structureTreeElements the elements representing the structure of the document */ - public DocumentNavigationHandler(IFDocumentNavigationHandler navHandler) { + public DocumentNavigationHandler(IFDocumentNavigationHandler navHandler, + Map<String, StructureTreeElement> structureTreeElements) { this.navHandler = navHandler; + assert structureTreeElements != null; + this.structureTreeElements = structureTreeElements; } /** {@inheritDoc} */ @@ -98,7 +107,8 @@ public class DocumentNavigationHandler extends DefaultHandler throw new SAXException(localName + " must be the root element!"); } Rectangle targetRect = XMLUtil.getAttributeAsRectangle(attributes, "rect"); - structurePointer = attributes.getValue("ptr"); + structureTreeElement = structureTreeElements.get(attributes.getValue( + InternalElementMapping.URI, InternalElementMapping.STRUCT_REF)); Link link = new Link(null, targetRect); objectStack.push(link); } else if (GOTO_XY.getLocalName().equals(localName)) { @@ -121,8 +131,8 @@ public class DocumentNavigationHandler extends DefaultHandler } action = new GoToXYAction(id, pageIndex, location); } - if (structurePointer != null) { - action.setStructurePointer(structurePointer); + if (structureTreeElement != null) { + action.setStructureTreeElement(structureTreeElement); } objectStack.push(action); } else if (GOTO_URI.getLocalName().equals(localName)) { @@ -134,8 +144,8 @@ public class DocumentNavigationHandler extends DefaultHandler if (id != null) { action.setID(id); } - if (structurePointer != null) { - action.setStructurePointer(structurePointer); + if (structureTreeElement != null) { + action.setStructureTreeElement(structureTreeElement); } objectStack.push(action); } else { diff --git a/src/java/org/apache/fop/render/intermediate/util/IFDocumentHandlerProxy.java b/src/java/org/apache/fop/render/intermediate/util/IFDocumentHandlerProxy.java index 3d89e812e..64c876fb1 100644 --- a/src/java/org/apache/fop/render/intermediate/util/IFDocumentHandlerProxy.java +++ b/src/java/org/apache/fop/render/intermediate/util/IFDocumentHandlerProxy.java @@ -20,9 +20,12 @@ package org.apache.fop.render.intermediate.util; import java.awt.Dimension; +import java.util.Locale; import javax.xml.transform.Result; +import org.apache.fop.accessibility.DummyStructureTreeEventHandler; +import org.apache.fop.accessibility.StructureTreeEventHandler; import org.apache.fop.fonts.FontInfo; import org.apache.fop.render.intermediate.IFContext; import org.apache.fop.render.intermediate.IFDocumentHandler; @@ -94,6 +97,11 @@ public class IFDocumentHandlerProxy implements IFDocumentHandler { } /** {@inheritDoc} */ + public StructureTreeEventHandler getStructureTreeEventHandler() { + return DummyStructureTreeEventHandler.INSTANCE; + } + + /** {@inheritDoc} */ public void setResult(Result result) throws IFException { this.delegate.setResult(result); } @@ -104,6 +112,12 @@ public class IFDocumentHandlerProxy implements IFDocumentHandler { } /** {@inheritDoc} */ + public void setDocumentLocale(Locale locale) { + this.delegate.setDocumentLocale(locale); + + } + + /** {@inheritDoc} */ public void startDocumentHeader() throws IFException { this.delegate.startDocumentHeader(); } @@ -184,4 +198,4 @@ public class IFDocumentHandlerProxy implements IFDocumentHandler { this.delegate.handleExtensionObject(extension); } -}
\ No newline at end of file +} diff --git a/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java b/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java index 26595bca1..9b76d0620 100644 --- a/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java +++ b/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java @@ -22,8 +22,6 @@ package org.apache.fop.render.pdf; import java.util.HashMap; import java.util.Map; -import org.w3c.dom.Node; - import org.apache.fop.events.EventBroadcaster; import org.apache.fop.pdf.PDFName; import org.apache.fop.pdf.PDFObject; @@ -37,9 +35,11 @@ final class FOToPDFRoleMap { /** * Standard structure types defined by the PDF Reference, Fourth Edition (PDF 1.5). */ - private static final Map STANDARD_STRUCTURE_TYPES = new HashMap(); + private static final Map<String, PDFName> STANDARD_STRUCTURE_TYPES + = new HashMap<String, PDFName>(); - private static final Map DEFAULT_MAPPINGS = new java.util.HashMap(); + private static final Map<String, Mapper> DEFAULT_MAPPINGS + = new java.util.HashMap<String, Mapper>(); private static final PDFName THEAD; private static final PDFName NON_STRUCT; @@ -172,7 +172,7 @@ final class FOToPDFRoleMap { * @return the structure type or null if no match could be found */ public static PDFName mapFormattingObject(String fo, PDFObject parent) { - Mapper mapper = (Mapper)DEFAULT_MAPPINGS.get(fo); + Mapper mapper = (Mapper) DEFAULT_MAPPINGS.get(fo); if (mapper != null) { return mapper.getStructureType(parent); } else { @@ -180,27 +180,32 @@ final class FOToPDFRoleMap { } } - public static PDFName mapFormattingObject(Node fo, PDFObject parent, - EventBroadcaster eventBroadcaster) { + /** + * Maps a Formatting Object to a PDFName representing the associated structure type. + * @param fo the formatting object's local name + * @param role the value of the formatting object's role property + * @param parent the parent of the structure element to be mapped + * @param eventBroadcaster the event broadcaster + * @return the structure type or null if no match could be found + */ + public static PDFName mapFormattingObject(String fo, String role, + PDFObject parent, EventBroadcaster eventBroadcaster) { PDFName type = null; - Node role = fo.getAttributes().getNamedItemNS(null, "role"); if (role == null) { - type = mapFormattingObject(fo.getLocalName(), parent); + type = mapFormattingObject(fo, parent); } else { - String customType = role.getNodeValue(); - type = (PDFName) STANDARD_STRUCTURE_TYPES.get(customType); + type = (PDFName) STANDARD_STRUCTURE_TYPES.get(role); if (type == null) { - String foName = fo.getLocalName(); - type = mapFormattingObject(foName, parent); + type = mapFormattingObject(fo, parent); PDFEventProducer.Provider.get(eventBroadcaster).nonStandardStructureType(fo, - foName, customType, type.toString().substring(1)); + fo, role, type.toString().substring(1)); } } assert type != null; return type; } - private static interface Mapper { + private interface Mapper { PDFName getStructureType(PDFObject parent); } @@ -222,7 +227,7 @@ final class FOToPDFRoleMap { public PDFName getStructureType(PDFObject parent) { PDFStructElem grandParent = (PDFStructElem) - ((PDFStructElem)parent).getParentStructElem(); + ((PDFStructElem) parent).getParentStructElem(); //TODO What to do with cells from table-footer? Currently they are mapped on TD. PDFName type; if (THEAD.equals(grandParent.getStructureType())) { diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java index c1d959dd1..5be8c3001 100644 --- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java @@ -25,15 +25,16 @@ import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; +import java.util.HashMap; +import java.util.Locale; import java.util.Map; -import org.w3c.dom.NodeList; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.xmp.Metadata; +import org.apache.fop.accessibility.StructureTreeEventHandler; import org.apache.fop.apps.MimeConstants; import org.apache.fop.fo.extensions.xmp.XMPMetadata; import org.apache.fop.pdf.PDFAnnotList; @@ -45,28 +46,26 @@ import org.apache.fop.render.extensions.prepress.PageBoundaries; import org.apache.fop.render.extensions.prepress.PageScale; import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler; import org.apache.fop.render.intermediate.IFContext; -import org.apache.fop.render.intermediate.IFDocumentHandler; import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; import org.apache.fop.render.intermediate.IFDocumentNavigationHandler; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFPainter; import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileExtensionAttachment; -import org.apache.fop.util.XMLUtil; /** - * {@link IFDocumentHandler} implementation that produces PDF. + * {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation that produces PDF. */ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { /** logging instance */ private static Log log = LogFactory.getLog(PDFDocumentHandler.class); - private int pageSequenceIndex; - private boolean accessEnabled; private PDFLogicalStructureHandler logicalStructureHandler; + private PDFStructureTreeBuilder structureTreeBuilder; + /** the PDF Document being created */ protected PDFDocument pdfDoc; @@ -92,8 +91,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { protected PageReference currentPageRef; /** Used for bookmarks/outlines. */ - protected Map<Integer, PageReference> pageReferences - = new java.util.HashMap<Integer, PageReference>(); + protected Map<Integer, PageReference> pageReferences = new HashMap<Integer, PageReference>(); private final PDFDocumentNavigationHandler documentNavigationHandler = new PDFDocumentNavigationHandler(this); @@ -145,15 +143,23 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { this.pdfDoc = pdfUtil.setupPDFDocument(this.outputStream); this.accessEnabled = getUserAgent().isAccessibilityEnabled(); if (accessEnabled) { - pdfDoc.getRoot().makeTagged(); - logicalStructureHandler = new PDFLogicalStructureHandler(pdfDoc, - getUserAgent().getEventBroadcaster()); + setupAccessibility(); } } catch (IOException e) { throw new IFException("I/O error in startDocument()", e); } } + private void setupAccessibility() { + pdfDoc.getRoot().makeTagged(); + logicalStructureHandler = new PDFLogicalStructureHandler(pdfDoc); + // TODO this is ugly. All the necessary information should be available + // at creation time in order to enforce immutability + structureTreeBuilder.setPdfFactory(pdfDoc.getFactory()); + structureTreeBuilder.setLogicalStructureHandler(logicalStructureHandler); + structureTreeBuilder.setEventBroadcaster(getUserAgent().getEventBroadcaster()); + } + /** {@inheritDoc} */ public void endDocumentHeader() throws IFException { pdfUtil.generateDefaultXMPMetadata(); @@ -178,18 +184,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { /** {@inheritDoc} */ public void startPageSequence(String id) throws IFException { - //TODO page sequence title - - if (this.pdfDoc.getRoot().getLanguage() == null - && getContext().getLanguage() != null) { - //No document-level language set, so we use the first page-sequence's language - this.pdfDoc.getRoot().setLanguage(XMLUtil.toRFC3066(getContext().getLanguage())); - } - - if (accessEnabled) { - NodeList nodes = getUserAgent().getStructureTree().getPageSequence(pageSequenceIndex++); - logicalStructureHandler.processStructureTree(nodes, getContext().getLanguage()); - } + //nop } /** {@inheritDoc} */ @@ -289,9 +284,9 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { /** {@inheritDoc} */ public void handleExtensionObject(Object extension) throws IFException { if (extension instanceof XMPMetadata) { - pdfUtil.renderXMPMetadata((XMPMetadata)extension); + pdfUtil.renderXMPMetadata((XMPMetadata) extension); } else if (extension instanceof Metadata) { - XMPMetadata wrapper = new XMPMetadata(((Metadata)extension)); + XMPMetadata wrapper = new XMPMetadata(((Metadata) extension)); pdfUtil.renderXMPMetadata(wrapper); } else if (extension instanceof PDFEmbeddedFileExtensionAttachment) { PDFEmbeddedFileExtensionAttachment embeddedFile @@ -307,6 +302,11 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { } } + /** {@inheritDoc} */ + public void setDocumentLocale(Locale locale) { + pdfDoc.getRoot().setLanguage(locale); + } + PageReference getPageReference(int pageIndex) { return this.pageReferences.get(Integer.valueOf(pageIndex)); } @@ -332,4 +332,11 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { } } + @Override + public StructureTreeEventHandler getStructureTreeEventHandler() { + if (structureTreeBuilder == null) { + structureTreeBuilder = new PDFStructureTreeBuilder(); + } + return structureTreeBuilder; + } } diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandlerMaker.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandlerMaker.java index f4ba03d5b..1186db3ae 100644 --- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandlerMaker.java +++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandlerMaker.java @@ -36,6 +36,9 @@ public class PDFDocumentHandlerMaker extends AbstractIFDocumentHandlerMaker { public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) { PDFDocumentHandler handler = new PDFDocumentHandler(); handler.setContext(new IFContext(ua)); + if (ua.isAccessibilityEnabled()) { + ua.setStructureTreeEventHandler(handler.getStructureTreeEventHandler()); + } return handler; } diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java index c8fa481d4..2236778b5 100644 --- a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java @@ -31,6 +31,7 @@ import org.apache.fop.pdf.PDFFactory; import org.apache.fop.pdf.PDFGoTo; import org.apache.fop.pdf.PDFLink; import org.apache.fop.pdf.PDFOutline; +import org.apache.fop.pdf.PDFStructElem; import org.apache.fop.render.intermediate.IFDocumentNavigationHandler; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.extensions.AbstractAction; @@ -111,10 +112,9 @@ public class PDFDocumentNavigationHandler implements IFDocumentNavigationHandler PDFLink pdfLink = getPDFDoc().getFactory().makeLink( targetRect2D, pdfAction); if (pdfLink != null) { - String ptr = link.getAction().getStructurePointer(); - if (documentHandler.getUserAgent().isAccessibilityEnabled() - && ptr != null && ptr.length() > 0) { - documentHandler.getLogicalStructureHandler().addLinkContentItem(pdfLink, ptr); + PDFStructElem structure = (PDFStructElem) link.getAction().getStructureTreeElement(); + if (documentHandler.getUserAgent().isAccessibilityEnabled() && structure != null) { + documentHandler.getLogicalStructureHandler().addLinkContentItem(pdfLink, structure); } documentHandler.currentPage.addAnnotation(pdfLink); } diff --git a/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java b/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java index 4fd66109a..88a6e9c22 100644 --- a/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java @@ -19,16 +19,8 @@ package org.apache.fop.render.pdf; -import java.util.HashMap; import java.util.Locale; -import java.util.Map; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import org.apache.fop.events.EventBroadcaster; -import org.apache.fop.fo.extensions.ExtensionElementMapping; -import org.apache.fop.fo.extensions.InternalElementMapping; import org.apache.fop.pdf.PDFArray; import org.apache.fop.pdf.PDFDictionary; import org.apache.fop.pdf.PDFDocument; @@ -53,13 +45,6 @@ class PDFLogicalStructureHandler { private final PDFDocument pdfDoc; - private final EventBroadcaster eventBroadcaster; - - /** - * Map of references to the corresponding structure elements. - */ - private final Map structTreeMap = new HashMap(); - private final PDFParentTree parentTree = new PDFParentTree(); private int parentTreeKey; @@ -108,23 +93,16 @@ class PDFLogicalStructureHandler { * * @param pdfDoc a document */ - PDFLogicalStructureHandler(PDFDocument pdfDoc, EventBroadcaster eventBroadcaster) { + PDFLogicalStructureHandler(PDFDocument pdfDoc) { this.pdfDoc = pdfDoc; - this.eventBroadcaster = eventBroadcaster; PDFStructTreeRoot structTreeRoot = pdfDoc.getFactory().makeStructTreeRoot(parentTree); rootStructureElement = pdfDoc.getFactory().makeStructureElement( FOToPDFRoleMap.mapFormattingObject("root", structTreeRoot), structTreeRoot); structTreeRoot.addKid(rootStructureElement); } - /** - * Converts the given structure tree into PDF. - * - * @param structureTree the structure tree of the current page sequence - * @param language language set on the page sequence - */ - void processStructureTree(NodeList structureTree, Locale language) { - pdfDoc.enforceLanguageOnRoot(); + + PDFStructElem createPageSequence(Locale language) { PDFStructElem structElemPart = pdfDoc.getFactory().makeStructureElement( FOToPDFRoleMap.mapFormattingObject("page-sequence", rootStructureElement), rootStructureElement); @@ -132,49 +110,7 @@ class PDFLogicalStructureHandler { if (language != null) { structElemPart.setLanguage(language); } - - for (int i = 0, n = structureTree.getLength(); i < n; i++) { - Node node = structureTree.item(i); - assert node.getLocalName().equals("flow") - || node.getLocalName().equals("static-content"); - PDFStructElem structElemSect = pdfDoc.getFactory().makeStructureElement( - FOToPDFRoleMap.mapFormattingObject(node.getLocalName(), structElemPart), - structElemPart); - structElemPart.addKid(structElemSect); - NodeList childNodes = node.getChildNodes(); - for (int j = 0, m = childNodes.getLength(); j < m; j++) { - processNode(childNodes.item(j), structElemSect, true); - } - } - } - - private void processNode(Node node, PDFStructElem parent, boolean addKid) { - Node attr = node.getAttributes().getNamedItemNS(InternalElementMapping.URI, "ptr"); - assert attr != null; - String ptr = attr.getNodeValue(); - PDFStructElem structElem = pdfDoc.getFactory().makeStructureElement( - FOToPDFRoleMap.mapFormattingObject(node, parent, eventBroadcaster), parent); - // TODO necessary? If a page-sequence is empty (e.g., contains a single - // empty fo:block), should the block still be added to the structure - // tree? This is not being done for descendant empty elements... - if (addKid) { - parent.addKid(structElem); - } - String nodeName = node.getLocalName(); - if (nodeName.equals("external-graphic") || nodeName.equals("instream-foreign-object")) { - Node altTextNode = node.getAttributes().getNamedItemNS( - ExtensionElementMapping.URI, "alt-text"); - if (altTextNode != null) { - structElem.put("Alt", altTextNode.getNodeValue()); - } else { - structElem.put("Alt", "No alternate text specified"); - } - } - structTreeMap.put(ptr, structElem); - NodeList nodes = node.getChildNodes(); - for (int i = 0, n = nodes.getLength(); i < n; i++) { - processNode(nodes.item(i), structElem, false); - } + return structElemPart; } private int getNextParentTreeKey() { @@ -208,96 +144,79 @@ class PDFLogicalStructureHandler { parentTree.getNums().put(currentPage.getStructParents(), pageParentTreeArray); } - private MarkedContentInfo addToParentTree(String structurePointer) { - PDFStructElem parent = (PDFStructElem) structTreeMap.get(structurePointer); - if (parent == null) { - return ARTIFACT; - } else { - pageParentTreeArray.add(parent); - String type = parent.getStructureType().toString(); - int mcid = pageParentTreeArray.length() - 1; - return new MarkedContentInfo(type, mcid, parent); - } + private MarkedContentInfo addToParentTree(PDFStructElem structureTreeElement) { + PDFStructElem parent = (structureTreeElement instanceof PDFStructElem.Placeholder) + ? structureTreeElement.getParentStructElem() + : structureTreeElement; + pageParentTreeArray.add(parent); + String type = parent.getStructureType().toString(); + int mcid = pageParentTreeArray.length() - 1; + return new MarkedContentInfo(type, mcid, structureTreeElement); } /** * Adds a content item corresponding to text into the structure tree, if * there is a structure element associated to it. * - * @param structurePointer reference to the parent structure element of the - * piece of text + * @param structElem the parent structure element of the piece of text * @return the necessary information for bracketing the content as a * marked-content sequence. If there is no element in the structure tree * associated to that content, returns an instance whose * {@link MarkedContentInfo#tag} value is <code>null</code>. The content * must then be treated as an artifact. */ - MarkedContentInfo addTextContentItem(String structurePointer) { - MarkedContentInfo mci = addToParentTree(structurePointer); - if (mci != ARTIFACT) { + MarkedContentInfo addTextContentItem(PDFStructElem structElem) { + if (structElem == null) { + return ARTIFACT; + } else { + MarkedContentInfo mci = addToParentTree(structElem); PDFDictionary contentItem = new PDFDictionary(); contentItem.put("Type", MCR); contentItem.put("Pg", this.currentPage); contentItem.put("MCID", mci.mcid); mci.parent.addKid(contentItem); + return mci; } - return mci; } /** * Adds a content item corresponding to an image into the structure tree, if * there is a structure element associated to it. * - * @param structurePointer reference to the parent structure element of the - * image + * @param structElem the parent structure element of the image * @return the necessary information for bracketing the content as a * marked-content sequence. If there is no element in the structure tree * associated to that image, returns an instance whose - * {@link MarkedContentInfo#tag} value is <code>null</code>. The image - * must then be treated as an artifact. + * {@link MarkedContentInfo#tag} value is <code>null</code>. The image must + * then be treated as an artifact. */ - MarkedContentInfo addImageContentItem(String structurePointer) { - MarkedContentInfo mci = addToParentTree(structurePointer); - if (mci != ARTIFACT) { + MarkedContentInfo addImageContentItem(PDFStructElem structElem) { + if (structElem == null) { + return ARTIFACT; + } else { + MarkedContentInfo mci = addToParentTree(structElem); mci.parent.setMCIDKid(mci.mcid); mci.parent.setPage(this.currentPage); + return mci; } - return mci; } - // While the PDF spec allows images to be referred as PDF objects, this - // makes the Acrobat Pro checker complain that the image is not accessible. - // Its alt-text is still read aloud though. Using marked-content sequences - // like for text works. -// MarkedContentInfo addImageObject(String parentReference) { -// MarkedContentInfo mci = addToParentTree(parentReference); -// if (mci != ARTIFACT) { -// PDFDictionary contentItem = new PDFDictionary(); -// contentItem.put("Type", OBJR); -// contentItem.put("Pg", this.currentPage); -// contentItem.put("Obj", null); -// mci.parent.addKid(contentItem); -// } -// return mci; -// } - /** * Adds a content item corresponding to the given link into the structure * tree. * * @param link a link - * @param structurePointer reference to the corresponding parent structure element + * @param structureTreeElement its parent structure element */ - void addLinkContentItem(PDFLink link, String structurePointer) { + void addLinkContentItem(PDFLink link, PDFStructElem structureTreeElement) { int structParent = getNextParentTreeKey(); link.setStructParent(structParent); PDFDictionary contentItem = new PDFDictionary(); contentItem.put("Type", OBJR); contentItem.put("Pg", this.currentPage); contentItem.put("Obj", link); - PDFStructElem parent = (PDFStructElem) structTreeMap.get(structurePointer); - parentTree.getNums().put(structParent, parent); - parent.addKid(contentItem); + parentTree.getNums().put(structParent, structureTreeElement); + structureTreeElement.addKid(contentItem); } } diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java index 11af216a2..f2fbfd014 100644 --- a/src/java/org/apache/fop/render/pdf/PDFPainter.java +++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java @@ -37,6 +37,7 @@ import org.apache.fop.fonts.SingleByteFont; import org.apache.fop.fonts.Typeface; import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFNumber; +import org.apache.fop.pdf.PDFStructElem; import org.apache.fop.pdf.PDFTextUtil; import org.apache.fop.pdf.PDFXObject; import org.apache.fop.render.RenderingContext; @@ -133,24 +134,24 @@ public class PDFPainter extends AbstractIFPainter { PDFXObject xobject = getPDFDoc().getXObject(uri); if (xobject != null) { if (accessEnabled) { - String ptr = getContext().getStructurePointer(); - prepareImageMCID(ptr); + PDFStructElem structElem = (PDFStructElem) getContext().getStructureTreeElement(); + prepareImageMCID(structElem); placeImageAccess(rect, xobject); } else { placeImage(rect, xobject); } } else { if (accessEnabled) { - String ptr = getContext().getStructurePointer(); - prepareImageMCID(ptr); + PDFStructElem structElem = (PDFStructElem) getContext().getStructureTreeElement(); + prepareImageMCID(structElem); } drawImageUsingURI(uri, rect); flushPDFDoc(); } } - private void prepareImageMCID(String ptr) { - imageMCI = logicalStructureHandler.addImageContentItem(ptr); + private void prepareImageMCID(PDFStructElem structElem) { + imageMCI = logicalStructureHandler.addImageContentItem(structElem); } /** {@inheritDoc} */ @@ -194,8 +195,8 @@ public class PDFPainter extends AbstractIFPainter { /** {@inheritDoc} */ public void drawImage(Document doc, Rectangle rect) throws IFException { if (accessEnabled) { - String ptr = getContext().getStructurePointer(); - prepareImageMCID(ptr); + PDFStructElem structElem = (PDFStructElem) getContext().getStructureTreeElement(); + prepareImageMCID(structElem); } drawImageUsingDocument(doc, rect); flushPDFDoc(); @@ -294,8 +295,8 @@ public class PDFPainter extends AbstractIFPainter { String text) throws IFException { if (accessEnabled) { - String ptr = getContext().getStructurePointer(); - MarkedContentInfo mci = logicalStructureHandler.addTextContentItem(ptr); + PDFStructElem structElem = (PDFStructElem) getContext().getStructureTreeElement(); + MarkedContentInfo mci = logicalStructureHandler.addTextContentItem(structElem); if (generator.getTextUtil().isInTextObject()) { generator.separateTextElements(mci.tag, mci.mcid); } diff --git a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java new file mode 100644 index 000000000..11eba4ea4 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java @@ -0,0 +1,116 @@ +/* + * 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.render.pdf; + +import java.util.LinkedList; +import java.util.Locale; + +import org.xml.sax.Attributes; + +import org.apache.fop.accessibility.StructureTreeElement; +import org.apache.fop.accessibility.StructureTreeEventHandler; +import org.apache.fop.events.EventBroadcaster; +import org.apache.fop.fo.extensions.ExtensionElementMapping; +import org.apache.fop.pdf.PDFFactory; +import org.apache.fop.pdf.PDFStructElem; + +class PDFStructureTreeBuilder implements StructureTreeEventHandler { + + private PDFFactory pdfFactory; + + private PDFLogicalStructureHandler logicalStructureHandler; + + private EventBroadcaster eventBroadcaster; + + private LinkedList<PDFStructElem> ancestors = new LinkedList<PDFStructElem>(); + + void setPdfFactory(PDFFactory pdfFactory) { + this.pdfFactory = pdfFactory; + } + + void setLogicalStructureHandler(PDFLogicalStructureHandler logicalStructureHandler) { + this.logicalStructureHandler = logicalStructureHandler; + } + + void setEventBroadcaster(EventBroadcaster eventBroadcaster) { + this.eventBroadcaster = eventBroadcaster; + } + + public void startPageSequence(Locale locale) { + ancestors = new LinkedList<PDFStructElem>(); + ancestors.add(logicalStructureHandler.createPageSequence(locale)); + } + + public void endPageSequence() { + } + + public StructureTreeElement startNode(String name, Attributes attributes) { + PDFStructElem parent = ancestors.getFirst(); + String role = attributes.getValue("role"); + PDFStructElem created; + created = pdfFactory.makeStructureElement( + FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster), parent); + parent.addKid(created); + ancestors.addFirst(created); + return created; + } + + public void endNode(String name) { + removeFirstAncestor(); + } + + private void removeFirstAncestor() { + ancestors.removeFirst(); + } + + public StructureTreeElement startImageNode(String name, Attributes attributes) { + PDFStructElem parent = ancestors.getFirst(); + String role = attributes.getValue("role"); + PDFStructElem created; + created = pdfFactory.makeStructureElement( + FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster), parent); + parent.addKid(created); + String altTextNode = attributes.getValue(ExtensionElementMapping.URI, "alt-text"); + if (altTextNode != null) { + created.put("Alt", altTextNode); + } else { + created.put("Alt", "No alternate text specified"); + } + ancestors.addFirst(created); + return created; + } + + public StructureTreeElement startReferencedNode(String name, Attributes attributes) { + PDFStructElem parent = ancestors.getFirst(); + String role = attributes.getValue("role"); + PDFStructElem created; + if ("#PCDATA".equals(name)) { + created = new PDFStructElem.Placeholder(parent, name); + } else { + created = pdfFactory.makeStructureElement( + FOToPDFRoleMap.mapFormattingObject(name, role, parent, + eventBroadcaster), parent); + } + parent.addKid(created); + ancestors.addFirst(created); + return created; + } + +} diff --git a/src/java/org/apache/fop/render/rtf/RTFHandler.java b/src/java/org/apache/fop/render/rtf/RTFHandler.java index 9abde32ce..22c0c8b76 100644 --- a/src/java/org/apache/fop/render/rtf/RTFHandler.java +++ b/src/java/org/apache/fop/render/rtf/RTFHandler.java @@ -937,10 +937,10 @@ public class RTFHandler extends FOEventHandler { */ //TODO: do this only, if the labels content <> previous labels content if (list.getChildCount() > 0) { - this.endListBody(); + this.endListBody(null); this.endList((ListBlock) li.getParent()); this.startList((ListBlock) li.getParent()); - this.startListBody(); + this.startListBody(null); list = (RtfList)builderContext.getContainer( RtfList.class, true, this); @@ -965,7 +965,7 @@ public class RTFHandler extends FOEventHandler { } /** {@inheritDoc} */ - public void startListLabel() { + public void startListLabel(ListItemLabel listItemLabel) { if (bDefer) { return; } @@ -985,7 +985,7 @@ public class RTFHandler extends FOEventHandler { } /** {@inheritDoc} */ - public void endListLabel() { + public void endListLabel(ListItemLabel listItemLabel) { if (bDefer) { return; } @@ -994,20 +994,20 @@ public class RTFHandler extends FOEventHandler { } /** {@inheritDoc} */ - public void startListBody() { + public void startListBody(ListItemBody listItemBody) { } /** {@inheritDoc} */ - public void endListBody() { + public void endListBody(ListItemBody listItemBody) { } // Static Regions /** {@inheritDoc} */ - public void startStatic() { + public void startStatic(StaticContent staticContent) { } /** {@inheritDoc} */ - public void endStatic() { + public void endStatic(StaticContent statisContent) { } /** {@inheritDoc} */ @@ -1050,7 +1050,7 @@ public class RTFHandler extends FOEventHandler { } /** {@inheritDoc} */ - public void endLink() { + public void endLink(BasicLink basicLink) { if (bDefer) { return; } @@ -1090,7 +1090,7 @@ public class RTFHandler extends FOEventHandler { } /** {@inheritDoc} */ - public void foreignObject(InstreamForeignObject ifo) { + public void endInstreamForeignObject(InstreamForeignObject ifo) { if (bDefer) { return; } @@ -1337,7 +1337,7 @@ public class RTFHandler extends FOEventHandler { } /** {@inheritDoc} */ - public void leader(Leader l) { + public void startLeader(Leader l) { if (bDefer) { return; } @@ -1353,8 +1353,10 @@ public class RTFHandler extends FOEventHandler { RtfTextrun textrun = container.getTextrun(); textrun.addLeader(rtfAttr); - - } catch (Exception e) { + } catch (IOException e) { + log.error("startLeader: " + e.getMessage()); + throw new RuntimeException(e.getMessage()); + } catch (FOPException e) { log.error("startLeader: " + e.getMessage()); throw new RuntimeException(e.getMessage()); } @@ -1362,11 +1364,9 @@ public class RTFHandler extends FOEventHandler { /** * @param text FOText object - * @param data Array of characters to process. - * @param start Offset for characters to process. - * @param length Portion of array to process. + * @param characters CharSequence of the characters to process. */ - public void text(FOText text, char[] data, int start, int length) { + public void text(FOText text, CharSequence characters) { if (bDefer) { return; } @@ -1381,7 +1381,7 @@ public class RTFHandler extends FOEventHandler { = TextAttributesConverter.convertCharacterAttributes(text); textrun.pushInlineAttributes(rtfAttr); - textrun.addString(new String(data, start, length - start)); + textrun.addString(characters.toString()); textrun.popInlineAttributes(); } catch (IOException ioe) { handleIOTrouble(ioe); @@ -1518,9 +1518,9 @@ public class RTFHandler extends FOEventHandler { } } else if (foNode instanceof StaticContent) { if (bStart) { - startStatic(); + startStatic(null); } else { - endStatic(); + endStatic(null); } } else if (foNode instanceof ExternalGraphic) { if (bStart) { @@ -1528,7 +1528,7 @@ public class RTFHandler extends FOEventHandler { } } else if (foNode instanceof InstreamForeignObject) { if (bStart) { - foreignObject( (InstreamForeignObject) foNode ); + endInstreamForeignObject( (InstreamForeignObject) foNode ); } } else if (foNode instanceof Block) { if (bStart) { @@ -1547,7 +1547,7 @@ public class RTFHandler extends FOEventHandler { if (bStart) { startLink( (BasicLink) foNode); } else { - endLink(); + endLink(null); } } else if (foNode instanceof Inline) { if (bStart) { @@ -1558,7 +1558,7 @@ public class RTFHandler extends FOEventHandler { } else if (foNode instanceof FOText) { if (bStart) { FOText text = (FOText) foNode; - text(text, text.getCharArray(), 0, text.length()); + text(text, text.getCharSequence()); } } else if (foNode instanceof Character) { if (bStart) { @@ -1591,9 +1591,9 @@ public class RTFHandler extends FOEventHandler { } } else if (foNode instanceof ListItemBody) { if (bStart) { - startListBody(); + startListBody(null); } else { - endListBody(); + endListBody(null); } } else if (foNode instanceof ListItem) { if (bStart) { @@ -1603,9 +1603,9 @@ public class RTFHandler extends FOEventHandler { } } else if (foNode instanceof ListItemLabel) { if (bStart) { - startListLabel(); + startListLabel(null); } else { - endListLabel(); + endListLabel(null); } } else if (foNode instanceof Table) { if (bStart) { @@ -1651,7 +1651,7 @@ public class RTFHandler extends FOEventHandler { } } else if (foNode instanceof Leader) { if (bStart) { - leader((Leader) foNode); + startLeader((Leader) foNode); } } else if (foNode instanceof PageNumberCitation) { if (bStart) { diff --git a/src/java/org/apache/fop/render/xml/XMLRenderer.java b/src/java/org/apache/fop/render/xml/XMLRenderer.java index d2cd55abe..504133c09 100644 --- a/src/java/org/apache/fop/render/xml/XMLRenderer.java +++ b/src/java/org/apache/fop/render/xml/XMLRenderer.java @@ -35,8 +35,6 @@ import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.apache.xmlgraphics.util.QName; @@ -82,15 +80,12 @@ import org.apache.fop.area.inline.TextArea; import org.apache.fop.area.inline.WordArea; import org.apache.fop.fo.Constants; import org.apache.fop.fo.extensions.ExtensionAttachment; -import org.apache.fop.fo.extensions.ExtensionElementMapping; -import org.apache.fop.fo.extensions.InternalElementMapping; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; import org.apache.fop.render.Renderer; import org.apache.fop.render.RendererContext; import org.apache.fop.render.XMLHandler; import org.apache.fop.util.ColorUtil; -import org.apache.fop.util.DOM2SAX; /** * Renderer that renders areas to XML for debugging purposes. @@ -110,8 +105,6 @@ public class XMLRenderer extends AbstractXMLRenderer { /** If not null, the XMLRenderer will mimic another renderer by using its font setup. */ protected Renderer mimic; - private int pageSequenceIndex; - /** * @param userAgent the user agent that contains configuration details. This cannot be null. */ @@ -452,29 +445,6 @@ public class XMLRenderer extends AbstractXMLRenderer { } transferForeignObjects(pageSequence); startElement("pageSequence", atts); - if (this.getUserAgent().isAccessibilityEnabled()) { - String structureTreeElement = "structureTree"; - startElement(structureTreeElement); - try { - this.handler.startPrefixMapping("foi", InternalElementMapping.URI); - this.handler.startPrefixMapping("fox", ExtensionElementMapping.URI); - NodeList nodes = getUserAgent().getStructureTree().getPageSequence( - pageSequenceIndex++); - for (int i = 0, n = nodes.getLength(); i < n; i++) { - Node node = nodes.item(i); - try { - new DOM2SAX(handler).writeFragment(node); - } catch (SAXException e) { - handleSAXException(e); - } - } - this.handler.endPrefixMapping("fox"); - this.handler.endPrefixMapping("foi"); - } catch (SAXException se) { - handleSAXException(se); - } - endElement(structureTreeElement); - } handleExtensionAttachments(pageSequence.getExtensionAttachments()); LineArea seqTitle = pageSequence.getTitle(); if (seqTitle != null) { diff --git a/src/java/org/apache/fop/util/LanguageTags.java b/src/java/org/apache/fop/util/LanguageTags.java new file mode 100644 index 000000000..662fab908 --- /dev/null +++ b/src/java/org/apache/fop/util/LanguageTags.java @@ -0,0 +1,66 @@ +/* + * 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.util; + +import java.util.Locale; + +/** + * Provides utility methods for manipulating language tags compliant with the + * RFC 3066 specification available at http://www.ietf.org/rfc/rfc3066.txt. A + * typical language tag is a 2-letter language code sometimes followed by a country + * code. For example: en, en-US. + */ +public final class LanguageTags { + + private LanguageTags() { + } + + /** + * Converts the given locale to an RFC 3066 compliant language tag. + * + * @param locale a locale + * @return the corresponding language tag + */ + public static String toLanguageTag(Locale locale) { + StringBuffer sb = new StringBuffer(5); + sb.append(locale.getLanguage()); + String country = locale.getCountry(); + if (country.length() > 0) { + sb.append('-'); + sb.append(country); + } + return sb.toString(); + } + + /** + * Converts an RFC 3066 compliant language tag to a locale. + * + * @param languageTag language tag to convert + * @return the corresponding locale + */ + public static Locale toLocale(String languageTag) { + String[] parts = languageTag.split("-"); + if (parts.length == 1) { + return new Locale(parts[0]); + } else { + return new Locale(parts[0], parts[1]); + } + } +} diff --git a/src/java/org/apache/fop/util/XMLUtil.java b/src/java/org/apache/fop/util/XMLUtil.java index 0a55ce573..d4397c2c7 100644 --- a/src/java/org/apache/fop/util/XMLUtil.java +++ b/src/java/org/apache/fop/util/XMLUtil.java @@ -21,7 +21,6 @@ package org.apache.fop.util; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; -import java.util.Locale; import org.xml.sax.Attributes; import org.xml.sax.SAXException; @@ -174,39 +173,4 @@ public final class XMLUtil implements XMLConstants { atts.addAttribute("", localName, localName, XMLUtil.CDATA, value); } - /** - * Converts a {@link Locale} instance to an RFC 3066 compliant language identifier. - * @param language the language - * @return the formatted language identifier - */ - public static String toRFC3066(Locale language) { - if (language == null || language.getLanguage().length() == 0) { - return null; - } - StringBuffer sb = new StringBuffer(); - sb.append(language.getLanguage()); - if (language.getCountry().length() > 0) { - sb.append('-'); - sb.append(language.getCountry()); - } - return sb.toString(); - } - - /** - * Converts an RFC 3066 compliant language identifier to a {@link Locale} instance. - * @param lang the language string - * @return the converted locale instance - */ - public static Locale convertRFC3066ToLocale(String lang) { - if (lang == null || lang.length() == 0) { - return null; - } - String[] parts = lang.split("-"); - if (parts.length == 1) { - return new Locale(parts[0]); - } else { - return new Locale(parts[0], parts[1]); - } - } - } diff --git a/status.xml b/status.xml index b395abe1e..a5d800c61 100644 --- a/status.xml +++ b/status.xml @@ -61,6 +61,9 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> <release version="FOP Trunk" date="TBD"> + <action context="Code" dev="PH" type="add" due-to="VH and PH"> + Improvements of the Accessibility feature + </action> <action context="Code" dev="CB" type="add" fixes-bug="52416" due-to="Luis Bernardo"> Suppress unnecessary "font not found warnings" when generating AFP with raster fonts </action> diff --git a/test/accessibility/README b/test/accessibility/README index 391d39055..1d2e04361 100644 --- a/test/accessibility/README +++ b/test/accessibility/README @@ -1,11 +1,8 @@ This directory contains sample FO files for testing the accessibility features of FOP. -To every FO file in this directory correspond two PDF files in the pdf/ -sub-directory: one generated by the painter, one by the renderer. For example, -the text_1.fo file has been rendered into pdf/text_1_painter_orig.pdf and -pdf/text_1_renderer_orig.pdf. The configuration file config-painter.xconf (resp. -config-renderer.xconf) was used. +Every FO file in this directory has a corresponding PDF file in the pdf/ +sub-directory. The fop.xconf configuration file was used. The PDF files have been checked with Adobe Acrobat Professional 9, using both the full accessibility checker and the read-aloud feature. The checker reports @@ -22,7 +19,7 @@ problem yet part or all of the document cannot be read aloud.) The purpose of this infrastructure is to be able to quickly re-test the accessibility processing chain when any change has been made to it. The -configuration files disable the compression of the PDF streams, so it is +configuration file disables the compression of the PDF streams, so it is possible to compare a re-generated PDF with the original one by using a simple diff tool. The files will not be identical because of the different creation dates (and the ID key in the trailer), but apart from that there should be no @@ -32,21 +29,13 @@ The rationale is that using a diff tool is much quicker and less tedious than running Acrobat's accessibility checker and read-aloud feature every time. -To re-generate the PDF files using the painter: - ../../fop -c config-painter.xconf text_1.fo pdf/text_1_painter.pdf - diff pdf/text_1_painter_orig.pdf pdf/text_1_painter.pdf +To re-generate the PDF files: + ../../fop -c fop.xconf text_1.fo pdf/text_1.new.pdf + diff pdf/text_1_painter.pdf pdf/text_1.new.pdf Or, going through the intermediate format: - ../../fop -c config-painter.xconf text_1.fo -if application/pdf text_1_if.xml - ../../fop -c config-painter.xconf -ifin text_1_if.xml pdf/text_1_painter.pdf - diff pdf/text_1_painter_orig.pdf pdf/text_1_painter.pdf - -To re-generate the PDF files using the legacy renderer: - ../../fop -c config-renderer.xconf text_1.fo pdf/text_1_renderer.pdf - diff pdf/text_1_renderer_orig.pdf pdf/text_1_renderer.pdf -Or, going through the intermediate format: - ../../fop -c config-renderer.xconf text_1.fo -at application/pdf text_1_at.xml - ../../fop -c config-renderer.xconf -atin text_1_at.xml pdf/text_1_renderer.pdf - diff pdf/text_1_renderer_orig.pdf pdf/text_1_renderer.pdf + ../../fop -c fop.xconf text_1.fo -if application/pdf text_1_if.xml + ../../fop -c fop.xconf -ifin text_1_if.xml pdf/text_1.new.pdf + diff pdf/text_1.pdf pdf/text_1.new.pdf $Id$ diff --git a/test/accessibility/background-image_jpg_repeat.fo b/test/accessibility/background-image_jpg_repeat.fo index 2c8f7f66d..727162e49 100644 --- a/test/accessibility/background-image_jpg_repeat.fo +++ b/test/accessibility/background-image_jpg_repeat.fo @@ -16,14 +16,14 @@ limitations under the License. --> <!-- $Id$ --> -<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> <fo:region-body background-image="../resources/images/bgimg72dpi.jpg"/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java application diff --git a/test/accessibility/background-image_jpg_single.fo b/test/accessibility/background-image_jpg_single.fo index 2250c24e2..335353e03 100644 --- a/test/accessibility/background-image_jpg_single.fo +++ b/test/accessibility/background-image_jpg_single.fo @@ -16,7 +16,7 @@ limitations under the License. --> <!-- $Id$ --> -<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> @@ -25,7 +25,7 @@ background-position-vertical="50%"/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java application diff --git a/test/accessibility/background-image_png_repeat.fo b/test/accessibility/background-image_png_repeat.fo index 7506c0f2b..5e4a8ba0d 100644 --- a/test/accessibility/background-image_png_repeat.fo +++ b/test/accessibility/background-image_png_repeat.fo @@ -16,14 +16,14 @@ limitations under the License. --> <!-- $Id$ --> -<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> <fo:region-body background-image="../resources/images/bgimg72dpi.png"/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java application diff --git a/test/accessibility/background-image_png_single.fo b/test/accessibility/background-image_png_single.fo index 1f60e9411..90067ec53 100644 --- a/test/accessibility/background-image_png_single.fo +++ b/test/accessibility/background-image_png_single.fo @@ -16,7 +16,7 @@ limitations under the License. --> <!-- $Id$ --> -<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> @@ -25,7 +25,7 @@ background-position-vertical="50%"/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java application diff --git a/test/accessibility/background-image_svg_repeat.fo b/test/accessibility/background-image_svg_repeat.fo index 6e71409d0..02520b6cf 100644 --- a/test/accessibility/background-image_svg_repeat.fo +++ b/test/accessibility/background-image_svg_repeat.fo @@ -16,14 +16,14 @@ limitations under the License. --> <!-- $Id$ --> -<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> <fo:region-body background-image="../resources/images/rgb-circles.svg"/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java application diff --git a/test/accessibility/background-image_svg_single.fo b/test/accessibility/background-image_svg_single.fo index 9dc67c86c..3029f32ec 100644 --- a/test/accessibility/background-image_svg_single.fo +++ b/test/accessibility/background-image_svg_single.fo @@ -16,7 +16,7 @@ limitations under the License. --> <!-- $Id$ --> -<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> @@ -25,7 +25,7 @@ background-position-vertical="50%"/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java application diff --git a/test/accessibility/complete.fo b/test/accessibility/complete.fo index 81df372b5..75684750c 100644 --- a/test/accessibility/complete.fo +++ b/test/accessibility/complete.fo @@ -17,7 +17,7 @@ --> <!-- $Id$ --> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" - xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> + xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> @@ -25,7 +25,7 @@ <fo:region-before extent="12pt"/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:static-content flow-name="xsl-region-before"> <fo:block font-size="8pt" text-align-last="justify">This is the page header<fo:leader/>Page <fo:page-number/></fo:block> @@ -117,7 +117,7 @@ <fo:block>And now we are going to see how a second page sequence is handled.</fo:block> </fo:flow> </fo:page-sequence> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:static-content flow-name="xsl-region-before"> <fo:block font-size="8pt" text-align-last="justify">This is the page header<fo:leader/>Page <fo:page-number/></fo:block> diff --git a/test/accessibility/config-renderer.xconf b/test/accessibility/config-renderer.xconf deleted file mode 100644 index 4b55e393f..000000000 --- a/test/accessibility/config-renderer.xconf +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<fop version="1.0"> - <prefer-renderer>true</prefer-renderer> - <accessibility>true</accessibility> - <source-resolution>144</source-resolution> - <use-cache>false</use-cache> - <font-base>../resources/fonts/</font-base> - <renderers> - <renderer mime="application/pdf"> - <filterList> - <value>null</value> - </filterList> - <filterList type="image"> - <value>flate</value> - <value>ascii-85</value> - </filterList> - <fonts> - <font embed-url="DejaVuLGCSerif.ttf"> - <font-triplet name="DejaVu" style="normal" weight="normal"/> - </font> - </fonts> - </renderer> - </renderers> -</fop> diff --git a/test/accessibility/config-painter.xconf b/test/accessibility/fop.xconf index 8c5dc2bd5..8c5dc2bd5 100644 --- a/test/accessibility/config-painter.xconf +++ b/test/accessibility/fop.xconf diff --git a/test/accessibility/image_jpg.fo b/test/accessibility/image_jpg.fo index be0e24bee..5fe36f61a 100644 --- a/test/accessibility/image_jpg.fo +++ b/test/accessibility/image_jpg.fo @@ -17,14 +17,14 @@ --> <!-- $Id$ --> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" - xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> + xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>This document contains an image in the JPEG format: <fo:external-graphic src="../resources/images/cmyk.jpg" diff --git a/test/accessibility/image_png.fo b/test/accessibility/image_png.fo index 3bcd12f4f..b529aa8c8 100644 --- a/test/accessibility/image_png.fo +++ b/test/accessibility/image_png.fo @@ -17,14 +17,14 @@ --> <!-- $Id$ --> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" - xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> + xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>This document contains an image in the PNG format: <fo:external-graphic src="../resources/images/fop-logo-color-24bit.png" diff --git a/test/accessibility/image_svg.fo b/test/accessibility/image_svg.fo index 4fac16be5..bbc77fe65 100644 --- a/test/accessibility/image_svg.fo +++ b/test/accessibility/image_svg.fo @@ -17,14 +17,14 @@ --> <!-- $Id$ --> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" - xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> + xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>This document contains an image in the SVG format: <fo:external-graphic src="../resources/images/circles.svg" diff --git a/test/accessibility/image_wmf.fo b/test/accessibility/image_wmf.fo index 729b48586..1a4de777b 100644 --- a/test/accessibility/image_wmf.fo +++ b/test/accessibility/image_wmf.fo @@ -17,14 +17,14 @@ --> <!-- $Id$ --> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" - xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> + xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="320pt" page-width="320pt" margin="10pt"> <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>This document contains an image in the WMF format: <fo:external-graphic src="../resources/images/testChart.wmf" diff --git a/test/accessibility/leader.fo b/test/accessibility/leader.fo index 91c9243e0..ffd768021 100644 --- a/test/accessibility/leader.fo +++ b/test/accessibility/leader.fo @@ -16,14 +16,14 @@ limitations under the License. --> <!-- $Id$ --> -<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify" text-align-last="justify"> <fo:block>This is a text followed by a leader with leader-pattern=​"use-content", the diff --git a/test/accessibility/links.fo b/test/accessibility/links.fo index 66f0f66f4..36250e332 100644 --- a/test/accessibility/links.fo +++ b/test/accessibility/links.fo @@ -17,14 +17,14 @@ --> <!-- $Id$ --> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" - xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> + xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>This is a <fo:wrapper color="blue"><fo:basic-link internal-destination="FOP">link</fo:basic-link></fo:wrapper> to the next diff --git a/test/accessibility/pdf/background-image_jpg_repeat_painter_orig.pdf b/test/accessibility/pdf/background-image_jpg_repeat.pdf Binary files differindex c7c27f9d1..221761757 100644 --- a/test/accessibility/pdf/background-image_jpg_repeat_painter_orig.pdf +++ b/test/accessibility/pdf/background-image_jpg_repeat.pdf diff --git a/test/accessibility/pdf/background-image_jpg_repeat_renderer_orig.pdf b/test/accessibility/pdf/background-image_jpg_repeat_renderer_orig.pdf Binary files differdeleted file mode 100644 index ee5d89d40..000000000 --- a/test/accessibility/pdf/background-image_jpg_repeat_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/background-image_jpg_single_painter_orig.pdf b/test/accessibility/pdf/background-image_jpg_single.pdf Binary files differindex 080f0b876..232afdd05 100644 --- a/test/accessibility/pdf/background-image_jpg_single_painter_orig.pdf +++ b/test/accessibility/pdf/background-image_jpg_single.pdf diff --git a/test/accessibility/pdf/background-image_jpg_single_renderer_orig.pdf b/test/accessibility/pdf/background-image_jpg_single_renderer_orig.pdf Binary files differdeleted file mode 100644 index 4d04ca427..000000000 --- a/test/accessibility/pdf/background-image_jpg_single_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/background-image_png_repeat_painter_orig.pdf b/test/accessibility/pdf/background-image_png_repeat.pdf Binary files differindex a10e1357e..52da4020a 100644 --- a/test/accessibility/pdf/background-image_png_repeat_painter_orig.pdf +++ b/test/accessibility/pdf/background-image_png_repeat.pdf diff --git a/test/accessibility/pdf/background-image_png_repeat_renderer_orig.pdf b/test/accessibility/pdf/background-image_png_repeat_renderer_orig.pdf Binary files differdeleted file mode 100644 index 982c50cc9..000000000 --- a/test/accessibility/pdf/background-image_png_repeat_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/background-image_png_single.pdf b/test/accessibility/pdf/background-image_png_single.pdf Binary files differnew file mode 100644 index 000000000..356e4885c --- /dev/null +++ b/test/accessibility/pdf/background-image_png_single.pdf diff --git a/test/accessibility/pdf/background-image_png_single_painter_orig.pdf b/test/accessibility/pdf/background-image_png_single_painter_orig.pdf Binary files differdeleted file mode 100644 index 1e9ff00b4..000000000 --- a/test/accessibility/pdf/background-image_png_single_painter_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/background-image_png_single_renderer_orig.pdf b/test/accessibility/pdf/background-image_png_single_renderer_orig.pdf Binary files differdeleted file mode 100644 index 2c7084dd8..000000000 --- a/test/accessibility/pdf/background-image_png_single_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/background-image_svg_repeat_painter_orig.pdf b/test/accessibility/pdf/background-image_svg_repeat.pdf Binary files differindex c641fbbe5..0bce2404d 100644 --- a/test/accessibility/pdf/background-image_svg_repeat_painter_orig.pdf +++ b/test/accessibility/pdf/background-image_svg_repeat.pdf diff --git a/test/accessibility/pdf/background-image_svg_repeat_renderer_orig.pdf b/test/accessibility/pdf/background-image_svg_repeat_renderer_orig.pdf Binary files differdeleted file mode 100644 index 65d71109e..000000000 --- a/test/accessibility/pdf/background-image_svg_repeat_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/background-image_svg_single_painter_orig.pdf b/test/accessibility/pdf/background-image_svg_single.pdf Binary files differindex 09a9ad895..3e4afcdcf 100644 --- a/test/accessibility/pdf/background-image_svg_single_painter_orig.pdf +++ b/test/accessibility/pdf/background-image_svg_single.pdf diff --git a/test/accessibility/pdf/background-image_svg_single_renderer_orig.pdf b/test/accessibility/pdf/background-image_svg_single_renderer_orig.pdf Binary files differdeleted file mode 100644 index b7c5a71e9..000000000 --- a/test/accessibility/pdf/background-image_svg_single_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/complete.pdf b/test/accessibility/pdf/complete.pdf Binary files differnew file mode 100644 index 000000000..cffb9e2cb --- /dev/null +++ b/test/accessibility/pdf/complete.pdf diff --git a/test/accessibility/pdf/complete_painter_orig.pdf b/test/accessibility/pdf/complete_painter_orig.pdf Binary files differdeleted file mode 100644 index caf7a41f8..000000000 --- a/test/accessibility/pdf/complete_painter_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/complete_renderer_orig.pdf b/test/accessibility/pdf/complete_renderer_orig.pdf Binary files differdeleted file mode 100644 index e7e4ed6c1..000000000 --- a/test/accessibility/pdf/complete_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/image_jpg_painter_orig.pdf b/test/accessibility/pdf/image_jpg.pdf Binary files differindex 1336f99eb..cb004bbd2 100644 --- a/test/accessibility/pdf/image_jpg_painter_orig.pdf +++ b/test/accessibility/pdf/image_jpg.pdf diff --git a/test/accessibility/pdf/image_jpg_renderer_orig.pdf b/test/accessibility/pdf/image_jpg_renderer_orig.pdf Binary files differdeleted file mode 100644 index 62be9c16c..000000000 --- a/test/accessibility/pdf/image_jpg_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/image_png.pdf b/test/accessibility/pdf/image_png.pdf Binary files differnew file mode 100644 index 000000000..c3289374f --- /dev/null +++ b/test/accessibility/pdf/image_png.pdf diff --git a/test/accessibility/pdf/image_png_painter_orig.pdf b/test/accessibility/pdf/image_png_painter_orig.pdf Binary files differdeleted file mode 100644 index b22c157b0..000000000 --- a/test/accessibility/pdf/image_png_painter_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/image_png_renderer_orig.pdf b/test/accessibility/pdf/image_png_renderer_orig.pdf Binary files differdeleted file mode 100644 index efd5d2bdd..000000000 --- a/test/accessibility/pdf/image_png_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/image_svg_painter_orig.pdf b/test/accessibility/pdf/image_svg.pdf Binary files differindex 1f5393c43..c3fce5b0e 100644 --- a/test/accessibility/pdf/image_svg_painter_orig.pdf +++ b/test/accessibility/pdf/image_svg.pdf diff --git a/test/accessibility/pdf/image_svg_renderer_orig.pdf b/test/accessibility/pdf/image_svg_renderer_orig.pdf Binary files differdeleted file mode 100644 index 9c020706d..000000000 --- a/test/accessibility/pdf/image_svg_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/image_wmf.pdf b/test/accessibility/pdf/image_wmf.pdf Binary files differnew file mode 100644 index 000000000..b9ec8c55a --- /dev/null +++ b/test/accessibility/pdf/image_wmf.pdf diff --git a/test/accessibility/pdf/image_wmf_painter_orig.pdf b/test/accessibility/pdf/image_wmf_painter_orig.pdf Binary files differdeleted file mode 100644 index 599b4c5ff..000000000 --- a/test/accessibility/pdf/image_wmf_painter_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/image_wmf_renderer_orig.pdf b/test/accessibility/pdf/image_wmf_renderer_orig.pdf Binary files differdeleted file mode 100644 index c82134915..000000000 --- a/test/accessibility/pdf/image_wmf_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/leader_painter_orig.pdf b/test/accessibility/pdf/leader.pdf Binary files differindex cf77a5c96..c7432e751 100644 --- a/test/accessibility/pdf/leader_painter_orig.pdf +++ b/test/accessibility/pdf/leader.pdf diff --git a/test/accessibility/pdf/leader_renderer_orig.pdf b/test/accessibility/pdf/leader_renderer_orig.pdf Binary files differdeleted file mode 100644 index 27c3afbde..000000000 --- a/test/accessibility/pdf/leader_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/links_painter_orig.pdf b/test/accessibility/pdf/links.pdf Binary files differindex ee37c72d6..91d7c2592 100644 --- a/test/accessibility/pdf/links_painter_orig.pdf +++ b/test/accessibility/pdf/links.pdf diff --git a/test/accessibility/pdf/links_renderer_orig.pdf b/test/accessibility/pdf/links_renderer_orig.pdf Binary files differdeleted file mode 100644 index 2bae65033..000000000 --- a/test/accessibility/pdf/links_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/role_painter_orig.pdf b/test/accessibility/pdf/role.pdf Binary files differindex 1eaaf1a9d..acb435027 100644 --- a/test/accessibility/pdf/role_painter_orig.pdf +++ b/test/accessibility/pdf/role.pdf diff --git a/test/accessibility/pdf/role_non-standard_painter_orig.pdf b/test/accessibility/pdf/role_non-standard.pdf Binary files differindex f8b1c9d2c..fcf614ed1 100644 --- a/test/accessibility/pdf/role_non-standard_painter_orig.pdf +++ b/test/accessibility/pdf/role_non-standard.pdf diff --git a/test/accessibility/pdf/role_non-standard_renderer_orig.pdf b/test/accessibility/pdf/role_non-standard_renderer_orig.pdf Binary files differdeleted file mode 100644 index 1700d7c03..000000000 --- a/test/accessibility/pdf/role_non-standard_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/role_renderer_orig.pdf b/test/accessibility/pdf/role_renderer_orig.pdf Binary files differdeleted file mode 100644 index fc8e002a8..000000000 --- a/test/accessibility/pdf/role_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/text_1_painter_orig.pdf b/test/accessibility/pdf/text_1.pdf Binary files differindex 2ad8a12f2..596419c9d 100644 --- a/test/accessibility/pdf/text_1_painter_orig.pdf +++ b/test/accessibility/pdf/text_1.pdf diff --git a/test/accessibility/pdf/text_1_renderer_orig.pdf b/test/accessibility/pdf/text_1_renderer_orig.pdf Binary files differdeleted file mode 100644 index 67d39890c..000000000 --- a/test/accessibility/pdf/text_1_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/text_2_painter_orig.pdf b/test/accessibility/pdf/text_2.pdf Binary files differindex 21ad6d0d8..19fff21a4 100644 --- a/test/accessibility/pdf/text_2_painter_orig.pdf +++ b/test/accessibility/pdf/text_2.pdf diff --git a/test/accessibility/pdf/text_2_renderer_orig.pdf b/test/accessibility/pdf/text_2_renderer_orig.pdf Binary files differdeleted file mode 100644 index 6a5956c66..000000000 --- a/test/accessibility/pdf/text_2_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/pdf/text_font-embedding_painter_orig.pdf b/test/accessibility/pdf/text_font-embedding.pdf Binary files differindex d5c61c040..0288449d5 100644 --- a/test/accessibility/pdf/text_font-embedding_painter_orig.pdf +++ b/test/accessibility/pdf/text_font-embedding.pdf diff --git a/test/accessibility/pdf/text_font-embedding_renderer_orig.pdf b/test/accessibility/pdf/text_font-embedding_renderer_orig.pdf Binary files differdeleted file mode 100644 index f8669cb98..000000000 --- a/test/accessibility/pdf/text_font-embedding_renderer_orig.pdf +++ /dev/null diff --git a/test/accessibility/role.fo b/test/accessibility/role.fo index e284cde9c..ced8a4d44 100644 --- a/test/accessibility/role.fo +++ b/test/accessibility/role.fo @@ -16,14 +16,14 @@ limitations under the License. --> <!-- $Id$ --> -<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" font-family="sans-serif"> <fo:block role="H1" font-weight="bold" font-size="150%" space-before.minimum="1.5em" diff --git a/test/accessibility/role_non-standard.fo b/test/accessibility/role_non-standard.fo index b6641aa2b..d3e1a9852 100644 --- a/test/accessibility/role_non-standard.fo +++ b/test/accessibility/role_non-standard.fo @@ -16,14 +16,14 @@ limitations under the License. --> <!-- $Id$ --> -<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" font-family="sans-serif"> <fo:block role="H1" font-weight="bold" font-size="150%" space-before.minimum="1.5em" diff --git a/test/accessibility/text_1.fo b/test/accessibility/text_1.fo index 2a90f6c14..31ad31514 100644 --- a/test/accessibility/text_1.fo +++ b/test/accessibility/text_1.fo @@ -16,14 +16,14 @@ limitations under the License. --> <!-- $Id$ --> -<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java application diff --git a/test/accessibility/text_2.fo b/test/accessibility/text_2.fo index 5eff059b6..f5693110e 100644 --- a/test/accessibility/text_2.fo +++ b/test/accessibility/text_2.fo @@ -16,14 +16,14 @@ limitations under the License. --> <!-- $Id$ --> -<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify"> <fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java application diff --git a/test/accessibility/text_font-embedding.fo b/test/accessibility/text_font-embedding.fo index 1d3abe027..10c1c99d9 100644 --- a/test/accessibility/text_font-embedding.fo +++ b/test/accessibility/text_font-embedding.fo @@ -16,14 +16,14 @@ limitations under the License. --> <!-- $Id$ --> -<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="en" country="GB"> <fo:layout-master-set> <fo:simple-page-master master-name="page" page-height="220pt" page-width="320pt" margin="10pt"> <fo:region-body/> </fo:simple-page-master> </fo:layout-master-set> - <fo:page-sequence master-reference="page" language="en" country="GB"> + <fo:page-sequence master-reference="page"> <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify" font-family="DejaVu"> <fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java application diff --git a/test/java/org/apache/fop/StandardTestSuite.java b/test/java/org/apache/fop/StandardTestSuite.java index bb59c8605..808542306 100644 --- a/test/java/org/apache/fop/StandardTestSuite.java +++ b/test/java/org/apache/fop/StandardTestSuite.java @@ -76,7 +76,8 @@ import org.apache.fop.pdf.PDFLibraryTestSuite; MinOptMaxTestCase.class, AdobeStandardEncodingTestCase.class, AFMParserTestCase.class, - FontEventProcessingTestCase.class + FontEventProcessingTestCase.class, + org.apache.fop.render.intermediate.IFStructureTreeBuilderTestCase.class }) public class StandardTestSuite { } diff --git a/test/java/org/apache/fop/accessibility/fo/DOMResultUtil.java b/test/java/org/apache/fop/accessibility/fo/DOMResultUtil.java new file mode 100644 index 000000000..5b4e264f2 --- /dev/null +++ b/test/java/org/apache/fop/accessibility/fo/DOMResultUtil.java @@ -0,0 +1,54 @@ +/* + * 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.io.File; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +/** + * Utility class to stream an instance of {@link DOMResult} into a file. May be + * useful for debugging. + */ +final class DOMResultUtil { + + private DOMResultUtil() { + } + + /** + * Streams the given result into a file of the given name. + * + * @param result the result of a transformation + * @param filename name of the file into which to stream the result + * @throws TransformerException if a problem occurs when streaming + */ + public static void streamToFile(DOMResult result, String filename) throws TransformerException { + DOMSource source = new DOMSource(result.getNode()); + TransformerFactory tFactory = TransformerFactory.newInstance(); + Transformer transformer = tFactory.newTransformer(); + transformer.transform(source, new StreamResult(new File(filename))); + } + +} diff --git a/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java b/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java new file mode 100644 index 000000000..9c53bdde3 --- /dev/null +++ b/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java @@ -0,0 +1,223 @@ +/* + * 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 static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TransformerHandler; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.custommonkey.xmlunit.Diff; +import org.junit.Test; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import org.apache.fop.accessibility.StructureTree2SAXEventAdapter; +import org.apache.fop.accessibility.StructureTreeEventHandler; +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.FODocumentParser; +import org.apache.fop.fo.FODocumentParser.FOEventHandlerFactory; +import org.apache.fop.fo.FOEventHandler; +import org.apache.fop.fo.LoadingException; +import org.apache.fop.fotreetest.DummyFOEventHandler; + +public class FO2StructureTreeConverterTestCase { + + private interface FOLoader { + + InputStream getFoInputStream(); + } + + private static final String STRUCTURE_TREE_SEQUENCE_NAME = "structure-tree-sequence"; + + private FOLoader foLoader; + + @Test + public void testCompleteDocument() throws Exception { + foLoader = new FOLoader() { + public InputStream getFoInputStream() { + return getResource("/org/apache/fop/fo/complete_document.fo"); + } + }; + testConverter(); + } + + @Test + public void testTableFooters() throws Exception { + foLoader = new FOLoader() { + public InputStream getFoInputStream() { + return getResource("table-footers.fo"); + } + }; + testConverter(); + } + + @Test + public void testCompleteContentWrappedInTableFooter() throws Exception { + Source xslt = new StreamSource(getResource("wrapCompleteDocumentInTableFooter.xsl")); + Transformer transformer = createTransformer(xslt); + InputStream originalFO = getResource("/org/apache/fop/fo/complete_document.fo"); + ByteArrayOutputStream transformedFoOutput = new ByteArrayOutputStream(); + transformer.transform(new StreamSource(originalFO), new StreamResult(transformedFoOutput)); + final byte[] transformedFoOutputBytes = transformedFoOutput.toByteArray(); + foLoader = new FOLoader() { + public InputStream getFoInputStream() { + return new ByteArrayInputStream(transformedFoOutputBytes); + } + }; + testConverter(); + } + + private Transformer createTransformer(Source xslt) throws TransformerFactoryConfigurationError, + TransformerConfigurationException { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + return transformerFactory.newTransformer(xslt); + } + + private static InputStream getResource(String name) { + return FO2StructureTreeConverterTestCase.class.getResourceAsStream(name); + } + + private void testConverter() throws Exception { + DOMResult expectedStructureTree = loadExpectedStructureTree(); + DOMResult actualStructureTree = buildActualStructureTree(); + final Diff diff = createDiff(expectedStructureTree, actualStructureTree); + assertTrue(diff.toString(), diff.identical()); + } + + private DOMResult loadExpectedStructureTree() { + DOMResult expectedStructureTree = new DOMResult(); + InputStream xslt = getResource("fo2StructureTree.xsl"); + runXSLT(xslt, foLoader.getFoInputStream(), expectedStructureTree); + return expectedStructureTree; + } + + private static void runXSLT(InputStream xslt, InputStream doc, Result result) { + Source fo = new StreamSource(doc); + try { + Transformer transformer = TransformerFactory.newInstance() + .newTransformer(new StreamSource(xslt)); + transformer.transform(fo, result); + } catch (TransformerConfigurationException e) { + throw new RuntimeException(e); + } catch (TransformerException e) { + throw new RuntimeException(e); + } finally { + closeStream(xslt); + closeStream(doc); + } + } + + private static void closeStream(InputStream stream) { + try { + stream.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private DOMResult buildActualStructureTree() throws Exception { + DOMResult actualStructureTree = new DOMResult(); + createStructureTreeFromDocument(foLoader.getFoInputStream(), actualStructureTree); + return actualStructureTree; + } + + private static void createStructureTreeFromDocument(InputStream foInputStream, + Result result) throws Exception { + TransformerHandler tHandler = createTransformerHandler(result); + startStructureTreeSequence(tHandler); + StructureTreeEventHandler structureTreeEventHandler + = StructureTree2SAXEventAdapter.newInstance(tHandler); + FODocumentParser documentParser = createDocumentParser(structureTreeEventHandler); + FOUserAgent userAgent = createFOUserAgent(documentParser); + parseDocument(foInputStream, documentParser, userAgent); + endStructureTreeSequence(tHandler); + } + + private static TransformerHandler createTransformerHandler(Result domResult) + throws TransformerConfigurationException, TransformerFactoryConfigurationError { + SAXTransformerFactory factory = (SAXTransformerFactory) SAXTransformerFactory.newInstance(); + TransformerHandler transformerHandler = factory.newTransformerHandler(); + transformerHandler.setResult(domResult); + return transformerHandler; + } + + private static void startStructureTreeSequence(TransformerHandler tHandler) throws SAXException { + tHandler.startDocument(); + tHandler.startElement("", STRUCTURE_TREE_SEQUENCE_NAME, STRUCTURE_TREE_SEQUENCE_NAME, + new AttributesImpl()); + } + + private static FODocumentParser createDocumentParser( + final StructureTreeEventHandler structureTreeEventHandler) { + return FODocumentParser.newInstance(new FOEventHandlerFactory() { + public FOEventHandler newFOEventHandler(FOUserAgent foUserAgent) { + return new FO2StructureTreeConverter(structureTreeEventHandler, + new DummyFOEventHandler(foUserAgent)); + } + }); + } + + private static FOUserAgent createFOUserAgent(FODocumentParser documentParser) { + FOUserAgent userAgent = documentParser.createFOUserAgent(); + userAgent.setAccessibility(true); + return userAgent; + } + + private static void parseDocument(InputStream foInputStream, FODocumentParser documentParser, + FOUserAgent userAgent) throws FOPException, LoadingException { + try { + documentParser.parse(foInputStream, userAgent); + } finally { + closeStream(foInputStream); + } + } + + private static void endStructureTreeSequence(TransformerHandler tHandler) throws SAXException { + tHandler.endElement("", STRUCTURE_TREE_SEQUENCE_NAME, STRUCTURE_TREE_SEQUENCE_NAME); + tHandler.endDocument(); + } + + private static Diff createDiff(DOMResult expected, DOMResult actual) { + Diff diff = new Diff(getDocument(expected), getDocument(actual)); + return diff; + } + + private static Document getDocument(DOMResult result) { + return (Document) result.getNode(); + } +} diff --git a/src/java/org/apache/fop/accessibility/reduceFOTree.xsl b/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl index 0d93a2506..ce326f3b1 100644 --- a/src/java/org/apache/fop/accessibility/reduceFOTree.xsl +++ b/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl @@ -29,8 +29,24 @@ </xsl:copy> </xsl:template> + + <!-- Ignore fo:root --> + <xsl:template match="fo:root"> + <structure-tree-sequence> + <xsl:apply-templates/> + </structure-tree-sequence> + </xsl:template> + + <!-- fo:page-sequence maps to structure-tree --> + <xsl:template match="fo:page-sequence"> + <structure-tree xmlns="http://xmlgraphics.apache.org/fop/intermediate"> + <xsl:apply-templates/> + </structure-tree> + </xsl:template> + + <!-- Declarations and Pagination and Layout Formatting Objects --> - <xsl:template match="fo:root|fo:page-sequence|fo:static-content|fo:flow"> + <xsl:template match="fo:static-content|fo:flow"> <xsl:call-template name="copy"/> </xsl:template> @@ -53,10 +69,18 @@ </xsl:template> <!-- Formatting Objects for Tables --> - <xsl:template match="fo:table-and-caption|fo:table-caption|fo:table"> + <xsl:template match="fo:table-and-caption|fo:table-caption"> <xsl:call-template name="copy"/> </xsl:template> + <xsl:template match="fo:table"> + <xsl:copy> + <xsl:apply-templates select="@*"/> + <xsl:apply-templates select="*[name() != 'fo:table-footer']"/> + <xsl:apply-templates select="fo:table-footer"/> + </xsl:copy> + </xsl:template> + <xsl:template match="fo:table-header|fo:table-footer|fo:table-body|fo:table-row|fo:table-cell"> <xsl:call-template name="copy"/> </xsl:template> @@ -84,17 +108,28 @@ <!-- Discard descendants of fo:leader --> <xsl:template match="fo:leader"/> - - <!-- Keep foi:ptr, fox:alt-text and role attributes, discard everything else --> - <xsl:template match="@foi:ptr|@fox:alt-text|@role"> + + <!-- Keep fox:alt-text and role attributes, discard everything else --> + <xsl:template match="@fox:alt-text|@role"> <xsl:copy-of select="."/> </xsl:template> <xsl:template match="@*"/> - <!-- Discard text --> + <!-- Discard text nodes... --> <xsl:template match="text()"/> + <!-- ...except those that will result into marked content --> + <xsl:template match="fo:title/text() + |fo:block/text() + |fo:bidi-override/text() + |fo:inline/text() + |fo:basic-link/text() + |fo:wrapper/text() + |fo:marker/text()"> + <marked-content xmlns="http://xmlgraphics.apache.org/fop/intermediate"/> + </xsl:template> + </xsl:stylesheet> diff --git a/test/java/org/apache/fop/accessibility/fo/table-footers.fo b/test/java/org/apache/fop/accessibility/fo/table-footers.fo new file mode 100644 index 000000000..6dcb9b68d --- /dev/null +++ b/test/java/org/apache/fop/accessibility/fo/table-footers.fo @@ -0,0 +1,195 @@ +<?xml version="1.0" standalone="no"?> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> + <fo:layout-master-set> + <fo:simple-page-master master-name="page" + page-height="440pt" page-width="420pt" margin="10pt"> + <fo:region-body display-align="center"/> + </fo:simple-page-master> + </fo:layout-master-set> + <fo:page-sequence master-reference="page"> + <fo:flow flow-name="xsl-region-body" line-height="10pt" font-size="8pt"> + <fo:table width="100% - 6pt" table-layout="fixed" + border-collapse="separate" border="2pt solid black" border-separation="2pt" padding="1pt" + start-indent="3pt" end-indent="3pt" space-after="2pt"> + <fo:table-header start-indent="0" end-indent="0"> + <fo:table-cell background-color="#E0E0E0" padding="2pt"> + <fo:block>Start Outer Header</fo:block> + <fo:table width="100% - 6pt" table-layout="fixed" + border="2pt solid red" padding="1pt" + start-indent="3pt" end-indent="3pt" space-after="2pt"> + <fo:table-header start-indent="0" end-indent="0"> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Header 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Header 1.2</fo:block> + </fo:table-cell> + </fo:table-header> + <fo:table-footer start-indent="0" end-indent="0"> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Footer 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Footer 1.2</fo:block> + </fo:table-cell> + </fo:table-footer> + <fo:table-body start-indent="0" end-indent="0"> + <fo:table-row> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Body 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Body 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + <fo:table-row> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Body 2.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Body 2.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + <fo:block>End Outer Header</fo:block> + </fo:table-cell> + </fo:table-header> + <fo:table-footer start-indent="0" end-indent="0"> + <fo:table-cell background-color="#E0E0E0" padding="2pt"> + <fo:block>Start Outer Footer</fo:block> + <fo:table width="100% - 6pt" table-layout="fixed" + border="2pt solid green" padding="1pt" + start-indent="3pt" end-indent="3pt" space-after="2pt"> + <fo:table-header start-indent="0" end-indent="0"> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Inner Header 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Inner Header 1.2</fo:block> + </fo:table-cell> + </fo:table-header> + <fo:table-footer start-indent="0" end-indent="0"> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Start Inner Footer 1.1</fo:block> + <fo:table width="100% - 6pt" table-layout="fixed" + border="2pt solid yellow" padding="1pt" + start-indent="3pt" end-indent="3pt" space-after="2pt"> + <fo:table-header start-indent="0" end-indent="0"> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Header 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Header 1.2</fo:block> + </fo:table-cell> + </fo:table-header> + <fo:table-footer start-indent="0" end-indent="0"> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Footer 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Footer 1.2</fo:block> + </fo:table-cell> + </fo:table-footer> + <fo:table-body start-indent="0" end-indent="0"> + <fo:table-row> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Body 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Body 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + <fo:table-row> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Body 2.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Body 2.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + <fo:block>End Inner Footer 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Inner Footer 1.2</fo:block> + </fo:table-cell> + </fo:table-footer> + <fo:table-body start-indent="0" end-indent="0"> + <fo:table-row> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Inner Body 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Inner Body 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + <fo:table-row> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Inner Body 2.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Inner Body 2.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + <fo:block>End Outer Footer</fo:block> + </fo:table-cell> + </fo:table-footer> + <fo:table-body start-indent="0" end-indent="0"> + <fo:table-row> + <fo:table-cell background-color="#E0E0E0" padding="2pt"> + <fo:block>Outer Body Cell 1</fo:block> + </fo:table-cell> + </fo:table-row> + <fo:table-row> + <fo:table-cell background-color="#E0E0E0" padding="2pt"> + <fo:block>Start Outer Body Cell 2</fo:block> + <fo:table width="100% - 6pt" table-layout="fixed" + border="2pt solid blue" padding="1pt" + start-indent="3pt" end-indent="3pt" space-after="2pt"> + <fo:table-header start-indent="0" end-indent="0"> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Footer 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Footer 1.2</fo:block> + </fo:table-cell> + </fo:table-header> + <fo:table-footer start-indent="0" end-indent="0"> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Header 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Header 1.2</fo:block> + </fo:table-cell> + </fo:table-footer> + <fo:table-body start-indent="0" end-indent="0"> + <fo:table-row> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Body 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Body 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + <fo:table-row> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Body 2.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Body 2.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + <fo:block>End Outer Body Cell 2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + </fo:flow> + </fo:page-sequence> +</fo:root> diff --git a/test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl b/test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl new file mode 100644 index 000000000..9608b2fb9 --- /dev/null +++ b/test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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$ --> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:fo="http://www.w3.org/1999/XSL/Format"> + + <xsl:template match="@*|node()" name="copy"> + <xsl:copy> + <xsl:apply-templates select="@*|node()"/> + </xsl:copy> + </xsl:template> + + + <xsl:template match="/"> + <fo:root> + <fo:layout-master-set> + <fo:simple-page-master master-name="page" + page-height="500pt" page-width="300pt" margin="20pt"> + <fo:region-body margin-top="20pt"/> + </fo:simple-page-master> + </fo:layout-master-set> + <xsl:apply-templates select="//fo:page-sequence"/> + </fo:root> + </xsl:template> + + <xsl:template match="fo:page-sequence"> + <fo:page-sequence master-reference="page"> + <xsl:apply-templates select="fo:flow"/> + </fo:page-sequence> + </xsl:template> + + <xsl:template match="fo:flow"> + <xsl:copy> + <xsl:apply-templates select="@*[not(starts-with(name(), 'space-before'))]"/> + <fo:table width="100%" table-layout="fixed"> + <fo:table-footer> + <fo:table-cell background-color="#F0F0F0"> + <xsl:apply-templates select="@*[starts-with(name(), 'space-before')]"/> + <xsl:apply-templates select="*"/> + </fo:table-cell> + </fo:table-footer> + <fo:table-body> + <fo:table-cell> + <fo:block>The content below is in the table footer.</fo:block> + </fo:table-cell> + </fo:table-body> + </fo:table> + </xsl:copy> + </xsl:template> + +</xsl:stylesheet> diff --git a/test/java/org/apache/fop/fo/DelegatingFOEventHandlerTestCase.java b/test/java/org/apache/fop/fo/DelegatingFOEventHandlerTestCase.java new file mode 100644 index 000000000..313379e02 --- /dev/null +++ b/test/java/org/apache/fop/fo/DelegatingFOEventHandlerTestCase.java @@ -0,0 +1,531 @@ +/* + * 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.fo; + +import static org.junit.Assert.assertArrayEquals; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.stream.StreamSource; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.FODocumentParser.FOEventHandlerFactory; +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.Leader; +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.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.TableColumn; +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.PageSequence; +import org.apache.fop.fo.pagination.Root; +import org.apache.fop.fo.pagination.StaticContent; + +/** + * Tests that {@link DelegatingFOEventHandler} does forward every event to its delegate + * event handler. + */ +public class DelegatingFOEventHandlerTestCase { + + private InputStream document; + + private List<String> expectedEvents; + + private List<String> actualEvents; + + private FODocumentParser documentParser; + + private class DelegatingFOEventHandlerTester extends FOEventHandler { + + DelegatingFOEventHandlerTester(FOUserAgent foUserAgent) { + super(foUserAgent); + } + + private final StringBuilder eventBuilder = new StringBuilder(); + + @Override + public void startDocument() throws SAXException { + actualEvents.add("start document"); + } + + @Override + public void endDocument() throws SAXException { + actualEvents.add("end document"); + } + + @Override + public void startRoot(Root root) { + startElement(root); + } + + @Override + public void endRoot(Root root) { + endElement(root); + } + + @Override + public void startPageSequence(PageSequence pageSeq) { + startElement(pageSeq); + } + + @Override + public void endPageSequence(PageSequence pageSeq) { + endElement(pageSeq); + } + + @Override + public void startPageNumber(PageNumber pagenum) { + startElement(pagenum); + } + + @Override + public void endPageNumber(PageNumber pagenum) { + endElement(pagenum); + } + + @Override + public void startPageNumberCitation(PageNumberCitation pageCite) { + startElement(pageCite); + } + + @Override + public void endPageNumberCitation(PageNumberCitation pageCite) { + endElement(pageCite); + } + + @Override + public void startPageNumberCitationLast(PageNumberCitationLast pageLast) { + startElement(pageLast); + } + + @Override + public void endPageNumberCitationLast(PageNumberCitationLast pageLast) { + endElement(pageLast); + } + + @Override + public void startFlow(Flow fl) { + startElement(fl); + } + + @Override + public void endFlow(Flow fl) { + endElement(fl); + } + + @Override + public void startBlock(Block bl) { + startElement(bl); + } + + @Override + public void endBlock(Block bl) { + endElement(bl); + } + + @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) { + startElement(tbl); + } + + @Override + public void endTable(Table tbl) { + endElement(tbl); + } + + @Override + public void startColumn(TableColumn tc) { + startElement(tc); + } + + @Override + public void endColumn(TableColumn tc) { + endElement(tc); + } + + @Override + public void startHeader(TableHeader header) { + startElement(header); + } + + @Override + public void endHeader(TableHeader header) { + endElement(header); + } + + @Override + public void startFooter(TableFooter footer) { + startElement(footer); + } + + @Override + public void endFooter(TableFooter footer) { + endElement(footer); + } + + @Override + public void startBody(TableBody body) { + startElement(body); + } + + @Override + public void endBody(TableBody body) { + endElement(body); + } + + @Override + public void startRow(TableRow tr) { + startElement(tr); + } + + @Override + public void endRow(TableRow tr) { + endElement(tr); + } + + @Override + public void startCell(TableCell tc) { + startElement(tc); + } + + @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 startStatic(StaticContent staticContent) { + startElement(staticContent); + } + + @Override + public void endStatic(StaticContent statisContent) { + endElement(statisContent); + } + + @Override + public void startLink(BasicLink basicLink) { + startElement(basicLink); + } + + @Override + public void endLink(BasicLink basicLink) { + endElement(basicLink); + } + + @Override + public void image(ExternalGraphic eg) { + startElement(eg); + endElement(eg); + } + + @Override + public void startInstreamForeignObject(InstreamForeignObject ifo) { + startElement(ifo); + } + + @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 startLeader(Leader l) { + startElement(l); + } + + @Override + public void endLeader(Leader l) { + endElement(l); + } + + @Override + public void startWrapper(Wrapper wrapper) { + startElement(wrapper); + } + + @Override + public void endWrapper(Wrapper wrapper) { + endElement(wrapper); + } + + @Override + public void character(Character c) { + startElement(c); + endElement(c); + } + + private void startElement(FObj node) { + addEvent("start ", node); + } + + private void endElement(FObj node) { + addEvent("end ", node); + } + + private void addEvent(String event, FObj node) { + eventBuilder.append(event); + eventBuilder.append(node.getLocalName()); + addID(node); + actualEvents.add(eventBuilder.toString()); + eventBuilder.setLength(0); + } + + private void addID(FObj node) { + String id = node.getId(); + if (id != null && id.length() > 0) { + eventBuilder.append(" id=\""); + eventBuilder.append(id); + eventBuilder.append("\""); + } + } + } + + @Before + public void setUp() throws IOException { + setUpEvents(); + loadDocument(); + createDocumentParser(); + } + + private void setUpEvents() throws IOException { + loadDocument(); + loadExpectedEvents(); + actualEvents = new ArrayList<String>(expectedEvents.size()); + } + + private void loadDocument() { + document = getClass().getResourceAsStream("complete_document.fo"); + } + + private void loadExpectedEvents() throws IOException { + expectedEvents = new ArrayList<String>(); + InputStream xslt = getClass().getResourceAsStream("extract-events.xsl"); + try { + runXSLT(xslt); + } finally { + closeStream(xslt); + closeStream(document); + } + } + + private void runXSLT(InputStream xslt) { + Transformer transformer = createTransformer(xslt); + Source fo = new StreamSource(document); + Result result = createTransformOutputHandler(); + try { + transformer.transform(fo, result); + } catch (TransformerException e) { + throw new RuntimeException(e); + } + } + + private Transformer createTransformer(InputStream xslt) { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + try { + return transformerFactory.newTransformer(new StreamSource(xslt)); + } catch (TransformerConfigurationException e) { + throw new RuntimeException(e); + } + } + + private Result createTransformOutputHandler() { + return new SAXResult(new DefaultHandler() { + + private final StringBuilder event = new StringBuilder(); + + @Override + public void startElement(String uri, String localName, String qName, + Attributes attributes) throws SAXException { + event.setLength(0); + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + event.append(ch, start, length); + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + expectedEvents.add(event.toString()); + } + + }); + } + + private void closeStream(InputStream stream) { + try { + stream.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void createDocumentParser() { + documentParser = FODocumentParser.newInstance(new FOEventHandlerFactory() { + + public FOEventHandler newFOEventHandler(FOUserAgent foUserAgent) { + return new DelegatingFOEventHandler( + new DelegatingFOEventHandlerTester(foUserAgent)) { + }; + } + }); + } + + @Test + public void testFOEventHandler() throws Exception { + documentParser.parse(document); + assertArrayEquals(expectedEvents.toArray(), actualEvents.toArray()); + } + + @After + public void unloadDocument() throws IOException { + document.close(); + } + + /** + * Prints the given list to {@code System.out}, each element on a new line. For + * debugging purpose. + * + * @param list a list + */ + public void printList(List<?> list) { + for (Object element : list) { + System.out.println(element); + } + } + +} diff --git a/test/java/org/apache/fop/fo/FODocumentParser.java b/test/java/org/apache/fop/fo/FODocumentParser.java new file mode 100644 index 000000000..a7574e49d --- /dev/null +++ b/test/java/org/apache/fop/fo/FODocumentParser.java @@ -0,0 +1,161 @@ +/* + * 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.fo; + +import java.io.InputStream; + +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.stream.StreamSource; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.Fop; +import org.apache.fop.apps.FopFactory; +import org.apache.fop.events.EventListener; + +/** + * Parse an FO document and run the corresponding FO events through a given + * {@link FOEventHandler} instance. That instance is created using the helper + * {@link FOEventHandlerFactory}. + * + * <p>An instance of this class may not be used in multiple threads concurrently.<p> + * + * <p>An instance of this class may be used multiple times if the given + * {@link FOEventHandler} implementation can be used multiple times. + */ +public final class FODocumentParser { + + private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance(); + + private static final FopFactory FOP_FACTORY = FopFactory.newInstance(); + + private final FOEventHandlerFactory foEventHandlerFactory; + + private Fop fop; + + private Transformer transformer; + + private EventListener eventListener; + + /** + * A factory to create custom instances of {@link FOEventHandler}. + */ + public static interface FOEventHandlerFactory { + + /** + * Creates a new {@code FOEventHandler} instance parameterized with the given FO user agent. + * + * @param foUserAgent an FO user agent + * @return a new {@code FOEventHandler} instance + */ + FOEventHandler newFOEventHandler(FOUserAgent foUserAgent); + } + + private FODocumentParser(FOEventHandlerFactory foeEventHandlerFactory) { + this.foEventHandlerFactory = foeEventHandlerFactory; + } + + /** + * Creates and returns a new FO document parser. The given factory will be used to + * customize the handler that will receive FO events, using the + * {@link FOUserAgent#setFOEventHandlerOverride(FOEventHandler)} method. + * + * @param foEventHandlerFactory the factory to be used to create {@code + * FOEventHandler} instances + * @return a new parser + */ + public static FODocumentParser newInstance(FOEventHandlerFactory foEventHandlerFactory) { + return new FODocumentParser(foEventHandlerFactory); + } + + /** + * Sets the event listener to be used if events occurs when parsing the document. + * + * @param eventListener an event listener + */ + public void setEventListener(EventListener eventListener) { + this.eventListener = eventListener; + } + + /** + * Runs FOP on the given document. + * + * @param document XSL-FO document to parse + * @throws FOPException if an error occurs when initializing FOP + * @throws LoadingException if an error occurs when parsing the document + */ + public void parse(InputStream document) throws FOPException, LoadingException { + parse(document, createFOUserAgent()); + } + + /** + * Runs FOP on the given document with the supplied {@link FOUserAgent}. + * + * @param document XSL-FO document to parse + * @param foUserAgent The user agent + * @throws FOPException if an error occurs when initializing FOP + * @throws LoadingException if an error occurs when parsing the document + */ + public void parse(InputStream document, FOUserAgent foUserAgent) + throws FOPException, LoadingException { + fop = FOP_FACTORY.newFop(foUserAgent); + createTransformer(); + runTransformer(document); + } + + /** + * Creates a new {@link FOUserAgent}. + * @return It + */ + public FOUserAgent createFOUserAgent() { + FOUserAgent userAgent = FOP_FACTORY.newFOUserAgent(); + FOEventHandler foEventHandler = foEventHandlerFactory.newFOEventHandler(userAgent); + userAgent.setFOEventHandlerOverride(foEventHandler); + if (eventListener != null) { + userAgent.getEventBroadcaster().addEventListener(eventListener); + } + return userAgent; + } + + private void createTransformer() { + try { + transformer = TRANSFORMER_FACTORY.newTransformer(); + } catch (TransformerConfigurationException e) { + throw new RuntimeException(e); + } + } + + private void runTransformer(InputStream input) throws LoadingException, FOPException { + Source source = new StreamSource(input); + Result result = new SAXResult(fop.getDefaultHandler()); + try { + transformer.transform(source, result); + } catch (TransformerException e) { + Throwable cause = e.getCause(); + throw new LoadingException(cause == null ? e : cause); + } + } +} diff --git a/test/java/org/apache/fop/fo/FONodeMocks.java b/test/java/org/apache/fop/fo/FONodeMocks.java new file mode 100644 index 000000000..1310d4a78 --- /dev/null +++ b/test/java/org/apache/fop/fo/FONodeMocks.java @@ -0,0 +1,88 @@ +/* + * 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.fo; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; + +import org.apache.xmlgraphics.image.loader.ImageException; +import org.apache.xmlgraphics.image.loader.ImageManager; +import org.apache.xmlgraphics.image.loader.ImageSessionContext; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.FopFactory; + +/** + * A helper class for creating mocks of {@link FONode} and its descendants. + */ +public final class FONodeMocks { + + private FONodeMocks() { } + + /** + * Creates and returns a mock {@link FONode} configured with a mock + * {@link FOEventHandler}. The FO event handler returns a mock {@link FOUserAgent}, + * which in turn returns a mock {@link FopFactory}, which returns a mock + * {@link ImageManager}. + * + * @return a mock FO node + */ + public static FONode mockFONode() { + FONode mockFONode = mock(FONode.class); + mockGetFOEventHandler(mockFONode); + return mockFONode; + } + + private static void mockGetFOEventHandler(FONode mockFONode) { + FOEventHandler mockFOEventHandler = mock(FOEventHandler.class); + mockGetUserAgent(mockFOEventHandler); + when(mockFONode.getFOEventHandler()).thenReturn(mockFOEventHandler); + } + + private static void mockGetUserAgent(FOEventHandler mockFOEventHandler) { + FOUserAgent mockFOUserAgent = mock(FOUserAgent.class); + mockGetFactory(mockFOUserAgent); + when(mockFOEventHandler.getUserAgent()).thenReturn(mockFOUserAgent); + } + + private static void mockGetFactory(FOUserAgent mockFOUserAgent) { + FopFactory mockFopFactory = mock(FopFactory.class); + mockGetImageManager(mockFopFactory); + when(mockFOUserAgent.getFactory()).thenReturn(mockFopFactory); + } + + private static void mockGetImageManager(FopFactory mockFopFactory) { + try { + ImageManager mockImageManager = mock(ImageManager.class); + when(mockImageManager.getImageInfo(anyString(), any(ImageSessionContext.class))) + .thenReturn(null); + when(mockFopFactory.getImageManager()).thenReturn(mockImageManager); + } catch (ImageException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/test/java/org/apache/fop/fo/LoadingException.java b/test/java/org/apache/fop/fo/LoadingException.java new file mode 100644 index 000000000..a5d509209 --- /dev/null +++ b/test/java/org/apache/fop/fo/LoadingException.java @@ -0,0 +1,34 @@ +/* + * 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.fo; + +/** + * This class specifies an exceptional condition that occurred while an XSL-FO document + * was being parsed. + */ +public class LoadingException extends Exception { + + private static final long serialVersionUID = 7529029475875542916L; + + LoadingException(Throwable cause) { + super(cause); + } + +} diff --git a/test/java/org/apache/fop/fo/complete_document.fo b/test/java/org/apache/fop/fo/complete_document.fo new file mode 100644 index 000000000..5a34e9e9a --- /dev/null +++ b/test/java/org/apache/fop/fo/complete_document.fo @@ -0,0 +1,176 @@ +<?xml version="1.0" standalone="no"?> +<!-- + 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$ --> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" + xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> + + <fo:layout-master-set> + <fo:simple-page-master master-name="page" + page-height="400pt" page-width="300pt" margin="20pt" margin-top="10pt"> + <fo:region-body margin-top="20pt"/> + <fo:region-before extent="15pt"/> + </fo:simple-page-master> + </fo:layout-master-set> + + <fo:page-sequence master-reference="page"> + <fo:static-content flow-name="xsl-region-before"> + <fo:block id="1" font-size="7pt" text-align-last="justify" padding-bottom="2pt" + border-bottom="0.25pt solid black">This is the page header<fo:leader/>Page <fo:page-number + id="2"/></fo:block> + </fo:static-content> + <fo:static-content flow-name="xsl-footnote-separator"> + <fo:block id="3"><fo:leader leader-length="100pt" leader-pattern="rule"/></fo:block> + </fo:static-content> + <fo:flow flow-name="xsl-region-body"> + <fo:block id="4">This is a link to the <fo:wrapper id="5" color="blue"><fo:basic-link id="6" + internal-destination="second-start">next page-sequence</fo:basic-link></fo:wrapper> + (which starts on page <fo:page-number-citation id="7" ref-id="second-start"/> and ends on + page <fo:page-number-citation-last id="8" ref-id="second-end"/>).</fo:block> + <fo:block id="9" font-family="sans-serif" font-weight="bold" space-before="1em" + space-after="0.2em" role="H1"><fo:block id="10">A Title Block</fo:block></fo:block> + <fo:block id="11">This block of text contains a footnote<fo:footnote id="12"><fo:inline id="13" + baseline-shift="super" font-size="70%">1</fo:inline><fo:footnote-body id="14"><fo:block + id="15">A footnote with a link to the <fo:wrapper id="16" color="blue"><fo:basic-link + id="17" external-destination="http://xmlgraphics.apache.org/fop/">FOP + website</fo:basic-link></fo:wrapper></fo:block></fo:footnote-body></fo:footnote> + call.</fo:block> + <fo:table id="18" space-before="1em" width="100%" table-layout="fixed"> + <fo:table-column id="19" column-width="proportional-column-width(1)"/> + <fo:table-column id="20" column-width="proportional-column-width(2)"/> + <fo:table-header id="21"> + <fo:table-row id="22"> + <fo:table-cell id="23" border="2pt solid black" padding="2pt 2pt 0"> + <fo:block id="24">Header 1.1</fo:block> + </fo:table-cell> + <fo:table-cell id="25" border="2pt solid black" padding="2pt 2pt 0"> + <fo:block id="26">Header 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-header> + <fo:table-footer id="27"> + <fo:table-row id="28"> + <fo:table-cell id="29" border="2pt solid black" padding="2pt 2pt 0"> + <fo:block id="30">Footer 1.1</fo:block> + </fo:table-cell> + <fo:table-cell id="31" border="2pt solid black" padding="2pt 2pt 0"> + <fo:block id="32">Footer 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-footer> + <fo:table-body id="33"> + <fo:table-row id="34"> + <fo:table-cell id="35" border="1pt solid black" padding="2pt 2pt 0"> + <fo:block id="36">Cell 1.1</fo:block> + </fo:table-cell> + <fo:table-cell id="37" border="1pt solid black" padding="2pt 2pt 0"> + <fo:block id="38">Cell 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + <fo:table-row id="39"> + <fo:table-cell id="40" border="1pt solid black" padding="2pt 2pt 0"> + <fo:block id="41">Cell 2.1</fo:block> + </fo:table-cell> + <fo:table-cell id="42" border="1pt solid black" padding="2pt 2pt 0"> + <fo:block id="43">Cell 2.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + <fo:block-container id="44" space-before="1.2em"> + <fo:block-container id="45" absolute-position="absolute" top="6pt" right="2.5pt" + inline-progression-dimension="37%" padding="3pt 1pt 2pt 3pt" border="1.5pt solid + darkblue"> + <fo:block id="46" color="darkblue" font-size="80%">This is an absolutely positioned + block-container. Nullam interdum mattis ipsum sit amet molestie.</fo:block> + </fo:block-container> + <fo:block id="47" end-indent="37% + 15pt">Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Integer vel lacinia diam. Etiam venenatis magna vel libero imperdiet + rhoncus.</fo:block> + </fo:block-container> + </fo:flow> + </fo:page-sequence> + + <fo:page-sequence master-reference="page"> + <fo:static-content id="48" flow-name="xsl-region-before"> + <fo:block id="49" font-size="7pt" text-align-last="justify" padding-bottom="2pt" + border-bottom="0.25pt solid black">This is the page header<fo:leader id="50"/>Page + <fo:page-number id="51"/></fo:block> + </fo:static-content> + <fo:flow flow-name="xsl-region-body" text-align="justify" space-before.minimum="8pt" + space-before.optimum="10pt" space-before.maximum="12pt"> + <fo:block id="second-start">Starting a new page-sequence.</fo:block> + <fo:block id="52" text-align="center">The <fo:external-graphic id="53" + src="test/resources/images/fop-logo-color-24bit.png" + inline-progression-dimension.maximum="50%" content-width="scale-to-fit" + alignment-adjust="-46%" alignment-baseline="middle" fox:alt-text="FOP Logo"/> + logo.</fo:block> + <fo:list-block id="54" provisional-distance-between-starts="15pt" + provisional-label-separation="0" space-before="inherit"> + <fo:list-item id="55"> + <fo:list-item-label id="56" end-indent="label-end()"> + <fo:block id="57">1.</fo:block> + </fo:list-item-label> + <fo:list-item-body id="58" start-indent="body-start()"> + <fo:block id="59">First item of a list</fo:block> + </fo:list-item-body> + </fo:list-item> + <fo:list-item id="60"> + <fo:list-item-label id="61" end-indent="label-end()"> + <fo:block id="62">2.</fo:block> + </fo:list-item-label> + <fo:list-item-body id="63" start-indent="body-start()"> + <fo:block id="64">Second item of a list</fo:block> + </fo:list-item-body> + </fo:list-item> + <fo:list-item id="65"> + <fo:list-item-label id="66" end-indent="label-end()"> + <fo:block id="67">3.</fo:block> + </fo:list-item-label> + <fo:list-item-body id="68" start-indent="body-start()"> + <fo:block id="69">Third item of a list</fo:block> + </fo:list-item-body> + </fo:list-item> + </fo:list-block> + <fo:block id="70" text-align="center"><fo:instream-foreign-object id="71" + inline-progression-dimension.maximum="50%" content-width="scale-to-fit" + fox:alt-text="An inline SVG"> + <svg xmlns="http://www.w3.org/2000/svg" width="319" height="286.6"> + <g style="fill-opacity:0.7; stroke:black; stroke-width:3" + transform="translate(0, 286.6) scale(1, -1) translate(100, 100)"> + <circle cx="50" cy="86.6" r="80" style="fill:red;"/> + <circle cx="0" cy="0" r="80" style="fill:green;"/> + <circle cx="100" cy="0" r="80" style="fill:blue;"/> + </g> + </svg> + </fo:instream-foreign-object></fo:block> + <fo:block id="72" space-before="inherit">A block containing an <fo:inline id="73" + border="0.5pt solid black" padding="2pt" padding-bottom="0">inline</fo:inline> + element.</fo:block> + <fo:block id="74" space-before="inherit">A block containing a fancy <fo:character id="75" + border="1pt solid black" padding="0 2pt 1pt 2pt" font-family="Symbol" character="♦"/> + character.</fo:block> + <fo:block id="76" space-before="inherit" text-align-last="justify">A leader with special + content: <fo:leader id="77" leader-pattern="use-content"><fo:inline id="78"><fo:character + id="79" character=" "/><fo:inline id="80" border="0.5pt solid black" + padding-left="2pt" padding-right="2pt"><fo:character id="81" baseline-shift="-10%" + character="•"/></fo:inline></fo:inline></fo:leader>.</fo:block> + <fo:block id="second-end" space-before="inherit">Ending the page-sequence.</fo:block> + </fo:flow> + </fo:page-sequence> + +</fo:root> diff --git a/test/java/org/apache/fop/fo/extract-events.xsl b/test/java/org/apache/fop/fo/extract-events.xsl new file mode 100644 index 000000000..6cf42c984 --- /dev/null +++ b/test/java/org/apache/fop/fo/extract-events.xsl @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:fo="http://www.w3.org/1999/XSL/Format" + exclude-result-prefixes="fo"> + + <xsl:output indent="yes" omit-xml-declaration="yes"/> + + <xsl:template match="/"> + <event> + <xsl:text>start document</xsl:text> + </event> + <xsl:apply-templates/> + <event> + <xsl:text>end document</xsl:text> + </event> + </xsl:template> + + <xsl:template match="fo:root"> + <event>start root</event> + <xsl:apply-templates select="fo:page-sequence"/> + <event>end root</event> + </xsl:template> + + <xsl:template match="fo:*"> + <xsl:call-template name="process.node"> + <xsl:with-param name="id"> + <xsl:apply-templates select="@id"/> + </xsl:with-param> + </xsl:call-template> + </xsl:template> + + <!-- Those elements do not retrieve the id property. + This will have to be fixed at some point. --> + <xsl:template match="fo:footnote|fo:footnote-body"> + <xsl:call-template name="process.node"/> + </xsl:template> + + <xsl:template name="process.node"> + <xsl:param name="id" select="''"/> + <event> + <xsl:text>start </xsl:text> + <xsl:value-of select="local-name()"/> + <xsl:value-of select="$id"/> + </event> + <xsl:apply-templates/> + <event> + <xsl:text>end </xsl:text> + <xsl:value-of select="local-name()"/> + <xsl:value-of select="$id"/> + </event> + </xsl:template> + + <xsl:template match="@id"> + <xsl:text> id="</xsl:text> + <xsl:value-of select="."/> + <xsl:text>"</xsl:text> + </xsl:template> + + <xsl:template match="text()"/> + +</xsl:stylesheet> diff --git a/test/java/org/apache/fop/fo/flow/table/AbstractTableTest.java b/test/java/org/apache/fop/fo/flow/table/AbstractTableTest.java index 69bbc8d8c..fb6ec6a25 100644 --- a/test/java/org/apache/fop/fo/flow/table/AbstractTableTest.java +++ b/test/java/org/apache/fop/fo/flow/table/AbstractTableTest.java @@ -19,33 +19,37 @@ package org.apache.fop.fo.flow.table; +import java.io.FileInputStream; import java.util.Iterator; import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.FODocumentParser; import org.apache.fop.fo.FOEventHandler; -import org.apache.fop.fotreetest.FOTreeUnitTester; +import org.apache.fop.fo.FODocumentParser.FOEventHandlerFactory; +import org.apache.fop.util.ConsoleEventListenerForTests; /** * Superclass for testcases related to tables, factoring the common stuff. */ -abstract class AbstractTableTest extends FOTreeUnitTester { +abstract class AbstractTableTest { - private FOTreeUnitTester.FOEventHandlerFactory tableHandlerFactory; + private FODocumentParser documentParser; private TableHandler tableHandler; - public AbstractTableTest() throws Exception { - super(); - tableHandlerFactory = new FOEventHandlerFactory() { - public FOEventHandler createFOEventHandler(FOUserAgent foUserAgent) { + protected void setUp(String filename) throws Exception { + createDocumentParser(); + documentParser.setEventListener(new ConsoleEventListenerForTests(filename)); + documentParser.parse(new FileInputStream("test/fotree/unittests/" + filename)); + } + + private void createDocumentParser() { + documentParser = FODocumentParser.newInstance(new FOEventHandlerFactory() { + public FOEventHandler newFOEventHandler(FOUserAgent foUserAgent) { tableHandler = new TableHandler(foUserAgent); return tableHandler; } - }; - } - - protected void setUp(String filename) throws Exception { - setUp(filename, tableHandlerFactory); + }); } protected TableHandler getTableHandler() { diff --git a/test/java/org/apache/fop/fo/flow/table/CollapsedConditionalBorderTestCase.java b/test/java/org/apache/fop/fo/flow/table/CollapsedConditionalBorderTestCase.java index 0c567b976..7c0301ca7 100644 --- a/test/java/org/apache/fop/fo/flow/table/CollapsedConditionalBorderTestCase.java +++ b/test/java/org/apache/fop/fo/flow/table/CollapsedConditionalBorderTestCase.java @@ -25,10 +25,11 @@ import java.awt.Color; import java.util.Iterator; import java.util.List; +import org.junit.Test; + import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode.FONodeIterator; import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; -import org.junit.Test; /** * A testcase for the resolution of collapsed borders in the FO tree, taking @@ -107,10 +108,6 @@ public class CollapsedConditionalBorderTestCase extends AbstractTableTest { {{border8pt, Color.black}, {border6pt, Color.blue}, {border8pt, Color.black}, {border6pt, Color.blue}, {border4pt, Color.black}, {border4pt, Color.black}, {border4pt, Color.red}, {border8pt, Color.black}, {border8pt, Color.black}, {border8pt, Color.black}, {border8pt, Color.black}, {border4pt, Color.blue}, {border4pt, Color.red}, {border6pt, Color.magenta}, {border6pt, Color.magenta}, {border6pt, Color.magenta}} }; - public CollapsedConditionalBorderTestCase() throws Exception { - super(); - } - private static GridUnit getGridUnit(TablePart part) { return (GridUnit) ((List) ((List) part.getRowGroups().get(0)).get(0)).get(0); } diff --git a/test/java/org/apache/fop/fo/flow/table/ErrorCheckTest.java b/test/java/org/apache/fop/fo/flow/table/ErrorCheckTest.java index 724259a75..b30c64c07 100644 --- a/test/java/org/apache/fop/fo/flow/table/ErrorCheckTest.java +++ b/test/java/org/apache/fop/fo/flow/table/ErrorCheckTest.java @@ -19,8 +19,10 @@ package org.apache.fop.fo.flow.table; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import org.apache.fop.fo.LoadingException; import org.apache.fop.fo.ValidationException; /** @@ -28,16 +30,13 @@ import org.apache.fop.fo.ValidationException; */ abstract class ErrorCheckTest extends AbstractTableTest { - public ErrorCheckTest() throws Exception { - super(); - } - protected void launchTest(String filename) throws Exception { try { setUp(filename); - fail(); - } catch (ValidationException e) { + fail("Expected ValidationException to be thrown"); + } catch (LoadingException e) { // TODO check location + assertTrue(e.getCause() instanceof ValidationException); } } diff --git a/test/java/org/apache/fop/fo/flow/table/IllegalRowSpanTestCase.java b/test/java/org/apache/fop/fo/flow/table/IllegalRowSpanTestCase.java index 024e15c2e..b2e7a2c9d 100644 --- a/test/java/org/apache/fop/fo/flow/table/IllegalRowSpanTestCase.java +++ b/test/java/org/apache/fop/fo/flow/table/IllegalRowSpanTestCase.java @@ -27,10 +27,6 @@ import org.junit.Test; */ public class IllegalRowSpanTestCase extends ErrorCheckTest { - public IllegalRowSpanTestCase() throws Exception { - super(); - } - @Test public void testBody1() throws Exception { launchTest("table/illegal-row-span_body_1.fo"); diff --git a/test/java/org/apache/fop/fo/flow/table/RowGroupBuilderTestCase.java b/test/java/org/apache/fop/fo/flow/table/RowGroupBuilderTestCase.java index 3df0338ab..361517a66 100644 --- a/test/java/org/apache/fop/fo/flow/table/RowGroupBuilderTestCase.java +++ b/test/java/org/apache/fop/fo/flow/table/RowGroupBuilderTestCase.java @@ -35,10 +35,6 @@ import org.junit.Test; */ public class RowGroupBuilderTestCase extends AbstractTableTest { - public RowGroupBuilderTestCase() throws Exception { - super(); - } - /** * Checks that the given table-body(header,footer) will return row groups as expected. * More precisely, checks that the number of row groups corresponds to the size of the diff --git a/test/java/org/apache/fop/fo/flow/table/TableColumnColumnNumberTestCase.java b/test/java/org/apache/fop/fo/flow/table/TableColumnColumnNumberTestCase.java index 4f7d9e054..a21806559 100644 --- a/test/java/org/apache/fop/fo/flow/table/TableColumnColumnNumberTestCase.java +++ b/test/java/org/apache/fop/fo/flow/table/TableColumnColumnNumberTestCase.java @@ -23,9 +23,10 @@ import static org.junit.Assert.assertEquals; import java.util.Iterator; +import org.junit.Test; + import org.apache.fop.datatypes.PercentBaseContext; import org.apache.fop.fo.FObj; -import org.junit.Test; public class TableColumnColumnNumberTestCase extends AbstractTableTest { @@ -50,10 +51,6 @@ public class TableColumnColumnNumberTestCase extends AbstractTableTest { private TablePercentBaseContext percentBaseContext = new TablePercentBaseContext(); - public TableColumnColumnNumberTestCase() throws Exception { - super(); - } - private void checkColumn(Table t, int number, boolean isImplicit, int spans, int repeated, int width) { TableColumn c = t.getColumn(number - 1); // TODO a repeated column has a correct number only for its first occurrence diff --git a/test/java/org/apache/fop/fo/flow/table/TooManyColumnsTestCase.java b/test/java/org/apache/fop/fo/flow/table/TooManyColumnsTestCase.java index 284fd1d2f..76a5d196d 100644 --- a/test/java/org/apache/fop/fo/flow/table/TooManyColumnsTestCase.java +++ b/test/java/org/apache/fop/fo/flow/table/TooManyColumnsTestCase.java @@ -23,10 +23,6 @@ import org.junit.Test; public class TooManyColumnsTestCase extends ErrorCheckTest { - public TooManyColumnsTestCase() throws Exception { - super(); - } - @Test public void testBody1() throws Exception { launchTest("table/too-many-columns_body_1.fo"); diff --git a/test/java/org/apache/fop/fo/flow/table/UnimplementedWarningNeutralizer.java b/test/java/org/apache/fop/fo/flow/table/UnimplementedWarningNeutralizer.java new file mode 100644 index 000000000..1a5e38291 --- /dev/null +++ b/test/java/org/apache/fop/fo/flow/table/UnimplementedWarningNeutralizer.java @@ -0,0 +1,38 @@ +/* + * 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.fo.flow.table; + +/** + * This class aims at easing testing, by preventing the event notification system from + * getting in the way just to issue an Unimplemented Feature warning. + */ +public final class UnimplementedWarningNeutralizer { + + private UnimplementedWarningNeutralizer() { } + + /** + * Neutralizes Unimplemented Feature events from the {@link TableAndCaption} and + * {@link TableCaption} classes. + */ + public static void neutralizeUnimplementedWarning() { + TableAndCaption.notImplementedWarningGiven = true; + TableCaption.notImplementedWarningGiven = true; + } +} diff --git a/test/java/org/apache/fop/fo/properties/AltTextHolderTestCase.java b/test/java/org/apache/fop/fo/properties/AltTextHolderTestCase.java new file mode 100644 index 000000000..cd5d545ff --- /dev/null +++ b/test/java/org/apache/fop/fo/properties/AltTextHolderTestCase.java @@ -0,0 +1,77 @@ +/* + * 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.fo.properties; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Test; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FONodeMocks; +import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.expr.PropertyException; +import org.apache.fop.fo.flow.AbstractGraphics; +import org.apache.fop.fo.flow.ExternalGraphic; +import org.apache.fop.fo.flow.InstreamForeignObject; + + +/** + * Tests that the fox:alt-text property is correctly set on objects that support it. + */ +public class AltTextHolderTestCase { + + private final String altText = "alternative text"; + + @Test + public void externalGraphicHasAltText() throws FOPException { + testAltTextGetter(new ExternalGraphic(mockFONode())); + } + + @Test + public void instreamForeignObjectHasAltText() throws FOPException { + testAltTextGetter(new InstreamForeignObject(mockFONode())); + } + + private FONode mockFONode() { + FONode mockFONode = FONodeMocks.mockFONode(); + FOUserAgent mockFOUserAgent = mockFONode.getFOEventHandler().getUserAgent(); + when(mockFOUserAgent.isAccessibilityEnabled()).thenReturn(true); + return mockFONode; + } + + private void testAltTextGetter(AbstractGraphics g) throws FOPException { + g.bind(mockPropertyList()); + assertEquals(altText, g.getAltText()); + } + + private PropertyList mockPropertyList() throws PropertyException { + PropertyList mockPropertyList = PropertyListMocks.mockPropertyList(); + Property mockAltText = mock(Property.class); + when(mockAltText.getString()).thenReturn(altText); + when(mockPropertyList.get(Constants.PR_X_ALT_TEXT)).thenReturn(mockAltText); + return mockPropertyList; + } + +} diff --git a/test/java/org/apache/fop/fo/properties/CommonAccessibilityHolderTestCase.java b/test/java/org/apache/fop/fo/properties/CommonAccessibilityHolderTestCase.java new file mode 100644 index 000000000..352a39713 --- /dev/null +++ b/test/java/org/apache/fop/fo/properties/CommonAccessibilityHolderTestCase.java @@ -0,0 +1,128 @@ +/* + * 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.fo.properties; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FONodeMocks; +import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.expr.PropertyException; +import org.apache.fop.fo.flow.table.UnimplementedWarningNeutralizer; + +/** + * This tests that all the FONodes that implement CommonAccessibilityHolder correctly configure + * the CommonAccessibility property. + */ +public class CommonAccessibilityHolderTestCase { + + private static final List<Class<? extends CommonAccessibilityHolder>> IMPLEMENTATIONS + = new ArrayList<Class<? extends CommonAccessibilityHolder>>(); + + private final String role = "role"; + + private final String sourceDocument = "source document"; + + static { + /* This triggers 'unimplemented feature' FO validation events so that the event system is + * not triggered when testing, avoiding extra convoluted dependency stubbing. */ + UnimplementedWarningNeutralizer.neutralizeUnimplementedWarning(); + + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.BasicLink.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.Block.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.pagination.bookmarks.Bookmark.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.pagination.bookmarks.BookmarkTitle.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.ExternalGraphic.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.Footnote.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.FootnoteBody.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.InitialPropertySet.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.Inline.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.InstreamForeignObject.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.Leader.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.ListBlock.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.ListItem.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.ListItemBody.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.ListItemLabel.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.PageNumber.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.PageNumberCitation.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.PageNumberCitationLast.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.pagination.Root.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.Table.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.TableAndCaption.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.TableBody.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.TableCaption.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.TableCell.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.TableFooter.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.TableHeader.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.TableRow.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.pagination.Title.class); + } + + /** + * Bind should be overridden to correctly configure the CommonAccessibility property + * @throws Exception - + */ + @Test + public void bindMustSetRoleAndSourceDoc() throws Exception { + final PropertyList mockPList = mockPropertyList(); + final FONode parent = FONodeMocks.mockFONode(); + for (Class<? extends CommonAccessibilityHolder> clazz : IMPLEMENTATIONS) { + Constructor<? extends CommonAccessibilityHolder> constructor + = clazz.getConstructor(FONode.class); + CommonAccessibilityHolder sut = constructor.newInstance(parent); + ((FONode)sut).bind(mockPList); + String errorMessage = "Test failed for " + clazz + ": "; + assertEquals(errorMessage, role, sut.getCommonAccessibility().getRole()); + assertEquals(errorMessage, sourceDocument, + sut.getCommonAccessibility().getSourceDocument()); + } + } + + private PropertyList mockPropertyList() throws PropertyException { + final PropertyList mockPList = PropertyListMocks.mockPropertyList(); + PropertyListMocks.mockTableProperties(mockPList); + PropertyListMocks.mockCommonBorderPaddingBackgroundProps(mockPList); + mockRoleProperty(mockPList); + mockSourceDocProperty(mockPList); + return mockPList; + } + + private void mockRoleProperty(PropertyList mockPList) throws PropertyException { + final Property mockRoleProperty = mock(Property.class); + when(mockRoleProperty.getString()).thenReturn(role); + when(mockPList.get(Constants.PR_ROLE)).thenReturn(mockRoleProperty); + } + + private void mockSourceDocProperty(PropertyList mockPList) throws PropertyException { + final Property mockSourceDocProperty = mock(Property.class); + when(mockSourceDocProperty.getString()).thenReturn(sourceDocument); + when(mockPList.get(Constants.PR_SOURCE_DOCUMENT)).thenReturn(mockSourceDocProperty); + } + +} diff --git a/test/java/org/apache/fop/fo/properties/PropertyListMocks.java b/test/java/org/apache/fop/fo/properties/PropertyListMocks.java new file mode 100644 index 000000000..380f6e5a8 --- /dev/null +++ b/test/java/org/apache/fop/fo/properties/PropertyListMocks.java @@ -0,0 +1,94 @@ +/* + * 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.fo.properties; + +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.expr.PropertyException; + +/** + * A helper class for mocking a property list. + */ +public final class PropertyListMocks { + + private PropertyListMocks() { } + + /** + * Creates and returns a mock property list returning a generic default for the + * {@link PropertyList#get(int)} method. + * + * @return a mock property list + */ + public static PropertyList mockPropertyList() { + try { + final PropertyList mockPList = mock(PropertyList.class); + final Property mockGenericProperty = PropertyMocks.mockGenericProperty(); + when(mockPList.get(anyInt())).thenReturn(mockGenericProperty); + return mockPList; + } catch (PropertyException e) { + throw new RuntimeException(e); + } + } + + /** + * Overrides with working mock properties the values returned by + * {@link PropertyList#get(int)} for {@link Constants#PR_COLUMN_NUMBER}, + * {@link Constants#PR_NUMBER_COLUMNS_SPANNED}, + * {@link Constants#PR_NUMBER_ROWS_SPANNED} and {@link Constants#PR_BORDER_COLLAPSE}. + * + * @param mockPList a mock property list + */ + public static void mockTableProperties(PropertyList mockPList) { + try { + final Property mockNumberProperty = PropertyMocks.mockNumberProperty(); + when(mockPList.get(Constants.PR_COLUMN_NUMBER)).thenReturn(mockNumberProperty); + when(mockPList.get(Constants.PR_NUMBER_COLUMNS_SPANNED)).thenReturn(mockNumberProperty); + when(mockPList.get(Constants.PR_NUMBER_ROWS_SPANNED)).thenReturn(mockNumberProperty); + + final Property borderCollapseProperty = mock(Property.class); + when(borderCollapseProperty.getEnum()).thenReturn(Constants.EN_SEPARATE); + when(mockPList.get(Constants.PR_BORDER_COLLAPSE)).thenReturn(borderCollapseProperty); + } catch (PropertyException e) { + throw new RuntimeException(e); + } + } + + /** + * Overrides with a working mock property the value returned by + * {@link PropertyList#getBorderPaddingBackgroundProps()}. + * + * @param mockPList a mock property list + */ + public static void mockCommonBorderPaddingBackgroundProps(PropertyList mockPList) { + try { + final CommonBorderPaddingBackground mockCommonBorderPaddingBackground + = mock(CommonBorderPaddingBackground.class); + when(mockPList.getBorderPaddingBackgroundProps()) + .thenReturn(mockCommonBorderPaddingBackground); + } catch (PropertyException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/test/java/org/apache/fop/fo/properties/PropertyMocks.java b/test/java/org/apache/fop/fo/properties/PropertyMocks.java new file mode 100644 index 000000000..40c923249 --- /dev/null +++ b/test/java/org/apache/fop/fo/properties/PropertyMocks.java @@ -0,0 +1,80 @@ +/* + * 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.fo.properties; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.fop.datatypes.Numeric; +import org.apache.fop.datatypes.PercentBaseContext; +import org.apache.fop.fo.Constants; + +/** + * Helper class to create mocks of various kinds of properties. + */ +public final class PropertyMocks { + + private PropertyMocks() { } + + /** + * Creates and returns a generic mock property returning decent defaults for the + * {@link Property#getString()}, {@link Property#getEnum()} and + * {@link Property#getLengthRange()} methods. + * + * @return a mock all-purpose property + */ + public static Property mockGenericProperty() { + final Property mockGenericProperty = mock(Property.class); + when(mockGenericProperty.getString()).thenReturn("A non-empty string"); + when(mockGenericProperty.getEnum()).thenReturn(Constants.EN_SPACE); + LengthRangeProperty lengthRangeProperty = mockLengthRangeProperty(); + when(mockGenericProperty.getLengthRange()).thenReturn(lengthRangeProperty); + return mockGenericProperty; + } + + private static LengthRangeProperty mockLengthRangeProperty() { + final LengthRangeProperty mockLengthRangeProperty = mock(LengthRangeProperty.class); + final Property optimum = mockOptimumProperty(); + when(mockLengthRangeProperty.getOptimum(any(PercentBaseContext.class))) + .thenReturn(optimum); + return mockLengthRangeProperty; + } + + /** + * Creates and returns a mock property returning a decent default for the + * {@link Property#getNumeric()} method. + * + * @return a mock number property + */ + public static Property mockNumberProperty() { + final Property mockNumberProperty = mock(Property.class); + final Numeric mockNumeric = mock(Numeric.class); + when(mockNumberProperty.getNumeric()).thenReturn(mockNumeric); + return mockNumberProperty; + } + + private static Property mockOptimumProperty() { + final Property optimum = mock(Property.class); + when(optimum.isAuto()).thenReturn(true); + return optimum; + } + +} diff --git a/test/java/org/apache/fop/fotreetest/FOTreeTestSuite.java b/test/java/org/apache/fop/fotreetest/FOTreeTestSuite.java index 4a77ca000..b74afefc5 100644 --- a/test/java/org/apache/fop/fotreetest/FOTreeTestSuite.java +++ b/test/java/org/apache/fop/fotreetest/FOTreeTestSuite.java @@ -29,7 +29,9 @@ import org.junit.runners.Suite; @Suite.SuiteClasses({ org.apache.fop.fo.flow.table.AllTests.class, org.apache.fop.fo.pagination.AllTests.class, - FOTreeTestCase.class }) - + org.apache.fop.fotreetest.FOTreeTestCase.class, + org.apache.fop.fo.properties.CommonAccessibilityHolderTestCase.class, + org.apache.fop.fo.DelegatingFOEventHandlerTestCase.class +}) public final class FOTreeTestSuite { } diff --git a/test/java/org/apache/fop/fotreetest/FOTreeUnitTester.java b/test/java/org/apache/fop/fotreetest/FOTreeUnitTester.java deleted file mode 100644 index 0906c266d..000000000 --- a/test/java/org/apache/fop/fotreetest/FOTreeUnitTester.java +++ /dev/null @@ -1,96 +0,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.fotreetest; - -import java.io.File; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.apache.fop.apps.FOUserAgent; -import org.apache.fop.apps.Fop; -import org.apache.fop.apps.FopFactory; -import org.apache.fop.fo.FOEventHandler; -import org.apache.fop.util.ConsoleEventListenerForTests; -import org.xml.sax.XMLReader; - - -/** - * Base class for unit-testing the FO tree building code. It performs the necessary setup - * to parse an FO file and register a proper {@link FOEventHandler}. That handler will be - * the entry point to test classes from the FObj hierarchy. - */ -public abstract class FOTreeUnitTester { - - private XMLReader foReader; - - private FopFactory fopFactory; - - /** - * Should be implemented by children testcases for properly setting up the custom - * FOEventHandler needed to test FObj classes. - */ - public abstract static class FOEventHandlerFactory { - - /** - * This method is called by FOTreeUnitTester when creating a {@link Fop} instance. - * That lets pass to the custom FOEventHandler the proper user agent that will be - * used by this instance. - * - * @param foUserAgent the user agent needed by the Fop instance that will be used - * to create the FO tree - * @return the appropriate FOEventHandler for performing the tests - */ - public abstract FOEventHandler createFOEventHandler(FOUserAgent foUserAgent); - } - - public FOTreeUnitTester() throws Exception { - // Stuff that needs to be set up only once and will be re-used for each test - SAXParserFactory spf = SAXParserFactory.newInstance(); - spf.setNamespaceAware(true); - spf.setValidating(false); - SAXParser parser; - parser = spf.newSAXParser(); - foReader = parser.getXMLReader(); - fopFactory = FopFactory.newInstance(); - } - - /** - * Launches FOP on the given FO file. - * - * @param filename path to the test FO file - * @param factory to create the appropriate FOEventHandler for performing tests - */ - public void setUp(String filename, FOEventHandlerFactory factory) throws Exception { - FOUserAgent ua = fopFactory.newFOUserAgent(); - ua.setFOEventHandlerOverride(factory.createFOEventHandler(ua)); - ua.getEventBroadcaster().addEventListener( - new ConsoleEventListenerForTests(filename)); - - Fop fop = fopFactory.newFop(ua); - - foReader.setContentHandler(fop.getDefaultHandler()); - foReader.setDTDHandler(fop.getDefaultHandler()); - foReader.setErrorHandler(fop.getDefaultHandler()); - foReader.setEntityResolver(fop.getDefaultHandler()); - - foReader.parse(new File("test/fotree/unittests/" + filename).toURI().toURL().toExternalForm()); - } -} diff --git a/test/java/org/apache/fop/intermediate/IFParserTestCase.java b/test/java/org/apache/fop/intermediate/IFParserTestCase.java index c37897dc1..7d4fb7ad8 100644 --- a/test/java/org/apache/fop/intermediate/IFParserTestCase.java +++ b/test/java/org/apache/fop/intermediate/IFParserTestCase.java @@ -48,6 +48,9 @@ import org.apache.fop.render.intermediate.IFSerializer; @RunWith(Parameterized.class) public class IFParserTestCase extends AbstractIFTest { + /** Set this to true to get the correspondence between test number and test file. */ + private static final boolean DEBUG = false; + /** * Gets the parameters for this test * @@ -56,7 +59,19 @@ public class IFParserTestCase extends AbstractIFTest { */ @Parameters public static Collection<File[]> getParameters() throws IOException { - return LayoutEngineTestUtils.getLayoutTestFiles(); + Collection<File[]> testFiles = LayoutEngineTestUtils.getLayoutTestFiles(); + if (DEBUG) { + printFiles(testFiles); + } + return testFiles; + } + + private static void printFiles(Collection<File[]> files) { + int index = 0; + for (File[] file : files) { + assert file.length == 1; + System.out.println(String.format("%3d %s", index++, file[0])); + } } /** diff --git a/test/java/org/apache/fop/render/intermediate/IFStructureTreeBuilderTestCase.java b/test/java/org/apache/fop/render/intermediate/IFStructureTreeBuilderTestCase.java new file mode 100644 index 000000000..65c6b25a6 --- /dev/null +++ b/test/java/org/apache/fop/render/intermediate/IFStructureTreeBuilderTestCase.java @@ -0,0 +1,169 @@ +/* + * 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.render.intermediate; + +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatcher; +import org.mockito.InOrder; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import org.apache.fop.fo.FOElementMapping; +import org.apache.fop.fo.extensions.ExtensionElementMapping; +import org.apache.fop.fo.extensions.InternalElementMapping; +import org.apache.fop.util.XMLUtil; + +public class IFStructureTreeBuilderTestCase { + + private IFStructureTreeBuilder sut; + + @Before + public void setUp() { + sut = new IFStructureTreeBuilder(); + } + + @Test + public void startAndEndPageSequence() throws SAXException { + final ContentHandler handler = mock(ContentHandler.class); + + try { + sut.replayEventsForPageSequence(handler, 0); + fail("No page sequences created"); + } catch (IndexOutOfBoundsException e) { + // Expected + } + + sut.startPageSequence(null); + sut.endPageSequence(); + + sut.replayEventsForPageSequence(handler, 0); + + InOrder inOrder = inOrder(handler); + + inOrder.verify(handler).startPrefixMapping( + InternalElementMapping.STANDARD_PREFIX, InternalElementMapping.URI); + inOrder.verify(handler).startPrefixMapping( + ExtensionElementMapping.STANDARD_PREFIX, ExtensionElementMapping.URI); + inOrder.verify(handler).startElement(eq(IFConstants.NAMESPACE), + eq(IFConstants.EL_STRUCTURE_TREE), + eq(IFConstants.EL_STRUCTURE_TREE), + any(Attributes.class)); + inOrder.verify(handler).endElement(eq(IFConstants.NAMESPACE), + eq(IFConstants.EL_STRUCTURE_TREE), + eq(IFConstants.EL_STRUCTURE_TREE)); + inOrder.verify(handler).endPrefixMapping(ExtensionElementMapping.STANDARD_PREFIX); + inOrder.verify(handler).endPrefixMapping(InternalElementMapping.STANDARD_PREFIX); + } + + @Test + public void startNode() throws Exception { + final String[] attributes = {"struct-id", "1"}; + final String nodeName = "block"; + final ContentHandler handler = mock(ContentHandler.class); + + sut.startPageSequence(null); + sut.startNode(nodeName, createSimpleAttributes(attributes)); + sut.endPageSequence(); + + sut.replayEventsForPageSequence(handler, 0); + + verify(handler).startElement(eq(FOElementMapping.URI), eq(nodeName), + eq(FOElementMapping.STANDARD_PREFIX + ":" + nodeName), + AttributesMatcher.match(createSimpleAttributes(attributes))); + } + + @Test + public void endNode() throws Exception { + final String nodeName = "block"; + final ContentHandler handler = mock(ContentHandler.class); + + sut.startPageSequence(null); + sut.endNode(nodeName); + sut.endPageSequence(); + + sut.replayEventsForPageSequence(handler, 0); + + verify(handler).endElement(eq(FOElementMapping.URI), eq(nodeName), + eq(FOElementMapping.STANDARD_PREFIX + ":" + nodeName)); + } + + private static Attributes createSimpleAttributes(String... attributes) { + assert (attributes.length % 2 == 0); + final AttributesImpl atts = new AttributesImpl(); + for (int i = 0; i < attributes.length; i += 2) { + String key = attributes[i]; + String value = attributes[i + 1]; + atts.addAttribute("", key, key, XMLUtil.CDATA, value); + } + return atts; + } + + private static final class AttributesMatcher extends ArgumentMatcher<Attributes> { + + private final Attributes expected; + + private AttributesMatcher(Attributes expected) { + this.expected = expected; + } + + public static Attributes match(Attributes expected) { + return argThat(new AttributesMatcher(expected)); + } + + public boolean matches(Object attributes) { + return attributesEqual(expected, (Attributes) attributes); + } + + private static boolean attributesEqual(Attributes attributes1, Attributes attributes2) { + if (attributes1.getLength() != attributes2.getLength()) { + return false; + } + for (int i = 0; i < attributes1.getLength(); i++) { + if (attributes1.getLocalName(i) != attributes2.getLocalName(i)) { + return false; + } + if (attributes1.getQName(i) != attributes2.getQName(i)) { + return false; + } + if (attributes1.getType(i) != attributes2.getType(i)) { + return false; + } + if (attributes1.getURI(i) != attributes2.getURI(i)) { + return false; + } + if (attributes1.getValue(i) != attributes2.getValue(i)) { + return false; + } + } + return true; + } + } +} diff --git a/test/java/org/apache/fop/render/intermediate/SAXEventRecorderTestCase.java b/test/java/org/apache/fop/render/intermediate/SAXEventRecorderTestCase.java new file mode 100644 index 000000000..c5aad66d0 --- /dev/null +++ b/test/java/org/apache/fop/render/intermediate/SAXEventRecorderTestCase.java @@ -0,0 +1,131 @@ +/* + * 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.render.intermediate; + + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import org.apache.fop.render.intermediate.IFStructureTreeBuilder.SAXEventRecorder; +import org.apache.fop.util.XMLUtil; + +/** + * Tests {@link SAXEventRecorder}. + */ +public class SAXEventRecorderTestCase { + + private static final String URI = "http://www.example.com/"; + + private SAXEventRecorder sut; + + @Before + public void setUp() { + sut = new SAXEventRecorder(); + } + + @Test + public void testStartEvent() throws SAXException { + final String localName = "element"; + final String qName = "prefix:" + localName; + final Attributes attributes = new AttributesImpl(); + + sut.startElement(URI, localName, qName, attributes); + ContentHandler handler = mock(ContentHandler.class); + sut.replay(handler); + verify(handler).startElement(URI, localName, qName, attributes); + } + + @Test + public void testEndEvent() throws SAXException { + final String localName = "element"; + final String qName = "prefix:" + localName; + sut.endElement(URI, localName, qName); + ContentHandler handler = mock(ContentHandler.class); + sut.replay(handler); + verify(handler).endElement(URI, localName, qName); + } + + @Test + public void testStartPrefixMapping() throws SAXException { + final String prefix = "prefix"; + + sut.startPrefixMapping(URI, prefix); + ContentHandler handler = mock(ContentHandler.class); + sut.replay(handler); + verify(handler).startPrefixMapping(URI, prefix); + } + + @Test + public void testEndPrefixMapping() throws SAXException { + final String prefix = "prefix"; + + sut.endPrefixMapping(prefix); + ContentHandler handler = mock(ContentHandler.class); + sut.replay(handler); + verify(handler).endPrefixMapping(prefix); + } + + @Test + public void completeTest() throws SAXException { + final String localName1 = "element"; + final String qName1 = "prefix:" + localName1; + final Attributes attributes1 = createAttributes(URI, localName1, qName1, "value-1"); + final String localName2 = "element2"; + final String qName2 = "prefix:" + localName2; + final Attributes attributes2 = createAttributes(URI, localName2, qName2, "value-2"); + final ContentHandler handler = mock(ContentHandler.class); + final String extensionUrl = "http://www.example.com/extension"; + final String extensionPrefix = "ext"; + + sut.startPrefixMapping(extensionPrefix, extensionUrl); + sut.startElement(URI, localName1, qName1, attributes1); + sut.startElement(URI, localName2, qName2, attributes2); + sut.endElement(URI, localName2, qName2); + sut.endElement(URI, localName1, qName1); + sut.endPrefixMapping(extensionPrefix); + + sut.replay(handler); + + InOrder inOrder = inOrder(handler); + inOrder.verify(handler).startPrefixMapping(extensionPrefix, extensionUrl); + inOrder.verify(handler).startElement(URI, localName1, qName1, attributes1); + inOrder.verify(handler).startElement(URI, localName2, qName2, attributes2); + inOrder.verify(handler).endElement(URI, localName2, qName2); + inOrder.verify(handler).endElement(URI, localName1, qName1); + inOrder.verify(handler).endPrefixMapping(extensionPrefix); + } + + private static Attributes createAttributes(String uri, String localName, + String qName, String value) { + final AttributesImpl atts = new AttributesImpl(); + atts.addAttribute(uri, localName, qName, XMLUtil.CDATA, value); + return atts; + } + +} diff --git a/test/java/org/apache/fop/util/XMLUtilTestCase.java b/test/java/org/apache/fop/util/LanguageTagsTestCase.java index 4c1b999e1..f7383c720 100644 --- a/test/java/org/apache/fop/util/XMLUtilTestCase.java +++ b/test/java/org/apache/fop/util/LanguageTagsTestCase.java @@ -20,31 +20,39 @@ package org.apache.fop.util; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; import java.util.Locale; import org.junit.Test; /** - * Tests {@link XMLUtil}. + * Tests {@link LanguageTags}. */ -public class XMLUtilTestCase { +public class LanguageTagsTestCase { + + @Test(expected = NullPointerException.class) + public void toLanguageTagRejectsNull() { + LanguageTags.toLanguageTag(null); + } @Test - public void testLocaleToRFC3066() throws Exception { - assertNull(XMLUtil.toRFC3066(null)); - assertEquals("en", XMLUtil.toRFC3066(new Locale("en"))); - assertEquals("en-US", XMLUtil.toRFC3066(new Locale("en", "US"))); - assertEquals("en-US", XMLUtil.toRFC3066(new Locale("EN", "us"))); + public void testToLanguageTag() throws Exception { + assertEquals("", LanguageTags.toLanguageTag(new Locale(""))); + assertEquals("en", LanguageTags.toLanguageTag(new Locale("en"))); + assertEquals("en-US", LanguageTags.toLanguageTag(new Locale("en", "US"))); + assertEquals("en-US", LanguageTags.toLanguageTag(new Locale("EN", "us"))); + } + + @Test(expected = NullPointerException.class) + public void toLocaleRejectsNull() { + LanguageTags.toLocale(null); } @Test public void testRFC3066ToLocale() throws Exception { - assertNull(XMLUtil.convertRFC3066ToLocale(null)); - assertNull(XMLUtil.convertRFC3066ToLocale("")); - assertEquals(new Locale("en"), XMLUtil.convertRFC3066ToLocale("en")); - assertEquals(new Locale("en", "US"), XMLUtil.convertRFC3066ToLocale("en-US")); - assertEquals(new Locale("en", "US"), XMLUtil.convertRFC3066ToLocale("EN-us")); + assertEquals(new Locale(""), LanguageTags.toLocale("")); + assertEquals(new Locale("en"), LanguageTags.toLocale("en")); + assertEquals(new Locale("en", "US"), LanguageTags.toLocale("en-US")); + assertEquals(new Locale("en", "US"), LanguageTags.toLocale("EN-us")); } } |