]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Removed the DOM representation of the structure tree.
authorVincent Hennebert <vhennebert@apache.org>
Thu, 24 Nov 2011 17:15:28 +0000 (17:15 +0000)
committerVincent Hennebert <vhennebert@apache.org>
Thu, 24 Nov 2011 17:15:28 +0000 (17:15 +0000)
The structure tree is now directly converted into corresponding PDF objects.
When going the IF route, the structure tree is stored in the form of SAX events that will be re-played when it's time to stream them into the output. This may still change.

* Extracted RFC3066 methods from XMLUtil into new LanguageTags class that can be re-used in a non-XML context.
* Dropped support for accessibility in the old Area Tree XML.
* Added support for the xml:lang property on fo:root, so that the global language can be set for the document without retrieving the language from the first page-sequence.
* Renamed StructureTreeBuilder into more appropriate StructureTreeEventHandler (same for applicable sub-classes)
* Renamed StructureTreeBuildingFOEventHandler into FO2StructureTreeConverter and added test case
* Added test cases for classes in the StructureTreeEventHandler hierarchy

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_ImproveAccessibility@1205935 13f79535-47bb-0310-9956-ffa450edef68

76 files changed:
src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd
src/java/org/apache/fop/accessibility/DummyStructureTreeEventHandler.java [new file with mode: 0644]
src/java/org/apache/fop/accessibility/FO2StructureTreeConverter.java [new file with mode: 0644]
src/java/org/apache/fop/accessibility/StructureTree.java [deleted file]
src/java/org/apache/fop/accessibility/StructureTree2SAXEventAdapter.java [new file with mode: 0644]
src/java/org/apache/fop/accessibility/StructureTreeBuilder.java [deleted file]
src/java/org/apache/fop/accessibility/StructureTreeBuildingFOEventHandler.java [deleted file]
src/java/org/apache/fop/accessibility/StructureTreeEventHandler.java [new file with mode: 0644]
src/java/org/apache/fop/apps/FOUserAgent.java
src/java/org/apache/fop/area/AreaTreeHandler.java
src/java/org/apache/fop/area/AreaTreeModel.java
src/java/org/apache/fop/area/AreaTreeParser.java
src/java/org/apache/fop/area/RenderPagesModel.java
src/java/org/apache/fop/fo/DelegatingFOEventHandler.java
src/java/org/apache/fop/fo/FOElementMapping.java
src/java/org/apache/fop/fo/FOEventHandler.java
src/java/org/apache/fop/fo/FOTreeBuilder.java
src/java/org/apache/fop/fo/pagination/Root.java
src/java/org/apache/fop/pdf/PDFDocument.java
src/java/org/apache/fop/pdf/PDFProfile.java
src/java/org/apache/fop/pdf/PDFRoot.java
src/java/org/apache/fop/pdf/PDFStructElem.java
src/java/org/apache/fop/render/AbstractRenderer.java
src/java/org/apache/fop/render/Renderer.java
src/java/org/apache/fop/render/intermediate/AbstractIFDocumentHandler.java
src/java/org/apache/fop/render/intermediate/IFConstants.java
src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java
src/java/org/apache/fop/render/intermediate/IFParser.java
src/java/org/apache/fop/render/intermediate/IFRenderer.java
src/java/org/apache/fop/render/intermediate/IFSerializer.java
src/java/org/apache/fop/render/intermediate/IFSerializerMaker.java
src/java/org/apache/fop/render/intermediate/IFStructureTreeBuilder.java [new file with mode: 0644]
src/java/org/apache/fop/render/intermediate/util/IFDocumentHandlerProxy.java
src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java
src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
src/java/org/apache/fop/render/pdf/PDFDocumentHandlerMaker.java
src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java
src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java [new file with mode: 0644]
src/java/org/apache/fop/render/xml/XMLRenderer.java
src/java/org/apache/fop/util/LanguageTags.java [new file with mode: 0644]
src/java/org/apache/fop/util/XMLUtil.java
test/accessibility/config-painter.xconf [deleted file]
test/accessibility/config-renderer.xconf [deleted file]
test/accessibility/fop.xconf [new file with mode: 0644]
test/accessibility/pdf/background-image_jpg_repeat.pdf
test/accessibility/pdf/background-image_jpg_single.pdf
test/accessibility/pdf/background-image_png_repeat.pdf
test/accessibility/pdf/background-image_png_single.pdf
test/accessibility/pdf/background-image_svg_repeat.pdf
test/accessibility/pdf/background-image_svg_single.pdf
test/accessibility/pdf/complete.pdf
test/accessibility/pdf/image_jpg.pdf
test/accessibility/pdf/image_png.pdf
test/accessibility/pdf/image_svg.pdf
test/accessibility/pdf/image_wmf.pdf
test/accessibility/pdf/leader.pdf
test/accessibility/pdf/links.pdf
test/accessibility/pdf/role.pdf
test/accessibility/pdf/role_non-standard.pdf
test/accessibility/pdf/text_1.pdf
test/accessibility/pdf/text_2.pdf
test/accessibility/pdf/text_font-embedding.pdf
test/java/org/apache/fop/StandardTestSuite.java
test/java/org/apache/fop/accessibility/FO2StructureTreeConverterTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/accessibility/fo2StructureTree.xsl [new file with mode: 0644]
test/java/org/apache/fop/fo/DelegatingFOEventHandlerTestCase.fo [deleted file]
test/java/org/apache/fop/fo/DelegatingFOEventHandlerTestCase.java
test/java/org/apache/fop/fo/FODocumentParser.java
test/java/org/apache/fop/fo/complete_document.fo [new file with mode: 0644]
test/java/org/apache/fop/fo/extract-events.xsl
test/java/org/apache/fop/fo/properties/CommonAccessibilityHolderTestCase.java
test/java/org/apache/fop/intermediate/IFParserTestCase.java
test/java/org/apache/fop/render/intermediate/IFStructureTreeBuilderTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/render/intermediate/SAXEventRecorderTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/util/LanguageTagsTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/util/XMLUtilTestCase.java [deleted file]

index b6fa00bd2b2807f358fa52206c416f904c7e0e93..86dce1a3cccb2478aa6638664b133a665180adfc 100644 (file)
   </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/DummyStructureTreeEventHandler.java b/src/java/org/apache/fop/accessibility/DummyStructureTreeEventHandler.java
new file mode 100644 (file)
index 0000000..80a011b
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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 startNode(String name, Attributes attributes) {
+    }
+
+    /** {@inheritDoc} */
+    public void endNode(String name) {
+    }
+
+    /** {@inheritDoc} */
+    public void endPageSequence() {
+    }
+
+}
diff --git a/src/java/org/apache/fop/accessibility/FO2StructureTreeConverter.java b/src/java/org/apache/fop/accessibility/FO2StructureTreeConverter.java
new file mode 100644 (file)
index 0000000..e6d0193
--- /dev/null
@@ -0,0 +1,823 @@
+/*
+ * 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.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fo.DelegatingFOEventHandler;
+import org.apache.fop.fo.FOEventHandler;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.extensions.ExtensionElementMapping;
+import org.apache.fop.fo.extensions.ExternalDocument;
+import org.apache.fop.fo.extensions.InternalElementMapping;
+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.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.fo.properties.CommonAccessibilityHolder;
+import org.apache.fop.util.XMLUtil;
+
+/**
+ * Allows to create the structure tree of an FO document, by converting FO
+ * events into appropriate structure tree events.
+ */
+public class FO2StructureTreeConverter extends DelegatingFOEventHandler {
+
+    private int idCounter;
+
+    /** Delegates to either {@link #foToStructureTreeEventAdapter} or {@link #eventSwallower}. */
+    private FOEventHandler converter;
+
+    private final FOEventHandler foToStructureTreeEventAdapter;
+
+    /** The descendants of some elements like fo:leader must be ignored. */
+    private final FOEventHandler eventSwallower;
+
+    private final StructureTreeEventHandler structureTreeEventHandler;
+
+    private final class FOToStructureTreeEventAdapter extends FOEventHandler {
+
+        public FOToStructureTreeEventAdapter(FOUserAgent foUserAgent) {
+            super(foUserAgent);
+        }
+
+        @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) {
+            startElementWithID(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) {
+            startElementWithID(inl);
+        }
+
+        @Override
+        public void endInline(Inline inl) {
+            endElement(inl);
+        }
+
+        @Override
+        public void startTable(Table tbl) {
+            startElementWithID(tbl);
+        }
+
+        @Override
+        public void endTable(Table tbl) {
+            endElement(tbl);
+        }
+
+        @Override
+        public void startHeader(TableHeader header) {
+            startElementWithID(header);
+        }
+
+        @Override
+        public void endHeader(TableHeader header) {
+            endElement(header);
+        }
+
+        @Override
+        public void startFooter(TableFooter footer) {
+            startElementWithID(footer);
+        }
+
+        @Override
+        public void endFooter(TableFooter footer) {
+            endElement(footer);
+        }
+
+        @Override
+        public void startBody(TableBody body) {
+            startElementWithID(body);
+        }
+
+        @Override
+        public void endBody(TableBody body) {
+            endElement(body);
+        }
+
+        @Override
+        public void startRow(TableRow tr) {
+            startElementWithID(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));
+            }
+            startElementWithID(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);
+        }
+
+
+        private void startElement(FONode node) {
+            startElement(node, new AttributesImpl());
+        }
+
+        private void startElementWithID(FONode node) {
+            startElementWithID(node, new AttributesImpl());
+        }
+
+        private void startElementWithIDAndAltText(AbstractGraphics node) {
+            AttributesImpl attributes = new AttributesImpl();
+            addAttribute(attributes, ExtensionElementMapping.URI, "alt-text",
+                    ExtensionElementMapping.STANDARD_PREFIX, node.getAltText());
+            startElementWithID(node, attributes);
+        }
+
+        private void startElementWithID(FONode node, AttributesImpl attributes) {
+            String id = Integer.toHexString(idCounter++);
+            node.setPtr(id);
+            addAttribute(attributes,
+                    InternalElementMapping.URI, "ptr", InternalElementMapping.STANDARD_PREFIX, id);
+            startElement(node, attributes);
+        }
+
+        private void startElement(FONode node, AttributesImpl attributes) {
+            String localName = node.getLocalName();
+            if (node instanceof CommonAccessibilityHolder) {
+                addRole((CommonAccessibilityHolder) node, attributes);
+            }
+            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);
+        }
+
+    }
+
+    /**
+     * 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.structureTreeEventHandler = structureTreeEventHandler;
+        this.foToStructureTreeEventAdapter = new FOToStructureTreeEventAdapter(foUserAgent);
+        this.converter = foToStructureTreeEventAdapter;
+        this.eventSwallower = new FOEventHandler(foUserAgent) { };
+    }
+
+    @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);
+        super.startTable(tbl);
+    }
+
+    @Override
+    public void endTable(Table tbl) {
+        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) {
+        converter.startFooter(footer);
+        super.startFooter(footer);
+    }
+
+    @Override
+    public void endFooter(TableFooter footer) {
+        converter.endFooter(footer);
+        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) {
+        converter = eventSwallower;
+        converter.startLeader(l);
+        super.startLeader(l);
+    }
+
+    @Override
+    public void endLeader(Leader l) {
+        converter.endLeader(l);
+        converter = foToStructureTreeEventAdapter;
+        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(char[] data, int start, int length) {
+        converter.characters(data, start, length);
+        super.characters(data, start, length);
+    }
+
+    @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/StructureTree.java b/src/java/org/apache/fop/accessibility/StructureTree.java
deleted file mode 100644 (file)
index a8afd4e..0000000
+++ /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.
-     */
-    public 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 (file)
index 0000000..240c384
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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
+     */
+    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 void startNode(String name, Attributes attributes) {
+        try {
+            contentHandler.startElement(FOElementMapping.URI, name,
+                    FOElementMapping.STANDARD_PREFIX + ":" + name,
+                    attributes);
+        } 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);
+        }
+    }
+}
diff --git a/src/java/org/apache/fop/accessibility/StructureTreeBuilder.java b/src/java/org/apache/fop/accessibility/StructureTreeBuilder.java
deleted file mode 100644 (file)
index 036502e..0000000
+++ /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 &lt;structure-tree&gt; or
-     * &lt;structureTree&gt; 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/StructureTreeBuildingFOEventHandler.java b/src/java/org/apache/fop/accessibility/StructureTreeBuildingFOEventHandler.java
deleted file mode 100644 (file)
index e3f9468..0000000
+++ /dev/null
@@ -1,840 +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.TransformerFactory;
-import javax.xml.transform.dom.DOMResult;
-import javax.xml.transform.sax.SAXTransformerFactory;
-import javax.xml.transform.sax.TransformerHandler;
-
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-import org.apache.fop.apps.FOUserAgent;
-import org.apache.fop.fo.DelegatingFOEventHandler;
-import org.apache.fop.fo.FOEventHandler;
-import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.extensions.ExtensionElementMapping;
-import org.apache.fop.fo.extensions.ExternalDocument;
-import org.apache.fop.fo.extensions.InternalElementMapping;
-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.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.StaticContent;
-import org.apache.fop.fo.properties.CommonAccessibilityHolder;
-import org.apache.fop.util.XMLUtil;
-
-/**
- * A class that builds the document's structure tree.
- */
-public class StructureTreeBuildingFOEventHandler extends DelegatingFOEventHandler {
-
-    private int idCounter;
-
-    private final StructureTree structureTree;
-
-    private TransformerHandler structureTreeDOMBuilder;
-
-    private DOMResult result;
-
-    /** Delegates to either {@link #actualStructureTreeBuilder} or {@link #eventSwallower}. */
-    private FOEventHandler structureTreeBuilder;
-
-    private FOEventHandler actualStructureTreeBuilder;
-
-    /** The descendants of some elements like fo:leader must be ignored. */
-    private final FOEventHandler eventSwallower;
-
-    private final class StructureTreeBuilder extends FOEventHandler {
-
-        public StructureTreeBuilder(FOUserAgent foUserAgent) {
-            super(foUserAgent);
-        }
-
-        @Override
-        public void startDocument() throws SAXException {
-        }
-
-        @Override
-        public void endDocument() throws SAXException {
-        }
-
-        @Override
-        public void startPageSequence(PageSequence pageSeq) {
-            SAXTransformerFactory transformerFactory =
-                    (SAXTransformerFactory) TransformerFactory.newInstance();
-            try {
-                structureTreeDOMBuilder = transformerFactory.newTransformerHandler();
-            } catch (TransformerConfigurationException e) {
-                throw new RuntimeException(e);
-            }
-            result = new DOMResult();
-            structureTreeDOMBuilder.setResult(result);
-            try {
-                structureTreeDOMBuilder.startDocument();
-            } catch (SAXException e) {
-                throw new RuntimeException(e);
-            }
-            startElement(pageSeq);
-        }
-
-        @Override
-        public void endPageSequence(PageSequence pageSeq) {
-            endElement(pageSeq);
-            try {
-                structureTreeDOMBuilder.endDocument();
-            } catch (SAXException e) {
-                throw new RuntimeException(e);
-            }
-            structureTree.addPageSequenceStructure(
-                    result.getNode().getFirstChild().getChildNodes());
-        }
-
-        @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) {
-            startElementWithID(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) {
-            startElementWithID(inl);
-        }
-
-        @Override
-        public void endInline(Inline inl) {
-            endElement(inl);
-        }
-
-        @Override
-        public void startTable(Table tbl) {
-            startElementWithID(tbl);
-        }
-
-        @Override
-        public void endTable(Table tbl) {
-            endElement(tbl);
-        }
-
-        @Override
-        public void startHeader(TableHeader header) {
-            startElementWithID(header);
-        }
-
-        @Override
-        public void endHeader(TableHeader header) {
-            endElement(header);
-        }
-
-        @Override
-        public void startFooter(TableFooter footer) {
-            startElementWithID(footer);
-        }
-
-        @Override
-        public void endFooter(TableFooter footer) {
-            endElement(footer);
-        }
-
-        @Override
-        public void startBody(TableBody body) {
-            startElementWithID(body);
-        }
-
-        @Override
-        public void endBody(TableBody body) {
-            endElement(body);
-        }
-
-        @Override
-        public void startRow(TableRow tr) {
-            startElementWithID(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));
-            }
-            startElementWithID(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);
-        }
-
-        private void startElement(FONode node) {
-            startElement(node, new AttributesImpl());
-        }
-
-        private void startElementWithID(FONode node) {
-            startElementWithID(node, new AttributesImpl());
-        }
-
-        private void startElementWithIDAndAltText(AbstractGraphics node) {
-            AttributesImpl attributes = new AttributesImpl();
-            addAttribute(attributes, ExtensionElementMapping.URI, "alt-text",
-                    ExtensionElementMapping.STANDARD_PREFIX, node.getAltText());
-            startElementWithID(node, attributes);
-        }
-
-        private void startElementWithID(FONode node, AttributesImpl attributes) {
-            String id = Integer.toHexString(idCounter++);
-            node.setPtr(id);
-            addAttribute(attributes,
-                    InternalElementMapping.URI, "ptr", InternalElementMapping.STANDARD_PREFIX, id);
-            startElement(node, attributes);
-        }
-
-        private void startElement(FONode node, AttributesImpl attributes) {
-            String localName = node.getLocalName();
-            if (node instanceof CommonAccessibilityHolder) {
-                addRole((CommonAccessibilityHolder) node, attributes);
-            }
-            try {
-                structureTreeDOMBuilder.startElement(node.getNamespaceURI(), localName,
-                        node.getNormalNamespacePrefix() + ":" + localName,
-                        attributes);
-            } catch (SAXException e) {
-                throw new RuntimeException(e);
-            }
-        }
-
-        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();
-            try {
-                structureTreeDOMBuilder.endElement(node.getNamespaceURI(), localName,
-                        node.getNormalNamespacePrefix() + ":" + localName);
-            } catch (SAXException e) {
-                throw new RuntimeException(e);
-            }
-        }
-
-    }
-
-    /**
-     * Creates a new instance.
-     *
-     * @param structureTree the object that will hold the structure tree
-     * @param delegate the FO event handler that must be wrapped by this instance
-     */
-    public StructureTreeBuildingFOEventHandler(StructureTree structureTree,
-            FOEventHandler delegate) {
-        super(delegate);
-        this.structureTree = structureTree;
-        this.actualStructureTreeBuilder = new StructureTreeBuilder(foUserAgent);
-        this.structureTreeBuilder = actualStructureTreeBuilder;
-        this.eventSwallower = new FOEventHandler(foUserAgent) { };
-    }
-
-    @Override
-    public void startDocument() throws SAXException {
-        structureTreeBuilder.startDocument();
-        super.startDocument();
-    }
-
-    @Override
-    public void endDocument() throws SAXException {
-        structureTreeBuilder.endDocument();
-        super.endDocument();
-    }
-
-    @Override
-    public void startPageSequence(PageSequence pageSeq) {
-        structureTreeBuilder.startPageSequence(pageSeq);
-        super.startPageSequence(pageSeq);
-    }
-
-    @Override
-    public void endPageSequence(PageSequence pageSeq) {
-        structureTreeBuilder.endPageSequence(pageSeq);
-        super.endPageSequence(pageSeq);
-    }
-
-    @Override
-    public void startPageNumber(PageNumber pagenum) {
-        structureTreeBuilder.startPageNumber(pagenum);
-        super.startPageNumber(pagenum);
-    }
-
-    @Override
-    public void endPageNumber(PageNumber pagenum) {
-        structureTreeBuilder.endPageNumber(pagenum);
-        super.endPageNumber(pagenum);
-    }
-
-    @Override
-    public void startPageNumberCitation(PageNumberCitation pageCite) {
-        structureTreeBuilder.startPageNumberCitation(pageCite);
-        super.startPageNumberCitation(pageCite);
-    }
-
-    @Override
-    public void endPageNumberCitation(PageNumberCitation pageCite) {
-        structureTreeBuilder.endPageNumberCitation(pageCite);
-        super.endPageNumberCitation(pageCite);
-    }
-
-    @Override
-    public void startPageNumberCitationLast(PageNumberCitationLast pageLast) {
-        structureTreeBuilder.startPageNumberCitationLast(pageLast);
-        super.startPageNumberCitationLast(pageLast);
-    }
-
-    @Override
-    public void endPageNumberCitationLast(PageNumberCitationLast pageLast) {
-        structureTreeBuilder.endPageNumberCitationLast(pageLast);
-        super.endPageNumberCitationLast(pageLast);
-    }
-
-    @Override
-    public void startFlow(Flow fl) {
-        structureTreeBuilder.startFlow(fl);
-        super.startFlow(fl);
-    }
-
-    @Override
-    public void endFlow(Flow fl) {
-        structureTreeBuilder.endFlow(fl);
-        super.endFlow(fl);
-    }
-
-    @Override
-    public void startBlock(Block bl) {
-        structureTreeBuilder.startBlock(bl);
-        super.startBlock(bl);
-    }
-
-    @Override
-    public void endBlock(Block bl) {
-        structureTreeBuilder.endBlock(bl);
-        super.endBlock(bl);
-    }
-
-    @Override
-    public void startBlockContainer(BlockContainer blc) {
-        structureTreeBuilder.startBlockContainer(blc);
-        super.startBlockContainer(blc);
-    }
-
-    @Override
-    public void endBlockContainer(BlockContainer blc) {
-        structureTreeBuilder.endBlockContainer(blc);
-        super.endBlockContainer(blc);
-    }
-
-    @Override
-    public void startInline(Inline inl) {
-        structureTreeBuilder.startInline(inl);
-        super.startInline(inl);
-    }
-
-    @Override
-    public void endInline(Inline inl) {
-        structureTreeBuilder.endInline(inl);
-        super.endInline(inl);
-    }
-
-    @Override
-    public void startTable(Table tbl) {
-        structureTreeBuilder.startTable(tbl);
-        super.startTable(tbl);
-    }
-
-    @Override
-    public void endTable(Table tbl) {
-        structureTreeBuilder.endTable(tbl);
-        super.endTable(tbl);
-    }
-
-    @Override
-    public void startColumn(TableColumn tc) {
-        structureTreeBuilder.startColumn(tc);
-        super.startColumn(tc);
-    }
-
-    @Override
-    public void endColumn(TableColumn tc) {
-        structureTreeBuilder.endColumn(tc);
-        super.endColumn(tc);
-    }
-
-    @Override
-    public void startHeader(TableHeader header) {
-        structureTreeBuilder.startHeader(header);
-        super.startHeader(header);
-    }
-
-    @Override
-    public void endHeader(TableHeader header) {
-        structureTreeBuilder.endHeader(header);
-        super.endHeader(header);
-    }
-
-    @Override
-    public void startFooter(TableFooter footer) {
-        structureTreeBuilder.startFooter(footer);
-        super.startFooter(footer);
-    }
-
-    @Override
-    public void endFooter(TableFooter footer) {
-        structureTreeBuilder.endFooter(footer);
-        super.endFooter(footer);
-    }
-
-    @Override
-    public void startBody(TableBody body) {
-        structureTreeBuilder.startBody(body);
-        super.startBody(body);
-    }
-
-    @Override
-    public void endBody(TableBody body) {
-        structureTreeBuilder.endBody(body);
-        super.endBody(body);
-    }
-
-    @Override
-    public void startRow(TableRow tr) {
-        structureTreeBuilder.startRow(tr);
-        super.startRow(tr);
-    }
-
-    @Override
-    public void endRow(TableRow tr) {
-        structureTreeBuilder.endRow(tr);
-        super.endRow(tr);
-    }
-
-    @Override
-    public void startCell(TableCell tc) {
-        structureTreeBuilder.startCell(tc);
-        super.startCell(tc);
-    }
-
-    @Override
-    public void endCell(TableCell tc) {
-        structureTreeBuilder.endCell(tc);
-        super.endCell(tc);
-    }
-
-    @Override
-    public void startList(ListBlock lb) {
-        structureTreeBuilder.startList(lb);
-        super.startList(lb);
-    }
-
-    @Override
-    public void endList(ListBlock lb) {
-        structureTreeBuilder.endList(lb);
-        super.endList(lb);
-    }
-
-    @Override
-    public void startListItem(ListItem li) {
-        structureTreeBuilder.startListItem(li);
-        super.startListItem(li);
-    }
-
-    @Override
-    public void endListItem(ListItem li) {
-        structureTreeBuilder.endListItem(li);
-        super.endListItem(li);
-    }
-
-    @Override
-    public void startListLabel(ListItemLabel listItemLabel) {
-        structureTreeBuilder.startListLabel(listItemLabel);
-        super.startListLabel(listItemLabel);
-    }
-
-    @Override
-    public void endListLabel(ListItemLabel listItemLabel) {
-        structureTreeBuilder.endListLabel(listItemLabel);
-        super.endListLabel(listItemLabel);
-    }
-
-    @Override
-    public void startListBody(ListItemBody listItemBody) {
-        structureTreeBuilder.startListBody(listItemBody);
-        super.startListBody(listItemBody);
-    }
-
-    @Override
-    public void endListBody(ListItemBody listItemBody) {
-        structureTreeBuilder.endListBody(listItemBody);
-        super.endListBody(listItemBody);
-    }
-
-    @Override
-    public void startStatic(StaticContent staticContent) {
-        structureTreeBuilder.startStatic(staticContent);
-        super.startStatic(staticContent);
-    }
-
-    @Override
-    public void endStatic(StaticContent statisContent) {
-        structureTreeBuilder.endStatic(statisContent);
-        super.endStatic(statisContent);
-    }
-
-    @Override
-    public void startMarkup() {
-        structureTreeBuilder.startMarkup();
-        super.startMarkup();
-    }
-
-    @Override
-    public void endMarkup() {
-        structureTreeBuilder.endMarkup();
-        super.endMarkup();
-    }
-
-    @Override
-    public void startLink(BasicLink basicLink) {
-        structureTreeBuilder.startLink(basicLink);
-        super.startLink(basicLink);
-    }
-
-    @Override
-    public void endLink(BasicLink basicLink) {
-        structureTreeBuilder.endLink(basicLink);
-        super.endLink(basicLink);
-    }
-
-    @Override
-    public void image(ExternalGraphic eg) {
-        structureTreeBuilder.image(eg);
-        super.image(eg);
-    }
-
-    @Override
-    public void pageRef() {
-        structureTreeBuilder.pageRef();
-        super.pageRef();
-    }
-
-    @Override
-    public void startInstreamForeignObject(InstreamForeignObject ifo) {
-        structureTreeBuilder.startInstreamForeignObject(ifo);
-        super.startInstreamForeignObject(ifo);
-    }
-
-    @Override
-    public void endInstreamForeignObject(InstreamForeignObject ifo) {
-        structureTreeBuilder.endInstreamForeignObject(ifo);
-        super.endInstreamForeignObject(ifo);
-    }
-
-    @Override
-    public void startFootnote(Footnote footnote) {
-        structureTreeBuilder.startFootnote(footnote);
-        super.startFootnote(footnote);
-    }
-
-    @Override
-    public void endFootnote(Footnote footnote) {
-        structureTreeBuilder.endFootnote(footnote);
-        super.endFootnote(footnote);
-    }
-
-    @Override
-    public void startFootnoteBody(FootnoteBody body) {
-        structureTreeBuilder.startFootnoteBody(body);
-        super.startFootnoteBody(body);
-    }
-
-    @Override
-    public void endFootnoteBody(FootnoteBody body) {
-        structureTreeBuilder.endFootnoteBody(body);
-        super.endFootnoteBody(body);
-    }
-
-    @Override
-    public void startLeader(Leader l) {
-        structureTreeBuilder = eventSwallower;
-        structureTreeBuilder.startLeader(l);
-        super.startLeader(l);
-    }
-
-    @Override
-    public void endLeader(Leader l) {
-        structureTreeBuilder.endLeader(l);
-        structureTreeBuilder = actualStructureTreeBuilder;
-        super.endLeader(l);
-    }
-
-    @Override
-    public void startWrapper(Wrapper wrapper) {
-        structureTreeBuilder.startWrapper(wrapper);
-        super.startWrapper(wrapper);
-    }
-
-    @Override
-    public void endWrapper(Wrapper wrapper) {
-        structureTreeBuilder.endWrapper(wrapper);
-        super.endWrapper(wrapper);
-    }
-
-    @Override
-    public void character(Character c) {
-        structureTreeBuilder.character(c);
-        super.character(c);
-    }
-
-    @Override
-    public void characters(char[] data, int start, int length) {
-        structureTreeBuilder.characters(data, start, length);
-        super.characters(data, start, length);
-    }
-
-    @Override
-    public void startExternalDocument(ExternalDocument document) {
-        structureTreeBuilder.startExternalDocument(document);
-        super.startExternalDocument(document);
-    }
-
-    @Override
-    public void endExternalDocument(ExternalDocument document) {
-        structureTreeBuilder.endExternalDocument(document);
-        super.endExternalDocument(document);
-    }
-
-}
diff --git a/src/java/org/apache/fop/accessibility/StructureTreeEventHandler.java b/src/java/org/apache/fop/accessibility/StructureTreeEventHandler.java
new file mode 100644 (file)
index 0000000..d84d078
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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 Map of node properties
+     */
+    void startNode(String name, Attributes attributes);
+
+    /**
+     * Ends a structure tree node.
+     * @param name The name of the structure tree node
+     */
+    void endNode(String name);
+
+    /**
+     * Ends a page sequence structure tree node.
+     */
+    void endPageSequence();
+}
index 071066e0437312e50779dba17d5f5c74441669fe..d5cc695c880daf4afa2f67016317d35d67d8bfbe 100644 (file)
@@ -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,7 @@ 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 +173,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 +677,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;
     }
 }
 
index f45245bfecffd3ff465001ae4af53771da52d84f..6448e7c5dfb8c1769669dd339315a5839fb4c368 100644 (file)
@@ -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
      */
index 2a1f14ab5d4f0ad9d305a961b99c1fb32f46f376..941f6cea2cf8b45b8767b957a2594f0ef7300bf0 100644 (file)
@@ -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) {
+    }
 }
index d2f52f991c3a746d301d8efa515aacfef65d5b93..545c9015df72da28355ac9316d60df7441948cb8 100644 (file)
@@ -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,8 @@ 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 +201,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 +281,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 {
index afec850f829ad4f40bae43d561d510c7cc71ef02..cef2552c83666d6253144678f480dbdc67f31ba0 100644 (file)
@@ -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) {
index cb4ded17fc58e5cf5811dd4a3aca307b6a8d40ed..ed85bd1c9cb2bf799394982a337e30e5409a15ec 100644 (file)
@@ -50,6 +50,7 @@ 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;
 
@@ -90,6 +91,16 @@ public abstract class DelegatingFOEventHandler extends FOEventHandler {
         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);
index 8069880c6f4cf1ed29a264224a6a9e76a7e7b369..c96c3b9334dee04bf62253f08221a1d009827fa2 100644 (file)
@@ -32,6 +32,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
      */
@@ -141,7 +144,7 @@ public class FOElementMapping extends ElementMapping {
 
     /** {@inheritDoc} */
     public String getStandardPrefix() {
-        return "fo";
+        return STANDARD_PREFIX;
     }
 
     /** {@inheritDoc} */
index 453edec351fe9a4d11590809a3ff7574691b0cfd..1f3514deaffd44b06f64d1cffb4273696b741632 100644 (file)
@@ -50,6 +50,7 @@ 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;
@@ -117,6 +118,12 @@ public abstract class FOEventHandler {
     public void endDocument() throws SAXException {
     }
 
+    public void startRoot(Root root) {
+    }
+
+    public void endRoot(Root root) {
+    }
+
     /**
      *
      * @param pageSeq PageSequence that is starting.
index 890138e5cda4f3746acaf244d28cf307945bcba6..b053692d694bda62574a349ca0b64aef60185a71 100644 (file)
@@ -33,8 +33,7 @@ import org.apache.commons.logging.LogFactory;
 
 import org.apache.xmlgraphics.util.QName;
 
-import org.apache.fop.accessibility.StructureTree;
-import org.apache.fop.accessibility.StructureTreeBuildingFOEventHandler;
+import org.apache.fop.accessibility.FO2StructureTreeConverter;
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.apps.FormattingResults;
@@ -107,9 +106,8 @@ public class FOTreeBuilder extends DefaultHandler {
         foEventHandler = foUserAgent.getRendererFactory().createFOEventHandler(
                 foUserAgent, outputFormat, stream);
         if (userAgent.isAccessibilityEnabled()) {
-            StructureTree structureTree = new StructureTree();
-            foEventHandler = new StructureTreeBuildingFOEventHandler(structureTree, foEventHandler);
-            userAgent.setStructureTree(structureTree);
+            foEventHandler = new FO2StructureTreeConverter(
+                    foUserAgent.getStructureTreeEventHandler(), foEventHandler);
         }
         builderContext = new FOTreeBuilderContext();
         builderContext.setPropertyListMaker(new PropertyListMaker() {
index c670d6e4256ccf1154d20b722c5b844461cd811a..6df6e95370e6c2df3f7622faa60aa6448a556dc9 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.fo.pagination;
 
 // java
 import java.util.List;
+import java.util.Locale;
 
 import org.xml.sax.Locator;
 
@@ -52,6 +53,7 @@ public class Root extends FObj implements CommonAccessibilityHolder {
     private BookmarkTree bookmarkTree = null;
     private List<Destination> destinationList;
     private List<PageSequence> pageSequences;
+    private Locale locale;
 
     // temporary until above list populated
     private boolean pageSequenceFound = false;
@@ -88,6 +90,24 @@ public class Root extends FObj implements CommonAccessibilityHolder {
         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} */
@@ -96,6 +116,7 @@ public class Root extends FObj implements CommonAccessibilityHolder {
             missingChildElementError("(layout-master-set, declarations?, "
                 + "bookmark-tree?, (page-sequence|fox:external-document)+)");
         }
+        foEventHandler.endRoot(this);
     }
 
     /**
@@ -343,4 +364,9 @@ public class Root extends FObj implements CommonAccessibilityHolder {
         return FO_ROOT;
     }
 
+
+    public Locale getLocale() {
+        return locale;
+    }
+
 }
index cbca3ea8f9c0f5f315e8b0ae7db95bf67352806d..e9f42d4b2217b0a1903ebc7a421972ed62160eb4 100644 (file)
@@ -353,25 +353,6 @@ public class PDFDocument {
         return this.root;
     }
 
-    /**
-     * 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.
      *
index fb45751056fae01017bb1e1c6ba1e785a09be8a6..b4b43c4220bc55997c0e597cf8d59cec49a7a33c 100644 (file)
@@ -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. */
index f71841005efbdd3fb56c51aee2ecbdc308d6dad2..177ca4b2dba4215ad1a18896f9a73405ac74f364 100644 (file)
@@ -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} */
@@ -254,10 +258,14 @@ public class PDFRoot extends PDFDictionary {
      * Sets the language identifier of the document.
      * @param lang the language identifier 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);
     }
 
index 4fb8cbcd50877ac3248f2fce9421ad159a9d3efc..6dfb0dc95214dc99425934b0f98d53c894c9046c 100644 (file)
@@ -21,7 +21,7 @@ package org.apache.fop.pdf;
 
 import java.util.Locale;
 
-import org.apache.fop.util.XMLUtil;
+import org.apache.fop.util.LanguageTags;
 
 /**
  * Class representing a PDF Structure Element.
@@ -145,7 +145,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));
     }
 
     /**
index 7ff236d160867eefd8491a1919645e9de5b60715..ec14e597c04eb193990eae88cd4ce12817650759 100644 (file)
@@ -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;
@@ -61,11 +62,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.Constants;
 import org.apache.fop.fonts.FontInfo;
@@ -152,6 +153,10 @@ public abstract class AbstractRenderer
         return false;
     }
 
+    /** {@inheritDoc} */
+    public void setDocumentLocale(Locale locale) {
+    }
+
     /**
      * {@inheritDoc}
      */
index 76f245251938dd8076518aeb7e63c56b1d71d3b2..599b8e2602c4d45a4e3fab2367c97e623b0decff 100644 (file)
@@ -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;
@@ -115,6 +116,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
index b156b6c3a1b685435ce118e614a2400c4b888150..ad4e6ad0e1e5403ef603de66a3f22248f36265d8 100644 (file)
 
 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;
 
 /**
@@ -52,6 +59,11 @@ public abstract class AbstractIFDocumentHandler implements IFDocumentHandler {
         return getContext().getUserAgent();
     }
 
+    /** {@inheritDoc} */
+    public StructureTreeEventHandler getStructureTreeEventHandler() {
+        return DummyStructureTreeEventHandler.INSTANCE;
+    }
+
     /** {@inheritDoc} */
     public IFDocumentNavigationHandler getDocumentNavigationHandler() {
         return null; //By default, this is not supported
@@ -65,6 +77,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
     }
-
 }
index 2b16b343d332f5108ab6b7e967b6183fd81af583..dba39bec09f94ac5698f37fe9a632bb45f9af529 100644 (file)
@@ -39,6 +39,7 @@ public interface IFConstants extends XMLConstants {
     String EL_HEADER = "header";
     /** element name trailer */
     String EL_TRAILER = "trailer";
+    String EL_LOCALE = "locale";
     /** element name page-sequence */
     String EL_PAGE_SEQUENCE = "page-sequence";
     /** element name page */
index af1451fe384020add79c7bc5e1300b722b5f2db6..6cb8f2795134c121d28642bcaea5b04839327776 100644 (file)
 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()
@@ -117,6 +120,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
@@ -151,6 +159,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
@@ -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
 }
index 313cee68547718ce68c68e081f6b0a7ccae27a81..8f0bb88ecea0fc257654f810d9827a00cff250bd 100644 (file)
@@ -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,7 +49,7 @@ 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.StructureTreeEventHandler;
 import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.fo.ElementMapping;
 import org.apache.fop.fo.ElementMappingRegistry;
@@ -62,7 +63,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 +154,40 @@ 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 final class StructureTreeHandler extends DefaultHandler {
 
-            private StructureTreeBuilderWrapper()
-                    throws SAXException {
-                super(structureTreeBuilder.getHandlerForNextPageSequence());
+            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)) {
+                    structureTreeEventHandler.startNode(localName, attributes);
+                }
+            }
+
+            @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 +197,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 +215,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) {
@@ -231,10 +244,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();
@@ -299,6 +315,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 +434,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 +456,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);
index 24a2e8a75809b11ec0888e0bd1579911160aa695..d217646f6dc913b75d075179e8b96d11218f2b47 100644 (file)
@@ -51,6 +51,7 @@ import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema;
 
 import org.apache.fop.Version;
 import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.apps.MimeConstants;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.AreaTreeObject;
@@ -70,10 +71,10 @@ import org.apache.fop.area.inline.ForeignObject;
 import org.apache.fop.area.inline.Image;
 import org.apache.fop.area.inline.InlineArea;
 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.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.datatypes.URISpecification;
 import org.apache.fop.fo.extensions.ExtensionAttachment;
@@ -227,7 +228,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;
     }
 
@@ -294,6 +299,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) {
index 471a982ca6288c814f50294a17a480418ef07aec..a4431b972ca7703af98ae3cd8d626bcf74491770 100644 (file)
@@ -31,16 +31,13 @@ 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.fonts.FontInfo;
 import org.apache.fop.render.PrintRendererConfigurator;
 import org.apache.fop.render.RenderingContext;
@@ -54,9 +51,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 +70,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
 
     private String currentID = "";
 
-    /**
-     * Default constructor.
-     */
-    public IFSerializer() {
-    }
+    private IFStructureTreeBuilder structureTreeBuilder;
 
     /** {@inheritDoc} */
     @Override
@@ -150,6 +145,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 {
@@ -166,6 +169,19 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
         }
     }
 
+    @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 +243,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 +260,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);
@@ -806,5 +817,4 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
             throw new IFException("SAX error serializing object", e);
         }
     }
-
 }
index 699fd054918fca58826311db21e055de6cfc954e..c0060ab8ee410e514e8d8a279a8dd21a6392bfad 100644 (file)
@@ -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 (file)
index 0000000..6645768
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * 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.DefaultHandler;
+
+import org.apache.fop.accessibility.StructureTree2SAXEventAdapter;
+import org.apache.fop.accessibility.StructureTreeEventHandler;
+
+/**
+ * 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 {
+
+    private StructureTreeEventHandler delegate;
+
+    private final List<SAXEventRecorder> pageSequenceEventRecorders = new ArrayList<SAXEventRecorder>();
+
+    /**
+     * 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);
+    }
+
+    /** {@inheritDoc} */
+    public void startPageSequence(Locale locale) {
+        SAXEventRecorder eventRecorder = new SAXEventRecorder();
+        pageSequenceEventRecorders.add(eventRecorder);
+        delegate = StructureTree2SAXEventAdapter.newInstance(eventRecorder);
+        delegate.startPageSequence(locale);
+    }
+
+    /** {@inheritDoc} */
+    public void endPageSequence() {
+         delegate.endPageSequence();
+    }
+
+    /** {@inheritDoc} */
+    public void startNode(String name, Attributes attributes) {
+        delegate.startNode(name, attributes);
+    }
+
+    /** {@inheritDoc} */
+    public void endNode(String name) {
+        delegate.endNode(name);
+    }
+
+    /** 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 {
+
+            protected final String prefix;
+            protected 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 {
+
+            protected 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);
+            }
+        }
+    }
+}
index 3d89e812e345c1e2f9c6118ffad22dcdce7e33f8..64c876fb12f156bf196fe1639884bd0e16bd857d 100644 (file)
 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;
@@ -93,6 +96,11 @@ public class IFDocumentHandlerProxy implements IFDocumentHandler {
         return this.delegate.getDocumentNavigationHandler();
     }
 
+    /** {@inheritDoc} */
+    public StructureTreeEventHandler getStructureTreeEventHandler() {
+        return DummyStructureTreeEventHandler.INSTANCE;
+    }
+
     /** {@inheritDoc} */
     public void setResult(Result result) throws IFException {
         this.delegate.setResult(result);
@@ -103,6 +111,12 @@ public class IFDocumentHandlerProxy implements IFDocumentHandler {
         this.delegate.startDocument();
     }
 
+    /** {@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
+}
index 26595bca164c1efe49b4587300e4cd9024cdbf4a..c773b133916ed0d6d1d66f9795f87f6d7861145b 100644 (file)
@@ -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,9 @@ 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 +170,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 +178,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 +225,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())) {
index c1d959dd1ae37d3a5ae211798e30729c4f528c5d..5be8c3001478dfe632b20e5e293d704ff5d79e5a 100644 (file)
@@ -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;
+    }
 }
index f4ba03d5b936642a02196724d4e935582f8e522d..1186db3ae518d8a565cc6b1679ee54aa81798437 100644 (file)
@@ -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;
     }
 
index ce86f48496d9a4ff2c26ca64224c7ed99c5bfa17..d49ef4a3e96e1dc6f67cc3887226ca2b42b740d4 100644 (file)
@@ -23,12 +23,6 @@ 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,12 +47,10 @@ 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 Map<String, PDFStructElem> structTreeMap = new HashMap<String, PDFStructElem>();
 
     private final PDFParentTree parentTree = new PDFParentTree();
 
@@ -108,23 +100,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,50 +117,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) {
-        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");
-            }
-        }
-        Node attr = node.getAttributes().getNamedItemNS(InternalElementMapping.URI, "ptr");
-        if (attr != null) {
-            String ptr = attr.getNodeValue();
-            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() {
@@ -301,4 +243,8 @@ class PDFLogicalStructureHandler {
         parent.addKid(contentItem);
     }
 
+    void addStructurePointer(String ptr, PDFStructElem structElem) {
+        structTreeMap.put(ptr, structElem);
+    }
+
 }
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 (file)
index 0000000..8ec10b2
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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.StructureTreeEventHandler;
+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.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 void startNode(String name, Attributes attributes) {
+        PDFStructElem parent = ancestors.getFirst();
+        String role = attributes.getValue("role");
+        PDFStructElem created = pdfFactory.makeStructureElement(
+                FOToPDFRoleMap.mapFormattingObject(name, role, parent,
+                        eventBroadcaster), parent);
+        if (ancestors.size() <= 2) { // TODO remove
+            parent.addKid(created);
+        }
+        String ptr = attributes.getValue(InternalElementMapping.URI, "ptr");
+        if (ptr != null) {
+            logicalStructureHandler.addStructurePointer(ptr, created);
+        }
+
+        if (name.equals("external-graphic") || name.equals("instream-foreign-object")) {
+            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);
+    }
+
+    public void endNode(String name) {
+        ancestors.removeFirst();
+    }
+
+}
index 21d28042dfce20708331bdbc4876b25a9ff7d2ca..2a62d2cea334e1fa12eb23c31edbe5f70616e6c2 100644 (file)
@@ -35,9 +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;
@@ -75,23 +72,20 @@ 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.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.
@@ -461,29 +455,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 (file)
index 0000000..070fa15
--- /dev/null
@@ -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
+     * @throws NullPointerException if the specified locale is null
+     */
+    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.
+     *
+     * @throws NullPointerException if the specified language tag is null
+     */
+    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]);
+        }
+    }
+}
index 0a55ce573f902bf62b7248562e4db032d10b03a5..d4397c2c7576d1b86839c581c97402ccbda37263 100644 (file)
@@ -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/test/accessibility/config-painter.xconf b/test/accessibility/config-painter.xconf
deleted file mode 100644 (file)
index 8c5dc2b..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<fop version="1.0">
-  <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-renderer.xconf b/test/accessibility/config-renderer.xconf
deleted file mode 100644 (file)
index 4b55e39..0000000
+++ /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/fop.xconf b/test/accessibility/fop.xconf
new file mode 100644 (file)
index 0000000..8c5dc2b
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fop version="1.0">
+  <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>
index c7c27f9d14d20d4f34bd8a098b37bc2641abcd6f..97b304c5396c53a5d3cd3ceaa98b451c2288f94b 100644 (file)
Binary files a/test/accessibility/pdf/background-image_jpg_repeat.pdf and b/test/accessibility/pdf/background-image_jpg_repeat.pdf differ
index 080f0b876d3239358c3f91b31882ee2ea07575b3..e1d7f93406611a7cf77e0de44b55acbc22dbf205 100644 (file)
Binary files a/test/accessibility/pdf/background-image_jpg_single.pdf and b/test/accessibility/pdf/background-image_jpg_single.pdf differ
index a10e1357e5a596aa4045de87a7a82ccae4b6a230..9f5e7ff124e8149f2b6381b8cec0b4fbac3f8859 100644 (file)
Binary files a/test/accessibility/pdf/background-image_png_repeat.pdf and b/test/accessibility/pdf/background-image_png_repeat.pdf differ
index 1e9ff00b46b9ee8bad6fda1665e241ff2e42d6c7..81156e18dc9e99bbd7b1347ceaf0139fe4135f42 100644 (file)
Binary files a/test/accessibility/pdf/background-image_png_single.pdf and b/test/accessibility/pdf/background-image_png_single.pdf differ
index c641fbbe54635b52e31dcefe77d4c00b49cd6a23..0e438958dcd21a35f596ef38bad50fb75a2d0314 100644 (file)
Binary files a/test/accessibility/pdf/background-image_svg_repeat.pdf and b/test/accessibility/pdf/background-image_svg_repeat.pdf differ
index 09a9ad89562c8dd1478838415b5dd145f202a17d..a46b6df384e678f7e4e0c27d30a1bd4299c6bf2d 100644 (file)
Binary files a/test/accessibility/pdf/background-image_svg_single.pdf and b/test/accessibility/pdf/background-image_svg_single.pdf differ
index caf7a41f82189ec3ac7f42e5887d893da654e580..a42bb2a976b5bb429ba0e1fae3e4628b39f1d1d1 100644 (file)
Binary files a/test/accessibility/pdf/complete.pdf and b/test/accessibility/pdf/complete.pdf differ
index 1336f99ebb811c624b0c7b977e4c322ccd9b2ffe..6b70c268d983c212f27197b3a6887850ec4a902d 100644 (file)
Binary files a/test/accessibility/pdf/image_jpg.pdf and b/test/accessibility/pdf/image_jpg.pdf differ
index b22c157b02ad1b3bcb6545da546bd11af44c74f5..d63db6708e2bc6489a05adbca4d8a606420fddd2 100644 (file)
Binary files a/test/accessibility/pdf/image_png.pdf and b/test/accessibility/pdf/image_png.pdf differ
index 1f5393c435f50db724e27356f53aa8f177ab4f1b..94720f5648923007ef43197374786ae6eedd5465 100644 (file)
Binary files a/test/accessibility/pdf/image_svg.pdf and b/test/accessibility/pdf/image_svg.pdf differ
index 599b4c5ffb13c7ac0853008719e0bd9709267159..4b6eb082763d0daed1c4c87e217a142f455f89fd 100644 (file)
Binary files a/test/accessibility/pdf/image_wmf.pdf and b/test/accessibility/pdf/image_wmf.pdf differ
index cf77a5c96969fddc7933bb8df2f7ee887f79d755..4a6c25253dac586b8707175e1dee25072ddf2980 100644 (file)
Binary files a/test/accessibility/pdf/leader.pdf and b/test/accessibility/pdf/leader.pdf differ
index ee37c72d65f450e9a0714137174e33ec9245ec12..b8d4e3be3a71e7f7c36f870afed82963ece8e547 100644 (file)
Binary files a/test/accessibility/pdf/links.pdf and b/test/accessibility/pdf/links.pdf differ
index 1eaaf1a9dcba356b55fbc06b3b2906444d4fab17..ec5d5f5366923bd5a1f3e6790fee839568f072b5 100644 (file)
Binary files a/test/accessibility/pdf/role.pdf and b/test/accessibility/pdf/role.pdf differ
index f8b1c9d2ceb889b738b7d071e691ca2589f5740d..d906930668912b0b56847b26d459772a7cc9c39b 100644 (file)
Binary files a/test/accessibility/pdf/role_non-standard.pdf and b/test/accessibility/pdf/role_non-standard.pdf differ
index 2ad8a12f22eb523cdd344d5b0263a9b00ec24e89..0b1e1ee9ff6a25a533a25779a41ac05536606d7d 100644 (file)
Binary files a/test/accessibility/pdf/text_1.pdf and b/test/accessibility/pdf/text_1.pdf differ
index 21ad6d0d84641dc9672e2ed4df0f9a63fc66f82a..3330cc902cf92dc48b92be253378cb747d9957dd 100644 (file)
Binary files a/test/accessibility/pdf/text_2.pdf and b/test/accessibility/pdf/text_2.pdf differ
index d5c61c040c407b4d2bde91790533d908ea504bf0..a268dbb63bfabfaa90e01cd7a5ec96b2b67244c0 100644 (file)
Binary files a/test/accessibility/pdf/text_font-embedding.pdf and b/test/accessibility/pdf/text_font-embedding.pdf differ
index 1dacd96146b814c0309f6c2842ff122102efe726..840b04917c7a5f5a89ad0743f50f75d9344094a9 100644 (file)
@@ -68,7 +68,8 @@ import org.apache.fop.traits.MinOptMaxTest;
     CharactersetEncoderTest.class,
     org.apache.fop.render.afp.AFPTestSuite.class,
     PSTestSuite.class,
-    MinOptMaxTest.class
+    MinOptMaxTest.class,
+    org.apache.fop.render.intermediate.IFStructureTreeBuilderTestCase.class
 })
 public class StandardTestSuite {
 }
diff --git a/test/java/org/apache/fop/accessibility/FO2StructureTreeConverterTestCase.java b/test/java/org/apache/fop/accessibility/FO2StructureTreeConverterTestCase.java
new file mode 100644 (file)
index 0000000..f8719ed
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * 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 static org.junit.Assert.assertTrue;
+
+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.StreamSource;
+
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.Difference;
+import org.custommonkey.xmlunit.DifferenceConstants;
+import org.custommonkey.xmlunit.DifferenceListener;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+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 static class IgnoringPtrDifferenceListener implements DifferenceListener {
+
+        public int differenceFound(Difference difference) {
+            switch (difference.getId()) {
+            case DifferenceConstants.ELEMENT_NUM_ATTRIBUTES_ID:
+                return RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR;
+            case DifferenceConstants.ATTR_NAME_NOT_FOUND_ID:
+                String additionalAttribute = difference.getTestNodeDetail().getValue();
+                if (additionalAttribute.equals("ptr")) {
+                    return RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR;
+                } else {
+                    return RETURN_ACCEPT_DIFFERENCE;
+                }
+            default:
+                return RETURN_ACCEPT_DIFFERENCE;
+            }
+        }
+
+        public void skippedComparison(Node control, Node test) {
+            throw new UnsupportedOperationException("Not implemented");
+        }
+    }
+
+    private static final String STRUCTURE_TREE_SEQUENCE_NAME = "structure-tree-sequence";
+
+    @Test
+    public void testConverter() throws Exception {
+        DOMResult expectedStructureTree = loadExpectedStructureTree();
+        DOMResult actualStructureTree = buildActualStructureTree();
+        final Diff diff = createDiff(expectedStructureTree, actualStructureTree);
+        assertTrue(diff.toString(), diff.similar());
+    }
+
+    private static DOMResult loadExpectedStructureTree() {
+        DOMResult expectedStructureTree = new DOMResult();
+        runXSLT(getXsltInputStream(), getFoInputStream(), expectedStructureTree);
+        return expectedStructureTree;
+    }
+
+    private static InputStream getXsltInputStream() {
+        return FO2StructureTreeConverterTestCase.class.getResourceAsStream("foToIfStructureTree.xsl");
+    }
+
+    private static InputStream getFoInputStream() {
+        return FO2StructureTreeConverterTestCase.class.getResourceAsStream(
+                "/org/apache/fop/fo/complete_document.fo");
+    }
+
+    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 static DOMResult buildActualStructureTree() throws Exception {
+        DOMResult actualStructureTree = new DOMResult();
+        createStructureTreeFromDocument(getFoInputStream(), actualStructureTree);
+        return actualStructureTree;
+    }
+
+    private static void createStructureTreeFromDocument(InputStream foInputStream,
+            DOMResult domResult) throws Exception {
+        TransformerHandler tHandler = createTransformerHandler(domResult);
+        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(DOMResult 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));
+        diff.overrideDifferenceListener(new IgnoringPtrDifferenceListener());
+        return diff;
+    }
+
+    private static Document getDocument(DOMResult result) {
+        return (Document) result.getNode();
+    }
+}
diff --git a/test/java/org/apache/fop/accessibility/fo2StructureTree.xsl b/test/java/org/apache/fop/accessibility/fo2StructureTree.xsl
new file mode 100644 (file)
index 0000000..5c9c561
--- /dev/null
@@ -0,0 +1,116 @@
+<?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:fox="http://xmlgraphics.apache.org/fop/extensions"
+  xmlns:foi="http://xmlgraphics.apache.org/fop/internal">
+
+  <xsl:output method="xml" indent="no"/>
+
+  <xsl:template name="copy">
+    <xsl:copy>
+      <xsl:apply-templates select="@*|node()"/>
+    </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:static-content|fo:flow">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <!-- Block-level Formatting Objects -->
+  <xsl:template match="fo:block|fo:block-container">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <!-- Inline-level Formatting Objects -->
+  <xsl:template match="fo:character|fo:inline|fo:inline-container">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <xsl:template match="fo:external-graphic|fo:instream-foreign-object">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <xsl:template match="fo:page-number|fo:page-number-citation|fo:page-number-citation-last">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <!-- Formatting Objects for Tables -->
+  <xsl:template match="fo:table-and-caption|fo:table-caption|fo:table">
+    <xsl:call-template name="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>
+
+  <!-- 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="copy"/>
+  </xsl:template>
+
+  <!-- Dynamic Effects: Link and Multi Formatting Objects -->
+  <xsl:template match="fo:basic-link">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <!-- Out-of-Line Formatting Objects -->
+  <xsl:template match="fo:float|fo:footnote|fo:footnote-body">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <!-- Other Formatting Objects -->
+  <xsl:template match="fo:wrapper|fo:marker">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+
+  <!-- Discard descendants of fo:leader -->
+  <xsl:template match="fo:leader"/>
+
+
+  <!-- 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 -->
+  <xsl:template match="text()"/>
+
+</xsl:stylesheet>
diff --git a/test/java/org/apache/fop/fo/DelegatingFOEventHandlerTestCase.fo b/test/java/org/apache/fop/fo/DelegatingFOEventHandlerTestCase.fo
deleted file mode 100644 (file)
index 74afd6c..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-<?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">
-          <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>
index faa88e38edc6ba383df022de59976a0abd8935b2..313379e02f3909173ae40228ea3a8b260b377bc8 100644 (file)
@@ -71,6 +71,7 @@ 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;
 
 /**
@@ -97,12 +98,22 @@ public class DelegatingFOEventHandlerTestCase {
 
         @Override
         public void startDocument() throws SAXException {
-            actualEvents.add("start root");
+            actualEvents.add("start document");
         }
 
         @Override
         public void endDocument() throws SAXException {
-            actualEvents.add("end   root");
+            actualEvents.add("end   document");
+        }
+
+        @Override
+        public void startRoot(Root root) {
+            startElement(root);
+        }
+
+        @Override
+        public void endRoot(Root root) {
+            endElement(root);
         }
 
         @Override
@@ -417,9 +428,7 @@ public class DelegatingFOEventHandlerTestCase {
     }
 
     private void loadDocument() {
-        Class<?> clazz = getClass();
-        String documentName = clazz.getSimpleName() + ".fo";
-        document = clazz.getResourceAsStream(documentName);
+        document = getClass().getResourceAsStream("complete_document.fo");
     }
 
     private void loadExpectedEvents() throws IOException {
index 4706866a40e016eab3d51af5af25ca082a18d81b..a7574e49dbb02d367e9a448d956d0476ccbea05b 100644 (file)
@@ -108,13 +108,29 @@ public final class FODocumentParser {
      * @throws LoadingException if an error occurs when parsing the document
      */
     public void parse(InputStream document) throws FOPException, LoadingException {
-        FOUserAgent foUserAgent = createFOUserAgent();
+        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);
     }
 
-    private FOUserAgent createFOUserAgent() {
+    /**
+     * Creates a new {@link FOUserAgent}.
+     * @return It
+     */
+    public FOUserAgent createFOUserAgent() {
         FOUserAgent userAgent = FOP_FACTORY.newFOUserAgent();
         FOEventHandler foEventHandler = foEventHandlerFactory.newFOEventHandler(userAgent);
         userAgent.setFOEventHandlerOverride(foEventHandler);
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 (file)
index 0000000..5a34e9e
--- /dev/null
@@ -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>
index 8edc0ee49aa81da9eb6aab9776a99298b9dd4cc6..6cf42c98464ce91e4b0e2647744038a701fea033 100644 (file)
@@ -6,16 +6,22 @@
 
   <xsl:output indent="yes" omit-xml-declaration="yes"/>
 
-  <xsl:template match="fo:root">
+  <xsl:template match="/">
     <event>
-      <xsl:text>start root</xsl:text>
+      <xsl:text>start document</xsl:text>
     </event>
-    <xsl:apply-templates select="fo:page-sequence"/>
+    <xsl:apply-templates/>
     <event>
-      <xsl:text>end   root</xsl:text>
+      <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">
index 45f6a61ab1f18248913cec50e5c2f4b4a3707449..352a39713311551dd4e268f797c09af960d75182 100644 (file)
@@ -34,6 +34,7 @@ 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
@@ -51,7 +52,7 @@ public class CommonAccessibilityHolderTestCase {
     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();
+        UnimplementedWarningNeutralizer.neutralizeUnimplementedWarning();
 
         IMPLEMENTATIONS.add(org.apache.fop.fo.flow.BasicLink.class);
         IMPLEMENTATIONS.add(org.apache.fop.fo.flow.Block.class);
index 52d322e3e3d001e614187298d2b71547aed87a36..6ac20c5565e24e8fdfa07375bbc37ed3d2e50630 100644 (file)
@@ -48,6 +48,9 @@ import org.apache.fop.render.intermediate.IFSerializer;
 @RunWith(Parameterized.class)
 public class IFParserTestCase extends AbstractIFTestCase {
 
+    /** 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 AbstractIFTestCase {
      */
     @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 (file)
index 0000000..d7db7db
--- /dev/null
@@ -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 = {"ptr", "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 (file)
index 0000000..c5aad66
--- /dev/null
@@ -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/LanguageTagsTestCase.java b/test/java/org/apache/fop/util/LanguageTagsTestCase.java
new file mode 100644 (file)
index 0000000..f7383c7
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+import java.util.Locale;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link LanguageTags}.
+ */
+public class LanguageTagsTestCase {
+
+    @Test(expected = NullPointerException.class)
+    public void toLanguageTagRejectsNull() {
+        LanguageTags.toLanguageTag(null);
+    }
+
+    @Test
+    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 {
+        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"));
+    }
+}
diff --git a/test/java/org/apache/fop/util/XMLUtilTestCase.java b/test/java/org/apache/fop/util/XMLUtilTestCase.java
deleted file mode 100644 (file)
index 4c1b999..0000000
+++ /dev/null
@@ -1,50 +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.util;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import java.util.Locale;
-
-import org.junit.Test;
-
-/**
- * Tests {@link XMLUtil}.
- */
-public class XMLUtilTestCase {
-
-    @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")));
-    }
-
-    @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"));
-    }
-}