Browse Source

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
tags/fop-2_0
Vincent Hennebert 11 years ago
parent
commit
69beec0c8f
30 changed files with 1044 additions and 945 deletions
  1. 14
    27
      src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java
  2. 0
    508
      src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java
  3. 26
    11
      src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java
  4. 10
    10
      src/java/org/apache/fop/fo/DelegatingFOEventHandler.java
  5. 14
    16
      src/java/org/apache/fop/fo/FOEventHandler.java
  6. 2
    1
      src/java/org/apache/fop/fo/FOPropertyMapping.java
  7. 4
    1
      src/java/org/apache/fop/fo/pagination/Flow.java
  8. 19
    0
      src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java
  9. 1
    1
      src/java/org/apache/fop/fo/pagination/StaticContent.java
  10. 6
    22
      src/java/org/apache/fop/pdf/PDFDocument.java
  11. 4
    2
      src/java/org/apache/fop/pdf/PDFStructElem.java
  12. 2
    1
      src/java/org/apache/fop/pdf/PDFStructTreeRoot.java
  13. 5
    0
      src/java/org/apache/fop/pdf/StandardStructureTypes.java
  14. 37
    0
      src/java/org/apache/fop/pdf/StructureHierarchyMember.java
  15. 0
    162
      src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java
  16. 1
    2
      src/java/org/apache/fop/render/pdf/PDFEventProducer.java
  17. 271
    60
      src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java
  18. 79
    0
      src/java/org/apache/fop/render/pdf/PageSequenceStructElem.java
  19. 48
    0
      src/java/org/apache/fop/render/pdf/TableStructElem.java
  20. 4
    0
      status.xml
  21. 18
    45
      test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java
  22. 20
    9
      test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl
  23. 0
    66
      test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl
  24. 95
    0
      test/java/org/apache/fop/fo/pagination/LayoutMasterSetTestCase.java
  25. 181
    0
      test/java/org/apache/fop/fo/pagination/side-regions.fo
  26. 2
    1
      test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java
  27. BIN
      test/pdf/accessibility/pdf/role.pdf
  28. BIN
      test/pdf/accessibility/pdf/role_non-standard.pdf
  29. BIN
      test/pdf/accessibility/pdf/side-regions.pdf
  30. 181
    0
      test/pdf/accessibility/side-regions.fo

+ 14
- 27
src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java View 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();

+ 0
- 508
src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java View File

@@ -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);
}
});
}

}

+ 26
- 11
src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java View 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);

+ 10
- 10
src/java/org/apache/fop/fo/DelegatingFOEventHandler.java View 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();

+ 14
- 16
src/java/org/apache/fop/fo/FOEventHandler.java View 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.
*/

+ 2
- 1
src/java/org/apache/fop/fo/FOPropertyMapping.java View 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);

+ 4
- 1
src/java/org/apache/fop/fo/pagination/Flow.java View 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,

+ 19
- 0
src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java View 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;
}
}


+ 1
- 1
src/java/org/apache/fop/fo/pagination/StaticContent.java View 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);
}

+ 6
- 22
src/java/org/apache/fop/pdf/PDFDocument.java View 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;
}

/**

+ 4
- 2
src/java/org/apache/fop/pdf/PDFStructElem.java View 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>();

+ 2
- 1
src/java/org/apache/fop/pdf/PDFStructTreeRoot.java View 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);
}

+ 5
- 0
src/java/org/apache/fop/pdf/StandardStructureTypes.java View 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>();

+ 37
- 0
src/java/org/apache/fop/pdf/StructureHierarchyMember.java View File

@@ -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);

}

+ 0
- 162
src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java View File

@@ -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() { }
}

+ 1
- 2
src/java/org/apache/fop/render/pdf/PDFEventProducer.java View 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.

+ 271
- 60
src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java View 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);
}

}

+ 79
- 0
src/java/org/apache/fop/render/pdf/PageSequenceStructElem.java View File

@@ -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);
}
}
}

}

+ 48
- 0
src/java/org/apache/fop/render/pdf/TableStructElem.java View File

@@ -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();
}

}

+ 4
- 0
status.xml View File

@@ -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>

+ 18
- 45
test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java View 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);

+ 20
- 9
test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl View File

@@ -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>


+ 0
- 66
test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl View File

@@ -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>

+ 95
- 0
test/java/org/apache/fop/fo/pagination/LayoutMasterSetTestCase.java View File

@@ -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"));
}

}

+ 181
- 0
test/java/org/apache/fop/fo/pagination/side-regions.fo View File

@@ -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>

+ 2
- 1
test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java View 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));
}


BIN
test/pdf/accessibility/pdf/role.pdf View File


BIN
test/pdf/accessibility/pdf/role_non-standard.pdf View File


BIN
test/pdf/accessibility/pdf/side-regions.pdf View File


+ 181
- 0
test/pdf/accessibility/side-regions.fo View File

@@ -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>

Loading…
Cancel
Save