diff options
author | Vincent Hennebert <vhennebert@apache.org> | 2012-08-24 14:10:39 +0000 |
---|---|---|
committer | Vincent Hennebert <vhennebert@apache.org> | 2012-08-24 14:10:39 +0000 |
commit | 69beec0c8ff0bb191c459184bc61b6a243ba693b (patch) | |
tree | bd14b44cd8fed471d3aa2d7c8aaf80fb40196c3b | |
parent | d6994df393ced4dd88e5edd5fdfdca38df0d875a (diff) | |
download | xmlgraphics-fop-69beec0c8ff0bb191c459184bc61b6a243ba693b.tar.gz xmlgraphics-fop-69beec0c8ff0bb191c459184bc61b6a243ba693b.zip |
Bugzilla 53778: When PDF accessibility is enabled, the contents for the different regions must appear in the proper order in the structure tree.
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1376923 13f79535-47bb-0310-9956-ffa450edef68
30 files changed, 1044 insertions, 945 deletions
diff --git a/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java b/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java index 27469d6e0..aaf112cb5 100644 --- a/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java +++ b/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java @@ -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. */ @@ -166,6 +164,20 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { } @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); super.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); } @@ -357,20 +358,6 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { } @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(); super.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 index b2b18046d..000000000 --- a/src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java +++ /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); - } - }); - } - -} diff --git a/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java b/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java index 7e3ed0591..93b815d30 100644 --- a/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java +++ b/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java @@ -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) { @@ -129,8 +135,27 @@ class StructureTreeEventTrigger extends FOEventHandler { } @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 @@ -278,16 +303,6 @@ class StructureTreeEventTrigger extends FOEventHandler { } @Override - public void startStatic(StaticContent staticContent) { - startElement(staticContent); - } - - @Override - public void endStatic(StaticContent statisContent) { - endElement(statisContent); - } - - @Override public void startLink(BasicLink basicLink) { startElementWithID(basicLink); } diff --git a/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java b/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java index 50026c996..bb0a1ba75 100644 --- a/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java +++ b/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java @@ -143,6 +143,16 @@ public abstract class DelegatingFOEventHandler extends FOEventHandler { } @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); } @@ -293,16 +303,6 @@ public abstract class DelegatingFOEventHandler extends FOEventHandler { } @Override - public void startStatic(StaticContent staticContent) { - delegate.startStatic(staticContent); - } - - @Override - public void endStatic(StaticContent statisContent) { - delegate.endStatic(statisContent); - } - - @Override public void startMarkup() { delegate.startMarkup(); } diff --git a/src/java/org/apache/fop/fo/FOEventHandler.java b/src/java/org/apache/fop/fo/FOEventHandler.java index 11b6d4a2f..ad647e42a 100644 --- a/src/java/org/apache/fop/fo/FOEventHandler.java +++ b/src/java/org/apache/fop/fo/FOEventHandler.java @@ -193,6 +193,20 @@ public abstract class FOEventHandler { } /** + * 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. * This method also handles fo:static-content tags, because the @@ -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. */ diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index b29571b09..d0d13fc17 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -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); diff --git a/src/java/org/apache/fop/fo/pagination/Flow.java b/src/java/org/apache/fop/fo/pagination/Flow.java index 981485e32..45c8f7804 100644 --- a/src/java/org/apache/fop/fo/pagination/Flow.java +++ b/src/java/org/apache/fop/fo/pagination/Flow.java @@ -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, diff --git a/src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java b/src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java index 533b113d2..8b109dd0e 100644 --- a/src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java +++ b/src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java @@ -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; + } } diff --git a/src/java/org/apache/fop/fo/pagination/StaticContent.java b/src/java/org/apache/fop/fo/pagination/StaticContent.java index e70c80c5d..412cdbcca 100644 --- a/src/java/org/apache/fop/fo/pagination/StaticContent.java +++ b/src/java/org/apache/fop/fo/pagination/StaticContent.java @@ -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); } diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java index e46a22c4a..0b412842f 100644 --- a/src/java/org/apache/fop/pdf/PDFDocument.java +++ b/src/java/org/apache/fop/pdf/PDFDocument.java @@ -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; } /** diff --git a/src/java/org/apache/fop/pdf/PDFStructElem.java b/src/java/org/apache/fop/pdf/PDFStructElem.java index 28cebb3ee..8250318d7 100644 --- a/src/java/org/apache/fop/pdf/PDFStructElem.java +++ b/src/java/org/apache/fop/pdf/PDFStructElem.java @@ -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>(); diff --git a/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java b/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java index 5f19bb2e2..ca6d82d22 100644 --- a/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java +++ b/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java @@ -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); } diff --git a/src/java/org/apache/fop/pdf/StandardStructureTypes.java b/src/java/org/apache/fop/pdf/StandardStructureTypes.java index dc045e180..69550119a 100644 --- a/src/java/org/apache/fop/pdf/StandardStructureTypes.java +++ b/src/java/org/apache/fop/pdf/StandardStructureTypes.java @@ -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 index 000000000..e3be92102 --- /dev/null +++ b/src/java/org/apache/fop/pdf/StructureHierarchyMember.java @@ -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 index 68bfd8686..000000000 --- a/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java +++ /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() { } -} diff --git a/src/java/org/apache/fop/render/pdf/PDFEventProducer.java b/src/java/org/apache/fop/render/pdf/PDFEventProducer.java index 40062f73f..4b8253867 100644 --- a/src/java/org/apache/fop/render/pdf/PDFEventProducer.java +++ b/src/java/org/apache/fop/render/pdf/PDFEventProducer.java @@ -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. diff --git a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java index 1377bbfce..0d4a6b7fb 100644 --- a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java +++ b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java @@ -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 index 000000000..09d5b81a2 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/PageSequenceStructElem.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.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 index 000000000..c44acb25c --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/TableStructElem.java @@ -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(); + } + +} diff --git a/status.xml b/status.xml index 5927dd82c..270496f59 100644 --- a/status.xml +++ b/status.xml @@ -62,6 +62,10 @@ 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> diff --git a/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java b/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java index 863bfe797..6092912f9 100644 --- a/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java +++ b/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java @@ -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); diff --git a/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl b/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl index db0dffb14..c739462e4 100644 --- a/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl +++ b/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl @@ -50,6 +50,25 @@ <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"/> @@ -73,15 +92,7 @@ <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 index 9608b2fb9..000000000 --- a/test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl +++ /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 index 000000000..cfe71f529 --- /dev/null +++ b/test/java/org/apache/fop/fo/pagination/LayoutMasterSetTestCase.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.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 index 000000000..8a0fba2ec --- /dev/null +++ b/test/java/org/apache/fop/fo/pagination/side-regions.fo @@ -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> diff --git a/test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java b/test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java index 89682628d..a1d581402 100644 --- a/test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java +++ b/test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java @@ -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)); } diff --git a/test/pdf/accessibility/pdf/role.pdf b/test/pdf/accessibility/pdf/role.pdf Binary files differindex 4dfb686c9..8fb665b79 100644 --- a/test/pdf/accessibility/pdf/role.pdf +++ b/test/pdf/accessibility/pdf/role.pdf diff --git a/test/pdf/accessibility/pdf/role_non-standard.pdf b/test/pdf/accessibility/pdf/role_non-standard.pdf Binary files differindex 6f1edea19..9effef793 100644 --- a/test/pdf/accessibility/pdf/role_non-standard.pdf +++ b/test/pdf/accessibility/pdf/role_non-standard.pdf diff --git a/test/pdf/accessibility/pdf/side-regions.pdf b/test/pdf/accessibility/pdf/side-regions.pdf Binary files differnew file mode 100644 index 000000000..6e5da9393 --- /dev/null +++ b/test/pdf/accessibility/pdf/side-regions.pdf diff --git a/test/pdf/accessibility/side-regions.fo b/test/pdf/accessibility/side-regions.fo new file mode 100644 index 000000000..8a0fba2ec --- /dev/null +++ b/test/pdf/accessibility/side-regions.fo @@ -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> |