]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Bugzilla 53778: When PDF accessibility is enabled, the contents for the different...
authorVincent Hennebert <vhennebert@apache.org>
Fri, 24 Aug 2012 14:10:39 +0000 (14:10 +0000)
committerVincent Hennebert <vhennebert@apache.org>
Fri, 24 Aug 2012 14:10:39 +0000 (14:10 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1376923 13f79535-47bb-0310-9956-ffa450edef68

30 files changed:
src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java
src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java [deleted file]
src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java
src/java/org/apache/fop/fo/DelegatingFOEventHandler.java
src/java/org/apache/fop/fo/FOEventHandler.java
src/java/org/apache/fop/fo/FOPropertyMapping.java
src/java/org/apache/fop/fo/pagination/Flow.java
src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java
src/java/org/apache/fop/fo/pagination/StaticContent.java
src/java/org/apache/fop/pdf/PDFDocument.java
src/java/org/apache/fop/pdf/PDFStructElem.java
src/java/org/apache/fop/pdf/PDFStructTreeRoot.java
src/java/org/apache/fop/pdf/StandardStructureTypes.java
src/java/org/apache/fop/pdf/StructureHierarchyMember.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java [deleted file]
src/java/org/apache/fop/render/pdf/PDFEventProducer.java
src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java
src/java/org/apache/fop/render/pdf/PageSequenceStructElem.java [new file with mode: 0644]
src/java/org/apache/fop/render/pdf/TableStructElem.java [new file with mode: 0644]
status.xml
test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java
test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl
test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl [deleted file]
test/java/org/apache/fop/fo/pagination/LayoutMasterSetTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/fo/pagination/side-regions.fo [new file with mode: 0644]
test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java
test/pdf/accessibility/pdf/role.pdf
test/pdf/accessibility/pdf/role_non-standard.pdf
test/pdf/accessibility/pdf/side-regions.pdf [new file with mode: 0644]
test/pdf/accessibility/side-regions.fo [new file with mode: 0644]

index 27469d6e067bb92a2121febf3a5b6a343129026f..aaf112cb50b659c4719596e19b0018afe05c310b 100644 (file)
@@ -72,8 +72,6 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler {
 
     private final Stack<FOEventHandler> converters = new Stack<FOEventHandler>();
 
-    private final Stack<FOEventRecorder> tableFooterRecorders = new Stack<FOEventRecorder>();
-
     private final FOEventHandler structureTreeEventTrigger;
 
     /** The descendants of some elements like fo:leader must be ignored. */
@@ -165,6 +163,20 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler {
         super.endPageNumberCitationLast(pageLast);
     }
 
+    @Override
+    public void startStatic(StaticContent staticContent) {
+        handleStartArtifact(staticContent);
+        converter.startStatic(staticContent);
+        super.startStatic(staticContent);
+    }
+
+    @Override
+    public void endStatic(StaticContent staticContent) {
+        converter.endStatic(staticContent);
+        handleEndArtifact(staticContent);
+        super.endStatic(staticContent);
+    }
+
     @Override
     public void startFlow(Flow fl) {
         converter.startFlow(fl);
@@ -216,16 +228,11 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler {
     @Override
     public void startTable(Table tbl) {
         converter.startTable(tbl);
-        tableFooterRecorders.push(null);
         super.startTable(tbl);
     }
 
     @Override
     public void endTable(Table tbl) {
-        FOEventRecorder tableFooterRecorder = tableFooterRecorders.pop();
-        if (tableFooterRecorder != null) {
-            tableFooterRecorder.replay(converter);
-        }
         converter.endTable(tbl);
         super.endTable(tbl);
     }
@@ -256,8 +263,6 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler {
 
     @Override
     public void startFooter(TableFooter footer) {
-        converters.push(converter);
-        converter = new FOEventRecorder();
         converter.startFooter(footer);
         super.startFooter(footer);
     }
@@ -265,10 +270,6 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler {
     @Override
     public void endFooter(TableFooter footer) {
         converter.endFooter(footer);
-        /* Replace the dummy table footer with the real one. */
-        tableFooterRecorders.pop();
-        tableFooterRecorders.push((FOEventRecorder) converter);
-        converter = converters.pop();
         super.endFooter(footer);
     }
 
@@ -356,20 +357,6 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler {
         super.endListBody(listItemBody);
     }
 
-    @Override
-    public void startStatic(StaticContent staticContent) {
-        handleStartArtifact(staticContent);
-        converter.startStatic(staticContent);
-        super.startStatic(staticContent);
-    }
-
-    @Override
-    public void endStatic(StaticContent statisContent) {
-        converter.endStatic(statisContent);
-        handleEndArtifact(statisContent);
-        super.endStatic(statisContent);
-    }
-
     @Override
     public void startMarkup() {
         converter.startMarkup();
diff --git a/src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java b/src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java
deleted file mode 100644 (file)
index b2b1804..0000000
+++ /dev/null
@@ -1,508 +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.fo;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.fop.fo.FOEventHandler;
-import org.apache.fop.fo.FOText;
-import org.apache.fop.fo.flow.BasicLink;
-import org.apache.fop.fo.flow.Block;
-import org.apache.fop.fo.flow.BlockContainer;
-import org.apache.fop.fo.flow.Character;
-import org.apache.fop.fo.flow.ExternalGraphic;
-import org.apache.fop.fo.flow.Footnote;
-import org.apache.fop.fo.flow.FootnoteBody;
-import org.apache.fop.fo.flow.Inline;
-import org.apache.fop.fo.flow.InstreamForeignObject;
-import org.apache.fop.fo.flow.Leader;
-import org.apache.fop.fo.flow.ListBlock;
-import org.apache.fop.fo.flow.ListItem;
-import org.apache.fop.fo.flow.ListItemBody;
-import org.apache.fop.fo.flow.ListItemLabel;
-import org.apache.fop.fo.flow.PageNumber;
-import org.apache.fop.fo.flow.PageNumberCitation;
-import org.apache.fop.fo.flow.PageNumberCitationLast;
-import org.apache.fop.fo.flow.Wrapper;
-import org.apache.fop.fo.flow.table.Table;
-import org.apache.fop.fo.flow.table.TableBody;
-import org.apache.fop.fo.flow.table.TableCell;
-import org.apache.fop.fo.flow.table.TableColumn;
-import org.apache.fop.fo.flow.table.TableFooter;
-import org.apache.fop.fo.flow.table.TableHeader;
-import org.apache.fop.fo.flow.table.TableRow;
-
-final class FOEventRecorder extends FOEventHandler {
-
-    private interface Event {
-        void replay(FOEventHandler target);
-    }
-
-    private final List<Event> events = new ArrayList<Event>();
-
-    public void replay(FOEventHandler target) {
-        for (Event event : events) {
-            event.replay(target);
-        }
-    }
-
-    @Override
-    public void startPageNumber(final PageNumber pagenum) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startPageNumber(pagenum);
-            }
-        });
-    }
-
-    @Override
-    public void endPageNumber(final PageNumber pagenum) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endPageNumber(pagenum);
-            }
-        });
-    }
-
-    @Override
-    public void startPageNumberCitation(final PageNumberCitation pageCite) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startPageNumberCitation(pageCite);
-            }
-        });
-    }
-
-    @Override
-    public void endPageNumberCitation(final PageNumberCitation pageCite) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endPageNumberCitation(pageCite);
-            }
-        });
-    }
-
-    @Override
-    public void startPageNumberCitationLast(final PageNumberCitationLast pageLast) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startPageNumberCitationLast(pageLast);
-            }
-        });
-    }
-
-    @Override
-    public void endPageNumberCitationLast(final PageNumberCitationLast pageLast) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endPageNumberCitationLast(pageLast);
-            }
-        });
-    }
-
-    @Override
-    public void startBlock(final Block bl) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startBlock(bl);
-            }
-        });
-    }
-
-    @Override
-    public void endBlock(final Block bl) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endBlock(bl);
-            }
-        });
-    }
-
-    @Override
-    public void startBlockContainer(final BlockContainer blc) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startBlockContainer(blc);
-            }
-        });
-    }
-
-    @Override
-    public void endBlockContainer(final BlockContainer blc) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endBlockContainer(blc);
-            }
-        });
-    }
-
-    @Override
-    public void startInline(final Inline inl) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startInline(inl);
-            }
-        });
-    }
-
-    @Override
-    public void endInline(final Inline inl) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endInline(inl);
-            }
-        });
-    }
-
-    @Override
-    public void startTable(final Table tbl) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startTable(tbl);
-            }
-        });
-    }
-
-    @Override
-    public void endTable(final Table tbl) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endTable(tbl);
-            }
-        });
-    }
-
-    @Override
-    public void startColumn(final TableColumn tc) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startColumn(tc);
-            }
-        });
-    }
-
-    @Override
-    public void endColumn(final TableColumn tc) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endColumn(tc);
-            }
-        });
-    }
-
-    @Override
-    public void startHeader(final TableHeader header) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startHeader(header);
-            }
-        });
-    }
-
-    @Override
-    public void endHeader(final TableHeader header) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endHeader(header);
-            }
-        });
-    }
-
-    @Override
-    public void startFooter(final TableFooter footer) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startFooter(footer);
-            }
-        });
-    }
-
-    @Override
-    public void endFooter(final TableFooter footer) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endFooter(footer);
-            }
-        });
-    }
-
-    @Override
-    public void startBody(final TableBody body) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startBody(body);
-            }
-        });
-    }
-
-    @Override
-    public void endBody(final TableBody body) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endBody(body);
-            }
-        });
-    }
-
-    @Override
-    public void startRow(final TableRow tr) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startRow(tr);
-            }
-        });
-    }
-
-    @Override
-    public void endRow(final TableRow tr) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endRow(tr);
-            }
-        });
-    }
-
-    @Override
-    public void startCell(final TableCell tc) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startCell(tc);
-            }
-        });
-    }
-
-    @Override
-    public void endCell(final TableCell tc) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endCell(tc);
-            }
-        });
-    }
-
-    @Override
-    public void startList(final ListBlock lb) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startList(lb);
-            }
-        });
-    }
-
-    @Override
-    public void endList(final ListBlock lb) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endList(lb);
-            }
-        });
-    }
-
-    @Override
-    public void startListItem(final ListItem li) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startListItem(li);
-            }
-        });
-    }
-
-    @Override
-    public void endListItem(final ListItem li) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endListItem(li);
-            }
-        });
-    }
-
-    @Override
-    public void startListLabel(final ListItemLabel listItemLabel) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startListLabel(listItemLabel);
-            }
-        });
-    }
-
-    @Override
-    public void endListLabel(final ListItemLabel listItemLabel) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endListLabel(listItemLabel);
-            }
-        });
-    }
-
-    @Override
-    public void startListBody(final ListItemBody listItemBody) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startListBody(listItemBody);
-            }
-        });
-    }
-
-    @Override
-    public void endListBody(final ListItemBody listItemBody) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endListBody(listItemBody);
-            }
-        });
-    }
-
-    @Override
-    public void startLink(final BasicLink basicLink) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startLink(basicLink);
-            }
-        });
-    }
-
-    @Override
-    public void endLink(final BasicLink basicLink) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endLink(basicLink);
-            }
-        });
-    }
-
-    @Override
-    public void image(final ExternalGraphic eg) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.image(eg);
-            }
-        });
-    }
-
-    @Override
-    public void startInstreamForeignObject(final InstreamForeignObject ifo) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startInstreamForeignObject(ifo);
-            }
-        });
-    }
-
-    @Override
-    public void endInstreamForeignObject(final InstreamForeignObject ifo) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endInstreamForeignObject(ifo);
-            }
-        });
-    }
-
-    @Override
-    public void startFootnote(final Footnote footnote) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startFootnote(footnote);
-            }
-        });
-    }
-
-    @Override
-    public void endFootnote(final Footnote footnote) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endFootnote(footnote);
-            }
-        });
-    }
-
-    @Override
-    public void startFootnoteBody(final FootnoteBody body) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startFootnoteBody(body);
-            }
-        });
-    }
-
-    @Override
-    public void endFootnoteBody(final FootnoteBody body) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endFootnoteBody(body);
-            }
-        });
-    }
-
-    @Override
-    public void startLeader(final Leader l) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startLeader(l);
-            }
-        });
-    }
-
-    @Override
-    public void endLeader(final Leader l) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endLeader(l);
-            }
-        });
-    }
-
-    @Override
-    public void startWrapper(final Wrapper wrapper) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.startWrapper(wrapper);
-            }
-        });
-    }
-
-    @Override
-    public void endWrapper(final Wrapper wrapper) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.endWrapper(wrapper);
-            }
-        });
-    }
-
-    @Override
-    public void character(final Character c) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.character(c);
-            }
-        });
-    }
-
-    @Override
-    public void characters(final FOText foText) {
-        events.add(new Event() {
-            public void replay(FOEventHandler target) {
-                target.characters(foText);
-            }
-        });
-    }
-
-}
index 7e3ed059179f52569181301e79cc50050b1e9c43..93b815d3043eadd47997cb0fc35967fa196da17a 100644 (file)
@@ -55,6 +55,7 @@ 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.LayoutMasterSet;
 import org.apache.fop.fo.pagination.PageSequence;
 import org.apache.fop.fo.pagination.StaticContent;
 import org.apache.fop.fo.properties.CommonAccessibilityHolder;
@@ -67,6 +68,8 @@ class StructureTreeEventTrigger extends FOEventHandler {
 
     private StructureTreeEventHandler structureTreeEventHandler;
 
+    private LayoutMasterSet layoutMasterSet;
+
     public StructureTreeEventTrigger(StructureTreeEventHandler structureTreeEventHandler) {
         this.structureTreeEventHandler = structureTreeEventHandler;
     }
@@ -81,6 +84,9 @@ class StructureTreeEventTrigger extends FOEventHandler {
 
     @Override
     public void startPageSequence(PageSequence pageSeq) {
+        if (layoutMasterSet == null) {
+            layoutMasterSet = pageSeq.getRoot().getLayoutMasterSet();
+        }
         Locale locale = null;
         if (pageSeq.getLanguage() != null) {
             if (pageSeq.getCountry() != null) {
@@ -128,9 +134,28 @@ class StructureTreeEventTrigger extends FOEventHandler {
         endElement(pageLast);
     }
 
+    @Override
+    public void startStatic(StaticContent staticContent) {
+        AttributesImpl flowName = createFlowNameAttribute(staticContent.getFlowName());
+        startElement(staticContent, flowName);
+    }
+
+    private AttributesImpl createFlowNameAttribute(String flowName) {
+        String regionName = layoutMasterSet.getDefaultRegionNameFor(flowName);
+        AttributesImpl attribute = new AttributesImpl();
+        addNoNamespaceAttribute(attribute, Flow.FLOW_NAME, regionName);
+        return attribute;
+    }
+
+    @Override
+    public void endStatic(StaticContent staticContent) {
+        endElement(staticContent);
+    }
+
     @Override
     public void startFlow(Flow fl) {
-        startElement(fl);
+        AttributesImpl flowName = createFlowNameAttribute(fl.getFlowName());
+        startElement(fl, flowName);
     }
 
     @Override
@@ -277,16 +302,6 @@ class StructureTreeEventTrigger extends FOEventHandler {
         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);
index 50026c99673c9cf6959b3b18944d0c138bb4fbea..bb0a1ba752023669d5fdd2a4436ad631367ddc9f 100644 (file)
@@ -142,6 +142,16 @@ public abstract class DelegatingFOEventHandler extends FOEventHandler {
         delegate.endPageNumberCitationLast(pageLast);
     }
 
+    @Override
+    public void startStatic(StaticContent staticContent) {
+        delegate.startStatic(staticContent);
+    }
+
+    @Override
+    public void endStatic(StaticContent statisContent) {
+        delegate.endStatic(statisContent);
+    }
+
     @Override
     public void startFlow(Flow fl) {
         delegate.startFlow(fl);
@@ -292,16 +302,6 @@ public abstract class DelegatingFOEventHandler extends FOEventHandler {
         delegate.endListBody(listItemBody);
     }
 
-    @Override
-    public void startStatic(StaticContent staticContent) {
-        delegate.startStatic(staticContent);
-    }
-
-    @Override
-    public void endStatic(StaticContent statisContent) {
-        delegate.endStatic(statisContent);
-    }
-
     @Override
     public void startMarkup() {
         delegate.startMarkup();
index 11b6d4a2f4a34b8c03453bda2c4614cdd665f53c..ad647e42a3321c9dcbaa778d941ead4332c2fbc9 100644 (file)
@@ -192,6 +192,20 @@ public abstract class FOEventHandler {
     public void endPageNumberCitationLast(PageNumberCitationLast pageLast) {
     }
 
+    /**
+     * Process start of a Static.
+     * @param staticContent StaticContent that is starting
+     */
+    public void startStatic(StaticContent staticContent) {
+    }
+
+    /**
+     * Process end of a Static.
+     * @param staticContent StaticContent that is ending
+     */
+    public void endStatic(StaticContent staticContent) {
+    }
+
     /**
      * This method is called to indicate the start of a new fo:flow
      * or fo:static-content.
@@ -409,22 +423,6 @@ public abstract class FOEventHandler {
     public void endListBody(ListItemBody listItemBody) {
     }
 
-    // Static Regions
-    /**
-     * Process start of a Static.
-     * @param staticContent StaticContent that is starting
-     */
-    public void startStatic(StaticContent staticContent) {
-    }
-
-    /**
-     * Process end of a Static.
-     * @param staticContent StaticContent that is ending
-     */
-    public void endStatic(StaticContent staticContent) {
-    }
-
-
     /**
      * Process start of a Markup.
      */
index b29571b09b110a31b9ff70d277d8da9f7365b45b..d0d13fc17e5a30c4234b1728c92ee174be21f79c 100644 (file)
@@ -26,6 +26,7 @@ import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.datatypes.LengthBase;
 import org.apache.fop.fo.expr.PropertyException;
 import org.apache.fop.fo.flow.table.TableFObj.ColumnNumberPropertyMaker;
+import org.apache.fop.fo.pagination.Flow;
 import org.apache.fop.fo.properties.BackgroundPositionShorthand;
 import org.apache.fop.fo.properties.BorderSpacingShorthandParser;
 import org.apache.fop.fo.properties.BorderWidthPropertyMaker;
@@ -2267,7 +2268,7 @@ public final class FOPropertyMapping implements Constants {
         m  = new StringProperty.Maker(PR_FLOW_NAME);
         m.setInherited(false);
         m.setDefault("");
-        addPropertyMaker("flow-name", m);
+        addPropertyMaker(Flow.FLOW_NAME, m);
 
         // force-page-count
         m  = new EnumProperty.Maker(PR_FORCE_PAGE_COUNT);
index 981485e3208a41370a7c06ccca3833cd9015828f..45c8f78041ac68f4037ac15eb3335f2142c6460d 100644 (file)
@@ -36,6 +36,9 @@ import org.apache.fop.fo.properties.CommonAccessibilityHolder;
  */
 public class Flow extends FObj implements CommonAccessibilityHolder {
 
+    /** The "flow-name" property name. */
+    public static final String FLOW_NAME = "flow-name";
+
     private String flowName;
 
     private CommonAccessibility commonAccessibility;
@@ -61,7 +64,7 @@ public class Flow extends FObj implements CommonAccessibilityHolder {
     /** {@inheritDoc} */
     protected void startOfNode() throws FOPException {
         if (flowName == null || flowName.equals("")) {
-            missingPropertyError("flow-name");
+            missingPropertyError(FLOW_NAME);
         }
 
         // according to communication from Paul Grosso (XSL-List,
index 533b113d24fc3dbadf72c74614a5c097a7461e06..8b109dd0e0e8de77d877f6fea6bd7eab932f2378 100644 (file)
@@ -217,5 +217,24 @@ public class LayoutMasterSet extends FObj {
     public int getNameId() {
         return FO_LAYOUT_MASTER_SET;
     }
+
+    /**
+     * Returns the default name of the region to which the flow or static-content having
+     * the given flow-name is assigned.
+     *
+     * @param flowName the value of the flow-name property
+     * @return the default region name ("xsl-region-body", "xsl-region-before", etc.)
+     */
+    public String getDefaultRegionNameFor(String flowName) {
+        for (SimplePageMaster spm : simplePageMasters.values()) {
+            for (Region region : spm.getRegions().values()) {
+                if (region.getRegionName().equals(flowName)) {
+                    return region.getDefaultRegionName();
+                }
+            }
+        }
+        assert flowName.equals("xsl-before-float-separator") || flowName.equals("xsl-footnote-separator");
+        return flowName;
+    }
 }
 
index e70c80c5de544e01d643bf59deaa11df9de916c1..412cdbcca97bf13845b64671c6f37ce39ba00d23 100644 (file)
@@ -42,7 +42,7 @@ public class StaticContent extends Flow {
     /** {@inheritDoc} */
     protected void startOfNode() throws FOPException {
         if (getFlowName() == null || getFlowName().equals("")) {
-            missingPropertyError("flow-name");
+            missingPropertyError(FLOW_NAME);
         }
         getFOEventHandler().startStatic(this);
     }
index e46a22c4a724e72eda2857ae6e5eecfd30453763..0b412842f99b829942a074526e8d4d3e2261571f 100644 (file)
@@ -355,36 +355,20 @@ public class PDFDocument {
     }
 
     /**
-     * Creates and returns a structure element.
-     *
-     * @param structureType the structure type of the new element (value for the
-     * S entry)
-     * @param parent the parent of the new structure element in the structure
-     * hierarchy
-     * @return a dictionary of type StructElem
+     * Adds the given element to the structure tree.
      */
-    public PDFStructElem makeStructureElement(StructureType structureType, PDFObject parent) {
-        PDFStructElem structElem = new PDFStructElem(parent, structureType);
+    public void registerStructureElement(PDFStructElem structElem) {
         assignObjectNumber(structElem);
         structureTreeElements.add(structElem);
-        return structElem;
     }
 
     /**
-     * Creates and returns a structure element.
-     *
-     * @param structureType the structure type of the new element (value for the
-     * S entry)
-     * @param parent the parent of the new structure element in the structure
-     * hierarchy
-     * @param scope the scope of the given table header element
-     * @return a dictionary of type StructElem
+     * Assigns the given scope to the given element and adds it to the structure tree. The
+     * scope may not be added if it's not compatible with this document's PDF version.
      */
-    public PDFStructElem makeStructureElement(StructureType structureType, PDFObject parent,
-            Scope scope) {
-        PDFStructElem structElem = makeStructureElement(structureType, parent);
+    public void registerStructureElement(PDFStructElem structElem, Scope scope) {
+        registerStructureElement(structElem);
         versionController.addTableHeaderScopeAttribute(structElem, scope);
-        return structElem;
     }
 
     /**
index 28cebb3ee82635ff0bf0c8d77119e645e66e349c..8250318d769703624c967d4e6664a6cf3fd0bff7 100644 (file)
@@ -32,7 +32,8 @@ import org.apache.fop.util.LanguageTags;
 /**
  * Class representing a PDF Structure Element.
  */
-public class PDFStructElem extends PDFDictionary implements StructureTreeElement, CompressedObject {
+public class PDFStructElem extends StructureHierarchyMember
+        implements StructureTreeElement, CompressedObject {
 
     private StructureType structureType;
 
@@ -51,7 +52,7 @@ public class PDFStructElem extends PDFDictionary implements StructureTreeElement
      * @param parent parent of this element
      * @param structureType the structure type of this element
      */
-    PDFStructElem(PDFObject parent, StructureType structureType) {
+    public PDFStructElem(PDFObject parent, StructureType structureType) {
         this(parent);
         this.structureType = structureType;
         put("S", structureType.getName());
@@ -86,6 +87,7 @@ public class PDFStructElem extends PDFDictionary implements StructureTreeElement
      *
      * @param kid element to be added
      */
+    @Override
     public void addKid(PDFObject kid) {
         if (kids == null) {
             kids = new ArrayList<PDFObject>();
index 5f19bb2e2b2275ec572592025cff8a46e9a79a81..ca6d82d2253ea740df6321e5719b52aaf6682996 100644 (file)
@@ -22,7 +22,7 @@ package org.apache.fop.pdf;
 /**
  * Class representing a PDF /StructTreeRoot dictionary.
  */
-public class PDFStructTreeRoot extends PDFDictionary {
+public class PDFStructTreeRoot extends StructureHierarchyMember {
 
     /**
      * Creates a new /StructTreeRoot dictionary.
@@ -49,6 +49,7 @@ public class PDFStructTreeRoot extends PDFDictionary {
      *
      * @param kid an object to be added to the K entry
      */
+    @Override
     public void addKid(PDFObject kid) {
         getKids().add(kid);
     }
index dc045e1805640a39d090dd4a3821fbf815acb4ab..69550119a56a78fec257312f3fa4b9c67ae73119 100644 (file)
@@ -111,6 +111,11 @@ public final class StandardStructureTypes {
             return name;
         }
 
+        @Override
+        public String toString() {
+            return name.toString().substring(1);
+        }
+
     }
 
     private static final Map<String, StructureType> STRUCTURE_TYPES = new HashMap<String, StructureType>();
diff --git a/src/java/org/apache/fop/pdf/StructureHierarchyMember.java b/src/java/org/apache/fop/pdf/StructureHierarchyMember.java
new file mode 100644 (file)
index 0000000..e3be921
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.pdf;
+
+/**
+ * An element in the document's structure tree. This can be either the structure tree root
+ * or a structure element.
+ *
+ * @see "Section 10.6, <q>Logical Structure</q> of the PDF Reference, 4th edition (PDF 1.5)"
+ */
+public abstract class StructureHierarchyMember extends PDFDictionary {
+
+    /**
+     * Adds the given object to the array of kids.
+     *
+     * @param kid an object to be added to the K entry
+     */
+    public abstract void addKid(PDFObject kid);
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java b/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java
deleted file mode 100644 (file)
index 68bfd86..0000000
+++ /dev/null
@@ -1,162 +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.render.pdf;
-
-import java.util.Map;
-
-import org.apache.fop.events.EventBroadcaster;
-import org.apache.fop.pdf.PDFObject;
-import org.apache.fop.pdf.PDFStructElem;
-import org.apache.fop.pdf.StandardStructureTypes;
-import org.apache.fop.pdf.StructureType;
-
-/**
- * This class provides the standard mappings from Formatting Objects to PDF structure types.
- */
-final class FOToPDFRoleMap {
-
-    private static final Map<String, Mapper> DEFAULT_MAPPINGS = new java.util.HashMap<String, Mapper>();
-
-    static {
-        // Create the standard mappings
-        // Declarations and Pagination and Layout Formatting Objects
-        addMapping("root",                      StandardStructureTypes.Grouping.DOCUMENT);
-        addMapping("page-sequence",             StandardStructureTypes.Grouping.PART);
-        addMapping("flow",                      StandardStructureTypes.Grouping.SECT);
-        addMapping("static-content",            StandardStructureTypes.Grouping.SECT);
-        // Block-level Formatting Objects
-        addMapping("block",                     StandardStructureTypes.Paragraphlike.P);
-        addMapping("block-container",           StandardStructureTypes.Grouping.DIV);
-        // Inline-level Formatting Objects
-        addMapping("character",                 StandardStructureTypes.InlineLevelStructure.SPAN);
-        addMapping("external-graphic",          StandardStructureTypes.Illustration.FIGURE);
-        addMapping("instream-foreign-object",   StandardStructureTypes.Illustration.FIGURE);
-        addMapping("inline",                    StandardStructureTypes.InlineLevelStructure.SPAN);
-        addMapping("inline-container",          StandardStructureTypes.Grouping.DIV);
-        addMapping("page-number",               StandardStructureTypes.InlineLevelStructure.QUOTE);
-        addMapping("page-number-citation",      StandardStructureTypes.InlineLevelStructure.QUOTE);
-        addMapping("page-number-citation-last", StandardStructureTypes.InlineLevelStructure.QUOTE);
-        // Formatting Objects for Tables
-        addMapping("table-and-caption",         StandardStructureTypes.Grouping.DIV);
-        addMapping("table",                     StandardStructureTypes.Table.TABLE);
-        addMapping("table-caption",             StandardStructureTypes.Grouping.CAPTION);
-        addMapping("table-header",              StandardStructureTypes.Table.THEAD);
-        addMapping("table-footer",              StandardStructureTypes.Table.TFOOT);
-        addMapping("table-body",                StandardStructureTypes.Table.TBODY);
-        addMapping("table-row",                 StandardStructureTypes.Table.TR);
-        addMapping("table-cell",                new TableCellMapper());
-        // Formatting Objects for Lists
-        addMapping("list-block",                StandardStructureTypes.List.L);
-        addMapping("list-item",                 StandardStructureTypes.List.LI);
-        addMapping("list-item-body",            StandardStructureTypes.List.LBODY);
-        addMapping("list-item-label",           StandardStructureTypes.List.LBL);
-        // Dynamic Effects: Link and Multi Formatting Objects
-        addMapping("basic-link",                StandardStructureTypes.InlineLevelStructure.LINK);
-        // Out-of-Line Formatting Objects
-        addMapping("float",                     StandardStructureTypes.Grouping.DIV);
-        addMapping("footnote",                  StandardStructureTypes.InlineLevelStructure.NOTE);
-        addMapping("footnote-body",             StandardStructureTypes.Grouping.SECT);
-        addMapping("wrapper",                   StandardStructureTypes.InlineLevelStructure.SPAN);
-        addMapping("marker",                    StandardStructureTypes.Grouping.PRIVATE);
-    }
-
-    private static void addMapping(String fo, StructureType structureType) {
-        addMapping(fo, new SimpleMapper(structureType));
-    }
-
-    private static void addMapping(String fo, Mapper mapper) {
-        DEFAULT_MAPPINGS.put(fo, mapper);
-    }
-
-
-    /**
-     * 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 StructureType mapFormattingObject(String fo, String role,
-            PDFObject parent, EventBroadcaster eventBroadcaster) {
-        StructureType type = null;
-        if (role == null) {
-            type = getDefaultMappingFor(fo, parent);
-        } else {
-            type = StandardStructureTypes.get(role);
-            if (type == null) {
-                type = getDefaultMappingFor(fo, parent);
-                PDFEventProducer.Provider.get(eventBroadcaster).nonStandardStructureType(fo,
-                        fo, role, type.toString().substring(1));
-            }
-        }
-        assert type != null;
-        return type;
-    }
-
-    /**
-     * Maps a Formatting Object to a PDFName representing the associated structure type.
-     * @param fo the formatting object's local name
-     * @param parent the parent of the structure element to be mapped
-     * @return the structure type or NonStruct if no match could be found
-     */
-    private static StructureType getDefaultMappingFor(String fo, PDFObject parent) {
-        Mapper mapper = DEFAULT_MAPPINGS.get(fo);
-        if (mapper != null) {
-            return mapper.getStructureType(parent);
-        } else {
-            return StandardStructureTypes.Grouping.NON_STRUCT;
-        }
-    }
-
-    private interface Mapper {
-        StructureType getStructureType(PDFObject parent);
-    }
-
-    private static class SimpleMapper implements Mapper {
-
-        private StructureType structureType;
-
-        public SimpleMapper(StructureType structureType) {
-            this.structureType = structureType;
-        }
-
-        public StructureType getStructureType(PDFObject parent) {
-            return structureType;
-        }
-
-    }
-
-    private static class TableCellMapper implements Mapper {
-
-        public StructureType getStructureType(PDFObject parent) {
-            PDFStructElem grandParent = ((PDFStructElem) parent).getParentStructElem();
-            //TODO What to do with cells from table-footer? Currently they are mapped on TD.
-            if (grandParent.getStructureType() == StandardStructureTypes.Table.THEAD) {
-               return StandardStructureTypes.Table.TH;
-            } else {
-                return StandardStructureTypes.Table.TD;
-            }
-        }
-
-    }
-
-    private FOToPDFRoleMap() { }
-}
index 40062f73fd083fbf649633fe4fe45ed477dd1148..4b825386747bfa27b76b36dd72de2105a2949fa3 100644 (file)
@@ -59,12 +59,11 @@ public interface PDFEventProducer extends EventProducer {
      * Custom structure type is not standard as per the PDF reference.
      *
      * @param source the event source
-     * @param fo the local name of the formatting object having the custom type
      * @param type custom structure type
      * @param fallback default structure type used as a fallback
      * @event.severity WARN
      */
-    void nonStandardStructureType(Object source, String fo, String type, String fallback);
+    void nonStandardStructureType(Object source, String type, String fallback);
 
     /**
      * The encryption length must be a multiple of 8 between 40 and 128.
index 1377bbfcebf6c371a60a0983df0a6358229c1077..0d4a6b7fb008ec49dd797c6d9e9f2e18ac6ad828 100644 (file)
@@ -21,24 +21,271 @@ package org.apache.fop.render.pdf;
 
 import java.util.LinkedList;
 import java.util.Locale;
+import java.util.Map;
 
 import org.xml.sax.Attributes;
+import org.xml.sax.helpers.AttributesImpl;
 
 import org.apache.fop.accessibility.StructureTreeElement;
 import org.apache.fop.accessibility.StructureTreeEventHandler;
 import org.apache.fop.events.EventBroadcaster;
 import org.apache.fop.fo.extensions.ExtensionElementMapping;
+import org.apache.fop.fo.pagination.Flow;
 import org.apache.fop.pdf.PDFFactory;
-import org.apache.fop.pdf.PDFObject;
 import org.apache.fop.pdf.PDFParentTree;
 import org.apache.fop.pdf.PDFStructElem;
 import org.apache.fop.pdf.PDFStructTreeRoot;
 import org.apache.fop.pdf.StandardStructureAttributes.Table.Scope;
+import org.apache.fop.pdf.StandardStructureTypes;
+import org.apache.fop.pdf.StandardStructureTypes.Grouping;
 import org.apache.fop.pdf.StandardStructureTypes.Table;
+import org.apache.fop.pdf.StructureHierarchyMember;
 import org.apache.fop.pdf.StructureType;
+import org.apache.fop.util.XMLUtil;
 
 class PDFStructureTreeBuilder implements StructureTreeEventHandler {
 
+    private static final String ROLE = "role";
+
+    private static final Map<String, StructureElementBuilder> BUILDERS
+            = new java.util.HashMap<String, StructureElementBuilder>();
+
+    private static final StructureElementBuilder DEFAULT_BUILDER
+            = new DefaultStructureElementBuilder(Grouping.NON_STRUCT);
+
+    static {
+        // Declarations and Pagination and Layout Formatting Objects
+        StructureElementBuilder regionBuilder = new RegionBuilder();
+        addBuilder("root",                      StandardStructureTypes.Grouping.DOCUMENT);
+        addBuilder("page-sequence",             new PageSequenceBuilder());
+        addBuilder("static-content",            regionBuilder);
+        addBuilder("flow",                      regionBuilder);
+        // Block-level Formatting Objects
+        addBuilder("block",                     StandardStructureTypes.Paragraphlike.P);
+        addBuilder("block-container",           StandardStructureTypes.Grouping.DIV);
+        // Inline-level Formatting Objects
+        addBuilder("character",                 StandardStructureTypes.InlineLevelStructure.SPAN);
+        addBuilder("external-graphic",          new ImageBuilder());
+        addBuilder("instream-foreign-object",   new ImageBuilder());
+        addBuilder("inline",                    StandardStructureTypes.InlineLevelStructure.SPAN);
+        addBuilder("inline-container",          StandardStructureTypes.Grouping.DIV);
+        addBuilder("page-number",               StandardStructureTypes.InlineLevelStructure.QUOTE);
+        addBuilder("page-number-citation",      StandardStructureTypes.InlineLevelStructure.QUOTE);
+        addBuilder("page-number-citation-last", StandardStructureTypes.InlineLevelStructure.QUOTE);
+        // Formatting Objects for Tables
+        addBuilder("table-and-caption",         StandardStructureTypes.Grouping.DIV);
+        addBuilder("table",                     new TableBuilder());
+        addBuilder("table-caption",             StandardStructureTypes.Grouping.CAPTION);
+        addBuilder("table-header",              StandardStructureTypes.Table.THEAD);
+        addBuilder("table-footer",              new TableFooterBuilder());
+        addBuilder("table-body",                StandardStructureTypes.Table.TBODY);
+        addBuilder("table-row",                 StandardStructureTypes.Table.TR);
+        addBuilder("table-cell",                new TableCellBuilder());
+        // Formatting Objects for Lists
+        addBuilder("list-block",                StandardStructureTypes.List.L);
+        addBuilder("list-item",                 StandardStructureTypes.List.LI);
+        addBuilder("list-item-body",            StandardStructureTypes.List.LBODY);
+        addBuilder("list-item-label",           StandardStructureTypes.List.LBL);
+        // Dynamic Effects: Link and Multi Formatting Objects
+        addBuilder("basic-link",                StandardStructureTypes.InlineLevelStructure.LINK);
+        // Out-of-Line Formatting Objects
+        addBuilder("float",                     StandardStructureTypes.Grouping.DIV);
+        addBuilder("footnote",                  StandardStructureTypes.InlineLevelStructure.NOTE);
+        addBuilder("footnote-body",             StandardStructureTypes.Grouping.SECT);
+        addBuilder("wrapper",                   StandardStructureTypes.InlineLevelStructure.SPAN);
+        addBuilder("marker",                    StandardStructureTypes.Grouping.PRIVATE);
+
+        addBuilder("#PCDATA", new PlaceholderBuilder());
+    }
+
+    private static void addBuilder(String fo, StructureType structureType) {
+        addBuilder(fo, new DefaultStructureElementBuilder(structureType));
+    }
+
+    private static void addBuilder(String fo, StructureElementBuilder mapper) {
+        BUILDERS.put(fo, mapper);
+    }
+
+    private interface StructureElementBuilder {
+
+        PDFStructElem build(StructureHierarchyMember parent, Attributes attributes, PDFFactory pdfFactory,
+                EventBroadcaster eventBroadcaster);
+
+    }
+
+    private static class DefaultStructureElementBuilder implements StructureElementBuilder {
+
+        private final StructureType defaultStructureType;
+
+        DefaultStructureElementBuilder(StructureType structureType) {
+            this.defaultStructureType = structureType;
+        }
+
+        public final PDFStructElem build(StructureHierarchyMember parent, Attributes attributes,
+                PDFFactory pdfFactory, EventBroadcaster eventBroadcaster) {
+            String role = attributes.getValue(ROLE);
+            StructureType structureType;
+            if (role == null) {
+                structureType = defaultStructureType;
+            } else {
+                structureType = StandardStructureTypes.get(role);
+                if (structureType == null) {
+                    structureType = defaultStructureType;
+                    PDFEventProducer.Provider.get(eventBroadcaster).nonStandardStructureType(role, role,
+                            structureType.toString());
+                }
+            }
+            PDFStructElem structElem = createStructureElement(parent, structureType);
+            setAttributes(structElem, attributes);
+            addKidToParent(structElem, parent, attributes);
+            registerStructureElement(structElem, pdfFactory);
+            return structElem;
+        }
+
+        protected PDFStructElem createStructureElement(StructureHierarchyMember parent,
+                StructureType structureType) {
+            return new PDFStructElem(parent, structureType);
+        }
+
+        protected void setAttributes(PDFStructElem structElem, Attributes attributes) {
+        }
+
+        protected void addKidToParent(PDFStructElem kid, StructureHierarchyMember parent,
+                Attributes attributes) {
+            parent.addKid(kid);
+        }
+
+        protected void registerStructureElement(PDFStructElem structureElement, PDFFactory pdfFactory) {
+            pdfFactory.getDocument().registerStructureElement(structureElement);
+        }
+
+    }
+
+    private static class PageSequenceBuilder extends DefaultStructureElementBuilder {
+
+        PageSequenceBuilder() {
+            super(StandardStructureTypes.Grouping.PART);
+        }
+
+        @Override
+        protected PDFStructElem createStructureElement(StructureHierarchyMember parent,
+                StructureType structureType) {
+            return new PageSequenceStructElem(parent, structureType);
+        }
+
+    }
+
+    private static class RegionBuilder extends DefaultStructureElementBuilder {
+
+        RegionBuilder() {
+            super(StandardStructureTypes.Grouping.SECT);
+        }
+
+        @Override
+        protected void addKidToParent(PDFStructElem kid, StructureHierarchyMember parent,
+                Attributes attributes) {
+            String flowName = attributes.getValue(Flow.FLOW_NAME);
+            ((PageSequenceStructElem) parent).addContent(flowName, kid);
+        }
+
+    }
+
+    private static class ImageBuilder extends DefaultStructureElementBuilder {
+
+        ImageBuilder() {
+            super(StandardStructureTypes.Illustration.FIGURE);
+        }
+
+        @Override
+        protected void setAttributes(PDFStructElem structElem, Attributes attributes) {
+            String altTextNode = attributes.getValue(ExtensionElementMapping.URI, "alt-text");
+            if (altTextNode == null) {
+                altTextNode = "No alternate text specified";
+            }
+            structElem.put("Alt", altTextNode);
+        }
+
+    }
+
+    private static class TableBuilder extends DefaultStructureElementBuilder {
+
+        TableBuilder() {
+            super(StandardStructureTypes.Table.TABLE);
+        }
+
+        @Override
+        protected PDFStructElem createStructureElement(StructureHierarchyMember parent,
+                StructureType structureType) {
+            return new TableStructElem(parent, structureType);
+        }
+    }
+
+    private static class TableFooterBuilder extends DefaultStructureElementBuilder {
+
+        public TableFooterBuilder() {
+            super(StandardStructureTypes.Table.TFOOT);
+        }
+
+        @Override
+        protected void addKidToParent(PDFStructElem kid, StructureHierarchyMember parent,
+                Attributes attributes) {
+            ((TableStructElem) parent).addTableFooter(kid);
+        }
+    }
+
+    private static class TableCellBuilder extends DefaultStructureElementBuilder {
+
+        TableCellBuilder() {
+            super(StandardStructureTypes.Table.TD);
+        }
+
+        @Override
+        protected PDFStructElem createStructureElement(StructureHierarchyMember parent,
+                StructureType structureType) {
+            PDFStructElem grandParent = ((PDFStructElem) parent).getParentStructElem();
+            //TODO What to do with cells from table-footer? Currently they are mapped on TD.
+            if (grandParent.getStructureType() == StandardStructureTypes.Table.THEAD) {
+                structureType = StandardStructureTypes.Table.TH;
+            } else {
+                structureType = StandardStructureTypes.Table.TD;
+            }
+            return super.createStructureElement(parent, structureType);
+        }
+
+        @Override
+        protected void registerStructureElement(PDFStructElem structureElement, PDFFactory pdfFactory) {
+            if (structureElement.getStructureType() == Table.TH) {
+                pdfFactory.getDocument().registerStructureElement(structureElement, Scope.COLUMN);
+            } else {
+                pdfFactory.getDocument().registerStructureElement(structureElement);
+            }
+        }
+
+        @Override
+        protected void setAttributes(PDFStructElem structElem, Attributes attributes) {
+            String columnSpan = attributes.getValue("number-columns-spanned");
+            if (columnSpan != null) {
+                structElem.setTableAttributeColSpan(Integer.parseInt(columnSpan));
+            }
+            String rowSpan = attributes.getValue("number-rows-spanned");
+            if (rowSpan != null) {
+                structElem.setTableAttributeRowSpan(Integer.parseInt(rowSpan));
+            }
+        }
+
+    }
+
+    private static class PlaceholderBuilder implements StructureElementBuilder {
+
+        public PDFStructElem build(StructureHierarchyMember parent, Attributes attributes,
+                PDFFactory pdfFactory, EventBroadcaster eventBroadcaster) {
+            PDFStructElem elem = new PDFStructElem.Placeholder(parent);
+            parent.addKid(elem);
+            return elem;
+        }
+
+    }
+
     private PDFFactory pdfFactory;
 
     private EventBroadcaster eventBroadcaster;
@@ -51,6 +298,10 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler {
         this.pdfFactory = pdfFactory;
     }
 
+    void setEventBroadcaster(EventBroadcaster eventBroadcaster) {
+        this.eventBroadcaster = eventBroadcaster;
+    }
+
     void setLogicalStructureHandler(PDFLogicalStructureHandler logicalStructureHandler) {
         createRootStructureElement(logicalStructureHandler);
     }
@@ -59,93 +310,53 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler {
         assert rootStructureElement == null;
         PDFParentTree parentTree = logicalStructureHandler.getParentTree();
         PDFStructTreeRoot structTreeRoot = pdfFactory.getDocument().makeStructTreeRoot(parentTree);
-        rootStructureElement = createStructureElement("root", structTreeRoot, null);
-        structTreeRoot.addKid(rootStructureElement);
+        rootStructureElement = createStructureElement("root", structTreeRoot,
+                new AttributesImpl(), pdfFactory, eventBroadcaster);
     }
 
-    void setEventBroadcaster(EventBroadcaster eventBroadcaster) {
-        this.eventBroadcaster = eventBroadcaster;
-    }
+    private static PDFStructElem createStructureElement(String name, StructureHierarchyMember parent,
+                Attributes attributes, PDFFactory pdfFactory, EventBroadcaster eventBroadcaster) {
+            StructureElementBuilder builder = BUILDERS.get(name);
+            if (builder == null) {
+                // TODO is a fallback really necessary?
+                builder = DEFAULT_BUILDER;
+            }
+            return builder.build(parent, attributes, pdfFactory, eventBroadcaster);
+        }
 
     public void startPageSequence(Locale language, String role) {
         ancestors = new LinkedList<PDFStructElem>();
-        PDFStructElem structElem = createStructureElement("page-sequence", rootStructureElement, role);
+        AttributesImpl attributes = new AttributesImpl();
+        attributes.addAttribute("", ROLE, ROLE, XMLUtil.CDATA, role);
+        PDFStructElem structElem = createStructureElement("page-sequence",
+                rootStructureElement, attributes, pdfFactory, eventBroadcaster);
         if (language != null) {
             structElem.setLanguage(language);
         }
-        rootStructureElement.addKid(structElem);
         ancestors.add(structElem);
     }
 
-    private PDFStructElem createStructureElement(String name, PDFObject parent, String role) {
-        StructureType structureType = FOToPDFRoleMap.mapFormattingObject(name, role, parent,
-                eventBroadcaster);
-        if (structureType == Table.TH) {
-            return pdfFactory.getDocument().makeStructureElement(structureType, parent, Scope.COLUMN);
-        } else {
-            return pdfFactory.getDocument().makeStructureElement(structureType, parent);
-        }
-    }
-
     public void endPageSequence() {
     }
 
     public StructureTreeElement startNode(String name, Attributes attributes) {
         PDFStructElem parent = ancestors.getFirst();
-        String role = attributes.getValue("role");
-        PDFStructElem structElem = createStructureElement(name, parent, role);
-        setSpanAttributes(structElem, attributes);
-        parent.addKid(structElem);
+        PDFStructElem structElem = createStructureElement(name, parent, attributes,
+                pdfFactory, eventBroadcaster);
         ancestors.addFirst(structElem);
         return structElem;
     }
 
-    private void setSpanAttributes(PDFStructElem structElem, Attributes attributes) {
-        String columnSpan = attributes.getValue("number-columns-spanned");
-        if (columnSpan != null) {
-            structElem.setTableAttributeColSpan(Integer.parseInt(columnSpan));
-        }
-        String rowSpan = attributes.getValue("number-rows-spanned");
-        if (rowSpan != null) {
-            structElem.setTableAttributeRowSpan(Integer.parseInt(rowSpan));
-        }
-    }
-
     public void endNode(String name) {
-        removeFirstAncestor();
-    }
-
-    private void removeFirstAncestor() {
         ancestors.removeFirst();
     }
 
     public StructureTreeElement startImageNode(String name, Attributes attributes) {
-        PDFStructElem parent = ancestors.getFirst();
-        String role = attributes.getValue("role");
-        PDFStructElem structElem = createStructureElement(name, parent, role);
-        parent.addKid(structElem);
-        String altTextNode = attributes.getValue(ExtensionElementMapping.URI, "alt-text");
-        if (altTextNode != null) {
-            structElem.put("Alt", altTextNode);
-        } else {
-            structElem.put("Alt", "No alternate text specified");
-        }
-        ancestors.addFirst(structElem);
-        return structElem;
+        return startNode(name, attributes);
     }
 
     public StructureTreeElement startReferencedNode(String name, Attributes attributes) {
-        PDFStructElem parent = ancestors.getFirst();
-        String role = attributes.getValue("role");
-        PDFStructElem structElem;
-        if ("#PCDATA".equals(name)) {
-            structElem = new PDFStructElem.Placeholder(parent);
-        } else {
-            structElem = createStructureElement(name, parent, role);
-        }
-        parent.addKid(structElem);
-        ancestors.addFirst(structElem);
-        return structElem;
+        return startNode(name, attributes);
     }
 
 }
diff --git a/src/java/org/apache/fop/render/pdf/PageSequenceStructElem.java b/src/java/org/apache/fop/render/pdf/PageSequenceStructElem.java
new file mode 100644 (file)
index 0000000..09d5b81
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.fop.pdf.PDFArray;
+import org.apache.fop.pdf.PDFObject;
+import org.apache.fop.pdf.PDFStructElem;
+import org.apache.fop.pdf.StructureType;
+
+class PageSequenceStructElem extends PDFStructElem {
+
+    private List<PDFStructElem> regionBefores = new ArrayList<PDFStructElem>();
+
+    private List<PDFStructElem> regionAfters = new ArrayList<PDFStructElem>();
+
+    private List<PDFStructElem> regionStarts = new ArrayList<PDFStructElem>();
+
+    private List<PDFStructElem> regionEnds = new ArrayList<PDFStructElem>();
+
+    PageSequenceStructElem(PDFObject parent, StructureType structureType) {
+        super(parent, structureType);
+    }
+
+    void addContent(String flowName, PDFStructElem content) {
+        if (flowName.equals("xsl-region-before")) {
+            regionBefores.add(content);
+        } else if (flowName.equals("xsl-region-after")) {
+            regionAfters.add(content);
+        } else if (flowName.equals("xsl-region-start")) {
+            regionStarts.add(content);
+        } else if (flowName.equals("xsl-region-end")) {
+            regionEnds.add(content);
+        } else {
+            addKid(content);
+        }
+    }
+
+    @Override
+    protected boolean attachKids() {
+        assert !kids.isEmpty();
+        PDFArray k = new PDFArray();
+        addRegions(k, regionBefores);
+        addRegions(k, regionStarts);
+        addRegions(k, kids);
+        addRegions(k, regionEnds);
+        addRegions(k, regionAfters);
+        put("K", k);
+        return true;
+    }
+
+    private void addRegions(PDFArray k, List<? extends PDFObject> regions) {
+        if (!regions.isEmpty()) {
+            for (PDFObject kid : regions) {
+                k.add(kid);
+            }
+        }
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/TableStructElem.java b/src/java/org/apache/fop/render/pdf/TableStructElem.java
new file mode 100644 (file)
index 0000000..c44acb2
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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 org.apache.fop.pdf.PDFObject;
+import org.apache.fop.pdf.PDFStructElem;
+import org.apache.fop.pdf.StructureType;
+
+class TableStructElem extends PDFStructElem {
+
+    private PDFStructElem tableFooter;
+
+    public TableStructElem(PDFObject parent, StructureType structureType) {
+        super(parent, structureType);
+    }
+
+    void addTableFooter(PDFStructElem footer) {
+        assert tableFooter == null;
+        tableFooter = footer;
+    }
+
+    @Override
+    protected boolean attachKids() {
+        assert !kids.isEmpty();
+        if (tableFooter != null) {
+            kids.add(tableFooter);
+        }
+        return super.attachKids();
+    }
+
+}
index 5927dd82c08c5586afcdfe472e2285d24fa96c39..270496f59ea8895ad28c5d53722c7e8252b3535a 100644 (file)
       documents. Example: the fix of marks layering will be such a case when it's done.
     -->
     <release version="FOP Trunk" date="TBD">
+      <action context="Renderers" dev="VH" type="fix" fixes-bug="53778">
+        When PDF accessibility is enabled, the contents for the different regions must appear in the 
+        proper order in the structure tree.
+      </action>
       <action context="Renderers" dev="MH" type="fix" fixes-bug="53766" due-to="Robert Meyer">
         Remove StandardEncoding as the encoding type from fonts used in the PDF renderer
       </action>
index 863bfe79733912209c8466ed13a76806b39a3080..6092912f94da2206e69109f40b1b8e2b5f1223ae 100644 (file)
@@ -19,8 +19,6 @@
 
 package org.apache.fop.accessibility.fo;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -34,7 +32,6 @@ import javax.xml.transform.TransformerFactoryConfigurationError;
 import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.sax.SAXTransformerFactory;
 import javax.xml.transform.sax.TransformerHandler;
-import javax.xml.transform.stream.StreamResult;
 import javax.xml.transform.stream.StreamSource;
 
 import org.custommonkey.xmlunit.Diff;
@@ -57,9 +54,17 @@ import org.apache.fop.fotreetest.DummyFOEventHandler;
 
 public class FO2StructureTreeConverterTestCase {
 
-    private interface FOLoader {
+    private static class FOLoader {
 
-        InputStream getFoInputStream();
+        private final String resourceName;
+
+        FOLoader(String resourceName) {
+            this.resourceName = resourceName;
+        }
+
+        public InputStream getFoInputStream() {
+            return getResource(resourceName);
+        }
     }
 
     private static final String STRUCTURE_TREE_SEQUENCE_NAME = "structure-tree-sequence";
@@ -68,62 +73,30 @@ public class FO2StructureTreeConverterTestCase {
 
     @Test
     public void testCompleteDocument() throws Exception {
-        foLoader = new FOLoader() {
-            public InputStream getFoInputStream() {
-                return getResource("/org/apache/fop/fo/complete_document.fo");
-            }
-        };
-        testConverter();
+        testConverter("/org/apache/fop/fo/complete_document.fo");
     }
 
     @Test
     public void testTableFooters() throws Exception {
-        foLoader = new FOLoader() {
-            public InputStream getFoInputStream() {
-                return getResource("table-footers.fo");
-            }
-        };
-        testConverter();
-    }
-
-    @Test
-    public void testCompleteContentWrappedInTableFooter() throws Exception {
-        Source xslt = new StreamSource(getResource("wrapCompleteDocumentInTableFooter.xsl"));
-        Transformer transformer = createTransformer(xslt);
-        InputStream originalFO = getResource("/org/apache/fop/fo/complete_document.fo");
-        ByteArrayOutputStream transformedFoOutput = new ByteArrayOutputStream();
-        transformer.transform(new StreamSource(originalFO), new StreamResult(transformedFoOutput));
-        final byte[] transformedFoOutputBytes = transformedFoOutput.toByteArray();
-        foLoader = new FOLoader() {
-            public InputStream getFoInputStream() {
-                return new ByteArrayInputStream(transformedFoOutputBytes);
-            }
-        };
-        testConverter();
+        testConverter("table-footers.fo");
     }
 
     @Test
     public void testArtifact() throws Exception {
-        foLoader = new FOLoader() {
-
-            public InputStream getFoInputStream() {
-                return getResource("artifact.fo");
-            }
-        };
-        testConverter();
+        testConverter("artifact.fo");
     }
 
-    private Transformer createTransformer(Source xslt) throws TransformerFactoryConfigurationError,
-            TransformerConfigurationException {
-        TransformerFactory transformerFactory = TransformerFactory.newInstance();
-        return transformerFactory.newTransformer(xslt);
+    @Test
+    public void testSideRegions() throws Exception {
+        testConverter("/org/apache/fop/fo/pagination/side-regions.fo");
     }
 
     private static InputStream getResource(String name) {
         return FO2StructureTreeConverterTestCase.class.getResourceAsStream(name);
     }
 
-    private void testConverter() throws Exception {
+    private void testConverter(String foResourceName) throws Exception {
+        foLoader = new FOLoader(foResourceName);
         DOMResult expectedStructureTree = loadExpectedStructureTree();
         DOMResult actualStructureTree = buildActualStructureTree();
         final Diff diff = createDiff(expectedStructureTree, actualStructureTree);
index db0dffb14e18ebe3fd743a8900db28c94dcf1910..c739462e4d976ae534f8f95edbcc1496d0abbaf2 100644 (file)
     <xsl:call-template name="copy"/>
   </xsl:template>
 
+  <xsl:template match="fo:static-content/@flow-name|fo:flow/@flow-name">
+    <xsl:choose>
+      <xsl:when test=". = 'xsl-region-body' or
+        . = 'xsl-region-before' or
+        . = 'xsl-region-after' or
+        . = 'xsl-region-start' or
+        . = 'xsl-region-end' or
+        . = 'xsl-before-float-separator' or
+        . = 'xsl-footnote-separator'">
+        <xsl:copy/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:attribute name="{local-name()}">
+          <xsl:value-of select="concat('xsl-', local-name(//*[@region-name = current()]))"/>
+        </xsl:attribute>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
   <!-- Block-level Formatting Objects -->
   <xsl:template match="fo:block|fo:block-container">
     <xsl:call-template name="copy"/>
     <xsl:call-template name="copy"/>
   </xsl:template>
 
-  <xsl:template match="fo:table">
-    <xsl:copy>
-      <xsl:apply-templates select="@*"/>
-      <xsl:apply-templates select="*[name() != 'fo:table-footer']"/>
-      <xsl:apply-templates select="fo:table-footer"/>
-    </xsl:copy>
-  </xsl:template>
-
-  <xsl:template match="fo:table-header|fo:table-footer|fo:table-body|fo:table-row|fo:table-cell">
+  <xsl:template match="fo:table|fo:table-header|fo:table-footer|fo:table-body|fo:table-row|fo:table-cell">
     <xsl:call-template name="copy"/>
   </xsl:template>
 
diff --git a/test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl b/test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl
deleted file mode 100644 (file)
index 9608b2f..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  Licensed to the Apache Software Foundation (ASF) under one or more
-  contributor license agreements.  See the NOTICE file distributed with
-  this work for additional information regarding copyright ownership.
-  The ASF licenses this file to You under the Apache License, Version 2.0
-  (the "License"); you may not use this file except in compliance with
-  the License.  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-<!-- $Id$ -->
-<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-  xmlns:fo="http://www.w3.org/1999/XSL/Format">
-
-  <xsl:template match="@*|node()" name="copy">
-    <xsl:copy>
-      <xsl:apply-templates select="@*|node()"/>
-    </xsl:copy>
-  </xsl:template>
-
-
-  <xsl:template match="/">
-    <fo:root>
-      <fo:layout-master-set>
-        <fo:simple-page-master master-name="page"
-          page-height="500pt" page-width="300pt" margin="20pt">
-          <fo:region-body margin-top="20pt"/>
-        </fo:simple-page-master>
-      </fo:layout-master-set>
-      <xsl:apply-templates select="//fo:page-sequence"/>
-    </fo:root>
-  </xsl:template>
-
-  <xsl:template match="fo:page-sequence">
-    <fo:page-sequence master-reference="page">
-      <xsl:apply-templates select="fo:flow"/>
-    </fo:page-sequence>
-  </xsl:template>
-
-  <xsl:template match="fo:flow">
-    <xsl:copy>
-      <xsl:apply-templates select="@*[not(starts-with(name(), 'space-before'))]"/>
-      <fo:table width="100%" table-layout="fixed">
-        <fo:table-footer>
-          <fo:table-cell background-color="#F0F0F0">
-            <xsl:apply-templates select="@*[starts-with(name(), 'space-before')]"/>
-            <xsl:apply-templates select="*"/>
-          </fo:table-cell>
-        </fo:table-footer>
-        <fo:table-body>
-          <fo:table-cell>
-            <fo:block>The content below is in the table footer.</fo:block>
-          </fo:table-cell>
-        </fo:table-body>
-      </fo:table>
-    </xsl:copy>
-  </xsl:template>
-
-</xsl:stylesheet>
diff --git a/test/java/org/apache/fop/fo/pagination/LayoutMasterSetTestCase.java b/test/java/org/apache/fop/fo/pagination/LayoutMasterSetTestCase.java
new file mode 100644 (file)
index 0000000..cfe71f5
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fo.pagination;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+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;
+
+public class LayoutMasterSetTestCase {
+
+    private static class FlowMappingTester extends FOEventHandler {
+
+        private static final String[][] FLOW_MAPPINGS = {
+
+            {"first-page-before", "xsl-region-before"},
+            {"first-page-after", "xsl-region-after"},
+            {"first-page-start", "xsl-region-start"},
+            {"first-page-end", "xsl-region-end"},
+
+            {"odd-page-before", "xsl-region-before"},
+            {"odd-page-after", "xsl-region-after"},
+            {"odd-page-start", "xsl-region-start"},
+            {"odd-page-end", "xsl-region-end"},
+
+            {"odd-page-before", "xsl-region-before"},
+            {"odd-page-after", "xsl-region-after"},
+            {"odd-page-start", "xsl-region-start"},
+            {"odd-page-end", "xsl-region-end"},
+
+            {"blank-page-before", "xsl-region-before"},
+            {"blank-page-after", "xsl-region-after"},
+            {"blank-page-start", "xsl-region-start"},
+            {"blank-page-end", "xsl-region-end"},
+
+            {"last-page-before", "xsl-region-before"},
+            {"last-page-after", "xsl-region-after"},
+            {"last-page-start", "xsl-region-start"},
+            {"last-page-end", "xsl-region-end"},
+
+            {"xsl-footnote-separator", "xsl-footnote-separator"}
+
+        };
+
+        FlowMappingTester(FOUserAgent userAgent) {
+            super(userAgent);
+        }
+
+        @Override
+        public void startPageSequence(PageSequence pageSeq) {
+            super.startPageSequence(pageSeq);
+            LayoutMasterSet layoutMasterSet = pageSeq.getRoot().getLayoutMasterSet();
+            for (String[] mapping : FLOW_MAPPINGS) {
+                assertEquals(mapping[1], layoutMasterSet.getDefaultRegionNameFor(mapping[0]));
+            }
+        }
+
+    }
+
+    /**
+     * Tests the {@link LayoutMasterSet#getDefaultRegionNameFor(String)} method.
+     */
+    @Test
+    public void testFlowMapping() throws Exception {
+        FODocumentParser foDocumentParser = FODocumentParser.newInstance(new FOEventHandlerFactory() {
+
+            public FOEventHandler newFOEventHandler(FOUserAgent foUserAgent) {
+                return new FlowMappingTester(foUserAgent);
+            }
+        });
+        foDocumentParser.parse(getClass().getResourceAsStream("side-regions.fo"));
+    }
+
+}
diff --git a/test/java/org/apache/fop/fo/pagination/side-regions.fo b/test/java/org/apache/fop/fo/pagination/side-regions.fo
new file mode 100644 (file)
index 0000000..8a0fba2
--- /dev/null
@@ -0,0 +1,181 @@
+<?xml version="1.0" standalone="no"?>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+  <fo:layout-master-set>
+    <fo:simple-page-master master-name="first-page"
+      page-height="100pt" page-width="150pt">
+      <fo:region-body margin="12pt" display-align="center" background-color="#FFF0F0"/>
+      <fo:region-before region-name="first-page-before" extent="10pt" precedence="true" 
+        display-align="after"/>
+      <fo:region-after region-name="first-page-after" extent="10pt" precedence="true"/>
+      <fo:region-start region-name="first-page-start" extent="10pt" reference-orientation="90" 
+        display-align="after"/>
+      <fo:region-end region-name="first-page-end" extent="10pt" reference-orientation="-90" 
+        display-align="after"/>
+    </fo:simple-page-master>
+    <fo:simple-page-master master-name="odd-page"
+      page-height="100pt" page-width="150pt">
+      <fo:region-body margin="12pt" display-align="center" background-color="#FFFFF0"/>
+      <fo:region-before region-name="odd-page-before" extent="10pt" precedence="true" 
+        display-align="after"/>
+      <fo:region-after region-name="odd-page-after" extent="10pt" precedence="true"/>
+      <fo:region-start region-name="odd-page-start" extent="10pt" reference-orientation="90" 
+        display-align="after"/>
+      <fo:region-end region-name="odd-page-end" extent="10pt" reference-orientation="-90" 
+        display-align="after"/>
+    </fo:simple-page-master>
+    <fo:simple-page-master master-name="even-page"
+      page-height="100pt" page-width="150pt">
+      <fo:region-body margin="12pt" display-align="center" background-color="#F0FFF0"/>
+      <fo:region-before region-name="even-page-before" extent="10pt" precedence="true" 
+        display-align="after"/>
+      <fo:region-after region-name="even-page-after" extent="10pt" precedence="true"/>
+      <fo:region-start region-name="even-page-start" extent="10pt" reference-orientation="90" 
+        display-align="after"/>
+      <fo:region-end region-name="even-page-end" extent="10pt" reference-orientation="-90" 
+        display-align="after"/>
+    </fo:simple-page-master>
+    <fo:simple-page-master master-name="blank-page"
+      page-height="100pt" page-width="150pt">
+      <fo:region-body margin="12pt" display-align="center" background-color="#F0F0F0"/>
+      <fo:region-before region-name="blank-page-before" extent="10pt" precedence="true" 
+        display-align="after"/>
+      <fo:region-after region-name="blank-page-after" extent="10pt" precedence="true"/>
+      <fo:region-start region-name="blank-page-start" extent="10pt" reference-orientation="90" 
+        display-align="after"/>
+      <fo:region-end region-name="blank-page-end" extent="10pt" reference-orientation="-90" 
+        display-align="after"/>
+    </fo:simple-page-master>
+    <fo:simple-page-master master-name="last-page"
+      page-height="100pt" page-width="150pt">
+      <fo:region-body margin="45pt 12pt" display-align="center" background-color="#F0F0FF"/>
+      <fo:region-before region-name="last-page-before" extent="10pt" precedence="true" 
+        display-align="after"/>
+      <fo:region-after region-name="last-page-after" extent="10pt" precedence="true"/>
+      <fo:region-start region-name="last-page-start" extent="10pt" reference-orientation="90" 
+        display-align="after"/>
+      <fo:region-end region-name="last-page-end" extent="10pt" reference-orientation="-90" 
+        display-align="after"/>
+    </fo:simple-page-master>
+    <fo:page-sequence-master master-name="pages">
+      <fo:repeatable-page-master-alternatives>
+        <fo:conditional-page-master-reference page-position="first" master-reference="first-page"/>
+        <fo:conditional-page-master-reference page-position="last" master-reference="last-page"/>
+        <fo:conditional-page-master-reference blank-or-not-blank="blank" 
+          master-reference="blank-page"/>
+        <fo:conditional-page-master-reference odd-or-even="odd" master-reference="odd-page"/>
+        <fo:conditional-page-master-reference odd-or-even="even" master-reference="even-page"/>
+      </fo:repeatable-page-master-alternatives>
+    </fo:page-sequence-master>
+  </fo:layout-master-set>
+  <fo:page-sequence master-reference="pages" force-page-count="even" font-size="4pt" 
+    text-align="center">
+
+    <fo:static-content flow-name="first-page-before">
+      <fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid red" 
+        padding-bottom="0.5pt">First Page Before.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="first-page-after">
+      <fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid red" 
+        padding-top="0.5pt">First Page After.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="first-page-start">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid red" 
+        padding-bottom="0.5pt">First Page Start.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="first-page-end">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid red" 
+        padding-bottom="0.5pt">First Page End.</fo:block>
+    </fo:static-content>
+
+    <fo:static-content flow-name="odd-page-after">
+      <fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid orange" 
+        padding-top="0.5pt">Odd Page After.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="odd-page-end">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid orange" 
+        padding-bottom="0.5pt">Odd Page End.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="odd-page-start">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid orange" 
+        padding-bottom="0.5pt">Odd Page Start.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="odd-page-before">
+      <fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid orange" 
+        padding-bottom="0.5pt">Odd Page Before.</fo:block>
+    </fo:static-content>
+
+    <fo:static-content flow-name="even-page-end">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid green" 
+        padding-bottom="0.5pt">Even Page End.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="even-page-start">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid green" 
+        padding-bottom="0.5pt">Even Page Start.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="even-page-after">
+      <fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid green" 
+        padding-top="0.5pt">Even Page After.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="even-page-before">
+      <fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid green" 
+        padding-bottom="0.5pt">Even Page Before.</fo:block>
+    </fo:static-content>
+
+    <fo:static-content flow-name="blank-page-start">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid black" 
+        padding-bottom="0.5pt">Blank Page Start.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="blank-page-after">
+      <fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid black" 
+        padding-top="0.5pt">Blank Page After.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="blank-page-before">
+      <fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid black" 
+        padding-bottom="0.5pt">Blank Page Before.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="blank-page-end">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid black" 
+        padding-bottom="0.5pt">Blank Page End.</fo:block>
+    </fo:static-content>
+
+    <fo:static-content flow-name="last-page-before">
+      <fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid blue" 
+        padding-bottom="0.5pt">Last Page Before.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="last-page-end">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid blue" 
+        padding-bottom="0.5pt">Last Page End.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="last-page-after">
+      <fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid blue" 
+        padding-top="0.5pt">Last Page After.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="last-page-start">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid blue" 
+        padding-bottom="0.5pt">Last Page Start.</fo:block>
+    </fo:static-content>
+
+    <fo:static-content flow-name="xsl-footnote-separator">
+      <fo:block>
+        <fo:leader leader-pattern="rule" leader-length="40%" rule-thickness="0.5pt"/>
+      </fo:block>
+    </fo:static-content>
+
+    <fo:flow flow-name="xsl-region-body" font-size="8pt" line-height="10pt">
+      <fo:block>Apache™ FOP (Formatting Objects Processor) is a print formatter driven by XSL 
+        formatting objects (XSL-FO) and an output independent formatter.</fo:block>
+      <fo:block break-before="page">It is an application<fo:footnote><fo:inline>*</fo:inline> 
+          <fo:footnote-body><fo:block font-size="80%">* written in 
+              Java</fo:block></fo:footnote-body></fo:footnote> that reads a formatting object (FO) 
+        tree and renders the resulting pages to a specified output.</fo:block>
+      <fo:block break-before="page">The FOP project is part of the Apache Software Foundation, which 
+        is a wider community of users and developers of open source projects.</fo:block>
+      <fo:block break-before="page">Apache™ FOP (Formatting Objects Processor) is a print formatter 
+        driven by XSL formatting objects (XSL-FO) and an output independent formatter.</fo:block>
+      <fo:block break-before="page">It is a Java application that reads a formatting object (FO) 
+        tree and renders the resulting pages to a specified output.</fo:block>
+      <fo:block break-before="page">The FOP project is part of the Apache Software Foundation, which 
+        is a wider community of users and developers of open source projects.</fo:block>
+    </fo:flow>
+  </fo:page-sequence>
+</fo:root>
index 89682628d19297ffb9080ed5956868a311e02ca1..a1d581402228a8cd6ddfb9805ddab9e19aba16b8 100644 (file)
@@ -52,7 +52,8 @@ public class TableHeaderScopeTestCase {
         VersionController controller = mock(VersionController.class);
         PDFDocument document = new PDFDocument("Test", controller);
         document.makeStructTreeRoot(null);
-        document.makeStructureElement(Table.TH, null, scope);
+        PDFStructElem th = new PDFStructElem(null, Table.TH);
+        document.registerStructureElement(th, scope);
         verify(controller).addTableHeaderScopeAttribute(any(PDFStructElem.class), eq(scope));
     }
 
index 4dfb686c9f962f298f1b1611ca221d67eb0a300d..8fb665b796ed86597862f5fa584a36b64bf36138 100644 (file)
Binary files a/test/pdf/accessibility/pdf/role.pdf and b/test/pdf/accessibility/pdf/role.pdf differ
index 6f1edea19c4c17675d5e0a8403af0b1e2416743b..9effef7935ed8f61e84311a4ad5ff6c85a09deb6 100644 (file)
Binary files a/test/pdf/accessibility/pdf/role_non-standard.pdf and b/test/pdf/accessibility/pdf/role_non-standard.pdf differ
diff --git a/test/pdf/accessibility/pdf/side-regions.pdf b/test/pdf/accessibility/pdf/side-regions.pdf
new file mode 100644 (file)
index 0000000..6e5da93
Binary files /dev/null and b/test/pdf/accessibility/pdf/side-regions.pdf differ
diff --git a/test/pdf/accessibility/side-regions.fo b/test/pdf/accessibility/side-regions.fo
new file mode 100644 (file)
index 0000000..8a0fba2
--- /dev/null
@@ -0,0 +1,181 @@
+<?xml version="1.0" standalone="no"?>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+  <fo:layout-master-set>
+    <fo:simple-page-master master-name="first-page"
+      page-height="100pt" page-width="150pt">
+      <fo:region-body margin="12pt" display-align="center" background-color="#FFF0F0"/>
+      <fo:region-before region-name="first-page-before" extent="10pt" precedence="true" 
+        display-align="after"/>
+      <fo:region-after region-name="first-page-after" extent="10pt" precedence="true"/>
+      <fo:region-start region-name="first-page-start" extent="10pt" reference-orientation="90" 
+        display-align="after"/>
+      <fo:region-end region-name="first-page-end" extent="10pt" reference-orientation="-90" 
+        display-align="after"/>
+    </fo:simple-page-master>
+    <fo:simple-page-master master-name="odd-page"
+      page-height="100pt" page-width="150pt">
+      <fo:region-body margin="12pt" display-align="center" background-color="#FFFFF0"/>
+      <fo:region-before region-name="odd-page-before" extent="10pt" precedence="true" 
+        display-align="after"/>
+      <fo:region-after region-name="odd-page-after" extent="10pt" precedence="true"/>
+      <fo:region-start region-name="odd-page-start" extent="10pt" reference-orientation="90" 
+        display-align="after"/>
+      <fo:region-end region-name="odd-page-end" extent="10pt" reference-orientation="-90" 
+        display-align="after"/>
+    </fo:simple-page-master>
+    <fo:simple-page-master master-name="even-page"
+      page-height="100pt" page-width="150pt">
+      <fo:region-body margin="12pt" display-align="center" background-color="#F0FFF0"/>
+      <fo:region-before region-name="even-page-before" extent="10pt" precedence="true" 
+        display-align="after"/>
+      <fo:region-after region-name="even-page-after" extent="10pt" precedence="true"/>
+      <fo:region-start region-name="even-page-start" extent="10pt" reference-orientation="90" 
+        display-align="after"/>
+      <fo:region-end region-name="even-page-end" extent="10pt" reference-orientation="-90" 
+        display-align="after"/>
+    </fo:simple-page-master>
+    <fo:simple-page-master master-name="blank-page"
+      page-height="100pt" page-width="150pt">
+      <fo:region-body margin="12pt" display-align="center" background-color="#F0F0F0"/>
+      <fo:region-before region-name="blank-page-before" extent="10pt" precedence="true" 
+        display-align="after"/>
+      <fo:region-after region-name="blank-page-after" extent="10pt" precedence="true"/>
+      <fo:region-start region-name="blank-page-start" extent="10pt" reference-orientation="90" 
+        display-align="after"/>
+      <fo:region-end region-name="blank-page-end" extent="10pt" reference-orientation="-90" 
+        display-align="after"/>
+    </fo:simple-page-master>
+    <fo:simple-page-master master-name="last-page"
+      page-height="100pt" page-width="150pt">
+      <fo:region-body margin="45pt 12pt" display-align="center" background-color="#F0F0FF"/>
+      <fo:region-before region-name="last-page-before" extent="10pt" precedence="true" 
+        display-align="after"/>
+      <fo:region-after region-name="last-page-after" extent="10pt" precedence="true"/>
+      <fo:region-start region-name="last-page-start" extent="10pt" reference-orientation="90" 
+        display-align="after"/>
+      <fo:region-end region-name="last-page-end" extent="10pt" reference-orientation="-90" 
+        display-align="after"/>
+    </fo:simple-page-master>
+    <fo:page-sequence-master master-name="pages">
+      <fo:repeatable-page-master-alternatives>
+        <fo:conditional-page-master-reference page-position="first" master-reference="first-page"/>
+        <fo:conditional-page-master-reference page-position="last" master-reference="last-page"/>
+        <fo:conditional-page-master-reference blank-or-not-blank="blank" 
+          master-reference="blank-page"/>
+        <fo:conditional-page-master-reference odd-or-even="odd" master-reference="odd-page"/>
+        <fo:conditional-page-master-reference odd-or-even="even" master-reference="even-page"/>
+      </fo:repeatable-page-master-alternatives>
+    </fo:page-sequence-master>
+  </fo:layout-master-set>
+  <fo:page-sequence master-reference="pages" force-page-count="even" font-size="4pt" 
+    text-align="center">
+
+    <fo:static-content flow-name="first-page-before">
+      <fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid red" 
+        padding-bottom="0.5pt">First Page Before.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="first-page-after">
+      <fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid red" 
+        padding-top="0.5pt">First Page After.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="first-page-start">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid red" 
+        padding-bottom="0.5pt">First Page Start.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="first-page-end">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid red" 
+        padding-bottom="0.5pt">First Page End.</fo:block>
+    </fo:static-content>
+
+    <fo:static-content flow-name="odd-page-after">
+      <fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid orange" 
+        padding-top="0.5pt">Odd Page After.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="odd-page-end">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid orange" 
+        padding-bottom="0.5pt">Odd Page End.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="odd-page-start">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid orange" 
+        padding-bottom="0.5pt">Odd Page Start.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="odd-page-before">
+      <fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid orange" 
+        padding-bottom="0.5pt">Odd Page Before.</fo:block>
+    </fo:static-content>
+
+    <fo:static-content flow-name="even-page-end">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid green" 
+        padding-bottom="0.5pt">Even Page End.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="even-page-start">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid green" 
+        padding-bottom="0.5pt">Even Page Start.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="even-page-after">
+      <fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid green" 
+        padding-top="0.5pt">Even Page After.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="even-page-before">
+      <fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid green" 
+        padding-bottom="0.5pt">Even Page Before.</fo:block>
+    </fo:static-content>
+
+    <fo:static-content flow-name="blank-page-start">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid black" 
+        padding-bottom="0.5pt">Blank Page Start.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="blank-page-after">
+      <fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid black" 
+        padding-top="0.5pt">Blank Page After.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="blank-page-before">
+      <fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid black" 
+        padding-bottom="0.5pt">Blank Page Before.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="blank-page-end">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid black" 
+        padding-bottom="0.5pt">Blank Page End.</fo:block>
+    </fo:static-content>
+
+    <fo:static-content flow-name="last-page-before">
+      <fo:block start-indent="12pt" end-indent="12pt" border-bottom="0.5pt solid blue" 
+        padding-bottom="0.5pt">Last Page Before.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="last-page-end">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid blue" 
+        padding-bottom="0.5pt">Last Page End.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="last-page-after">
+      <fo:block start-indent="12pt" end-indent="12pt" border-top="0.5pt solid blue" 
+        padding-top="0.5pt">Last Page After.</fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="last-page-start">
+      <fo:block start-indent="2pt" end-indent="2pt" border-bottom="0.5pt solid blue" 
+        padding-bottom="0.5pt">Last Page Start.</fo:block>
+    </fo:static-content>
+
+    <fo:static-content flow-name="xsl-footnote-separator">
+      <fo:block>
+        <fo:leader leader-pattern="rule" leader-length="40%" rule-thickness="0.5pt"/>
+      </fo:block>
+    </fo:static-content>
+
+    <fo:flow flow-name="xsl-region-body" font-size="8pt" line-height="10pt">
+      <fo:block>Apache™ FOP (Formatting Objects Processor) is a print formatter driven by XSL 
+        formatting objects (XSL-FO) and an output independent formatter.</fo:block>
+      <fo:block break-before="page">It is an application<fo:footnote><fo:inline>*</fo:inline> 
+          <fo:footnote-body><fo:block font-size="80%">* written in 
+              Java</fo:block></fo:footnote-body></fo:footnote> that reads a formatting object (FO) 
+        tree and renders the resulting pages to a specified output.</fo:block>
+      <fo:block break-before="page">The FOP project is part of the Apache Software Foundation, which 
+        is a wider community of users and developers of open source projects.</fo:block>
+      <fo:block break-before="page">Apache™ FOP (Formatting Objects Processor) is a print formatter 
+        driven by XSL formatting objects (XSL-FO) and an output independent formatter.</fo:block>
+      <fo:block break-before="page">It is a Java application that reads a formatting object (FO) 
+        tree and renders the resulting pages to a specified output.</fo:block>
+      <fo:block break-before="page">The FOP project is part of the Apache Software Foundation, which 
+        is a wider community of users and developers of open source projects.</fo:block>
+    </fo:flow>
+  </fo:page-sequence>
+</fo:root>