Browse Source

Added support for the role property on fo:page-sequence, fo:flow and fo:static-content.

This allows to change the mapping of those FOs to PDF structure types when enabling accessibility.

Suggested by Martin Koegler as part of Bugzilla #50852


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1342680 13f79535-47bb-0310-9956-ffa450edef68
tags/fop-1_1rc1old
Vincent Hennebert 12 years ago
parent
commit
89b98e43e0

+ 1
- 1
src/java/org/apache/fop/accessibility/DummyStructureTreeEventHandler.java View File

@@ -34,7 +34,7 @@ public final class DummyStructureTreeEventHandler implements StructureTreeEventH
private DummyStructureTreeEventHandler() { }

/** {@inheritDoc} */
public void startPageSequence(Locale locale) {
public void startPageSequence(Locale locale, String role) {
}

/** {@inheritDoc} */

+ 7
- 3
src/java/org/apache/fop/accessibility/StructureTree2SAXEventAdapter.java View File

@@ -30,6 +30,7 @@ import org.apache.fop.fo.FOElementMapping;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fo.extensions.InternalElementMapping;
import org.apache.fop.render.intermediate.IFConstants;
import org.apache.fop.util.XMLConstants;

/**
* Converts structure tree events to SAX events.
@@ -52,16 +53,19 @@ public final class StructureTree2SAXEventAdapter implements StructureTreeEventHa
}

/** {@inheritDoc} */
public void startPageSequence(Locale locale) {
public void startPageSequence(Locale locale, String role) {
try {

AttributesImpl attributes = new AttributesImpl();
if (role != null) {
attributes.addAttribute("", "type", "type", XMLConstants.CDATA, role);
}
contentHandler.startPrefixMapping(
InternalElementMapping.STANDARD_PREFIX, InternalElementMapping.URI);
contentHandler.startPrefixMapping(
ExtensionElementMapping.STANDARD_PREFIX, ExtensionElementMapping.URI);
contentHandler.startElement(IFConstants.NAMESPACE,
IFConstants.EL_STRUCTURE_TREE, IFConstants.EL_STRUCTURE_TREE,
new AttributesImpl());
attributes);
} catch (SAXException e) {
throw new RuntimeException(e);
}

+ 2
- 1
src/java/org/apache/fop/accessibility/StructureTreeEventHandler.java View File

@@ -34,8 +34,9 @@ public interface StructureTreeEventHandler {
* Starts a page sequence structure tree node.
*
* @param locale The locale of the page sequence
* @param role the value of the role property. May be null.
*/
void startPageSequence(Locale locale);
void startPageSequence(Locale locale, String role);

/**
* Starts a structure tree node.

+ 2
- 1
src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java View File

@@ -89,7 +89,8 @@ class StructureTreeEventTrigger extends FOEventHandler {
locale = new Locale(pageSeq.getLanguage());
}
}
structureTreeEventHandler.startPageSequence(locale);
String role = pageSeq.getCommonAccessibility().getRole();
structureTreeEventHandler.startPageSequence(locale, role);
}

@Override

+ 11
- 4
src/java/org/apache/fop/fo/pagination/AbstractPageSequence.java View File

@@ -24,6 +24,8 @@ import org.apache.fop.datatypes.Numeric;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.properties.CommonAccessibility;
import org.apache.fop.fo.properties.CommonAccessibilityHolder;

/**
* Abstract base class for the <a href="http://www.w3.org/TR/xsl/#fo_page-sequence">
@@ -31,9 +33,8 @@ import org.apache.fop.fo.PropertyList;
* <a href="http://xmlgraphics.apache.org/fop/0.95/extensions.html#external-document">
* <code>fox:external-document</code></a> extension object.
*/
public abstract class AbstractPageSequence extends FObj {
public abstract class AbstractPageSequence extends FObj implements CommonAccessibilityHolder {

// The value of properties relevant for fo:page-sequence.
/** initial page number */
protected Numeric initialPageNumber;
/** forced page count */
@@ -42,11 +43,12 @@ public abstract class AbstractPageSequence extends FObj {
private int letterValue;
private char groupingSeparator;
private int groupingSize;
private Numeric referenceOrientation; //XSL 1.1
private Numeric referenceOrientation;
private String language;
private String country;
private String numberConversionFeatures;
// End of property values

private CommonAccessibility commonAccessibility;

private PageNumberGenerator pageNumberGenerator;

@@ -76,6 +78,7 @@ public abstract class AbstractPageSequence extends FObj {
language = pList.get(PR_LANGUAGE).getString();
country = pList.get(PR_COUNTRY).getString();
numberConversionFeatures = pList.get(PR_X_NUMBER_CONVERSION_FEATURES).getString();
commonAccessibility = CommonAccessibility.getInstance(pList);
}

/** {@inheritDoc} */
@@ -130,6 +133,10 @@ public abstract class AbstractPageSequence extends FObj {
return pageNumberGenerator.makeFormattedPageNumber(pageNumber);
}

public CommonAccessibility getCommonAccessibility() {
return commonAccessibility;
}

/**
* Public accessor for the ancestor Root.
* @return the ancestor Root

+ 11
- 3
src/java/org/apache/fop/fo/pagination/Flow.java View File

@@ -26,16 +26,19 @@ import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.properties.CommonAccessibility;
import org.apache.fop.fo.properties.CommonAccessibilityHolder;

/**
* Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_flow">
* <code>fo:flow</code></a> object.
*
*/
public class Flow extends FObj {
// The value of properties relevant for fo:flow.
public class Flow extends FObj implements CommonAccessibilityHolder {
private String flowName;
// End of property values

private CommonAccessibility commonAccessibility;

/** used for FO validation */
private boolean blockItemFound = false;
@@ -52,6 +55,7 @@ public class Flow extends FObj {
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
flowName = pList.get(PR_FLOW_NAME).getString();
commonAccessibility = CommonAccessibility.getInstance(pList);
}

/** {@inheritDoc} */
@@ -120,6 +124,10 @@ public class Flow extends FObj {
return flowName;
}

public CommonAccessibility getCommonAccessibility() {
return commonAccessibility;
}

/** {@inheritDoc} */
public String getLocalName() {
return "flow";

+ 10
- 2
src/java/org/apache/fop/render/intermediate/IFParser.java View File

@@ -156,7 +156,7 @@ public class IFParser implements IFConstants {

private ContentHandler navParser;

private ContentHandler structureTreeHandler;
private StructureTreeHandler structureTreeHandler;

private Attributes pageSequenceAttributes;

@@ -165,12 +165,18 @@ public class IFParser implements IFConstants {

private final class StructureTreeHandler extends DefaultHandler {

private final Locale pageSequenceLanguage;

private final StructureTreeEventHandler structureTreeEventHandler;

private StructureTreeHandler(StructureTreeEventHandler structureTreeEventHandler,
Locale pageSequenceLanguage) throws SAXException {
this.pageSequenceLanguage = pageSequenceLanguage;
this.structureTreeEventHandler = structureTreeEventHandler;
structureTreeEventHandler.startPageSequence(pageSequenceLanguage);
}

void startStructureTree(String type) {
structureTreeEventHandler.startPageSequence(pageSequenceLanguage, type);
}

public void endDocument() throws SAXException {
@@ -263,6 +269,8 @@ public class IFParser implements IFConstants {

} else if (localName.equals(EL_STRUCTURE_TREE)) {
if (userAgent.isAccessibilityEnabled()) {
String type = attributes.getValue("type");
structureTreeHandler.startStructureTree(type);
delegate = structureTreeHandler;
} else {
/* Delegate to a handler that does nothing */

+ 2
- 2
src/java/org/apache/fop/render/intermediate/IFStructureTreeBuilder.java View File

@@ -189,11 +189,11 @@ final class IFStructureTreeBuilder implements StructureTreeEventHandler {
pageSequenceEventRecorders.get(pageSequenceIndex).replay(handler);
}

public void startPageSequence(Locale locale) {
public void startPageSequence(Locale locale, String role) {
SAXEventRecorder eventRecorder = new SAXEventRecorder();
pageSequenceEventRecorders.add(eventRecorder);
delegate = StructureTree2SAXEventAdapter.newInstance(eventRecorder);
delegate.startPageSequence(locale);
delegate.startPageSequence(locale, role);
}

public void endPageSequence() {

+ 24
- 25
src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java View File

@@ -103,9 +103,9 @@ final class FOToPDFRoleMap {
addStructureType("Formula");
addStructureType("Form");

NON_STRUCT = (PDFName) STANDARD_STRUCTURE_TYPES.get("NonStruct");
NON_STRUCT = STANDARD_STRUCTURE_TYPES.get("NonStruct");
assert NON_STRUCT != null;
THEAD = (PDFName) STANDARD_STRUCTURE_TYPES.get("THead");
THEAD = STANDARD_STRUCTURE_TYPES.get("THead");
assert THEAD != null;

// Create the standard mappings
@@ -155,7 +155,7 @@ final class FOToPDFRoleMap {
}

private static void addMapping(String fo, String structureType) {
PDFName type = (PDFName) STANDARD_STRUCTURE_TYPES.get(structureType);
PDFName type = STANDARD_STRUCTURE_TYPES.get(structureType);
assert type != null;
addMapping(fo, new SimpleMapper(type));
}
@@ -165,21 +165,6 @@ final class FOToPDFRoleMap {
}


/**
* 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 null if no match could be found
*/
public static PDFName mapFormattingObject(String fo, PDFObject parent) {
Mapper mapper = (Mapper) DEFAULT_MAPPINGS.get(fo);
if (mapper != null) {
return mapper.getStructureType(parent);
} else {
return NON_STRUCT;
}
}

/**
* Maps a Formatting Object to a PDFName representing the associated structure type.
* @param fo the formatting object's local name
@@ -192,11 +177,11 @@ final class FOToPDFRoleMap {
PDFObject parent, EventBroadcaster eventBroadcaster) {
PDFName type = null;
if (role == null) {
type = mapFormattingObject(fo, parent);
type = getDefaultMappingFor(fo, parent);
} else {
type = (PDFName) STANDARD_STRUCTURE_TYPES.get(role);
type = STANDARD_STRUCTURE_TYPES.get(role);
if (type == null) {
type = mapFormattingObject(fo, parent);
type = getDefaultMappingFor(fo, parent);
PDFEventProducer.Provider.get(eventBroadcaster).nonStandardStructureType(fo,
fo, role, type.toString().substring(1));
}
@@ -205,6 +190,21 @@ final class FOToPDFRoleMap {
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 PDFName getDefaultMappingFor(String fo, PDFObject parent) {
Mapper mapper = DEFAULT_MAPPINGS.get(fo);
if (mapper != null) {
return mapper.getStructureType(parent);
} else {
return NON_STRUCT;
}
}

private interface Mapper {
PDFName getStructureType(PDFObject parent);
}
@@ -226,14 +226,13 @@ final class FOToPDFRoleMap {
private static class TableCellMapper implements Mapper {

public PDFName getStructureType(PDFObject parent) {
PDFStructElem grandParent = (PDFStructElem)
((PDFStructElem) parent).getParentStructElem();
PDFStructElem grandParent = ((PDFStructElem) parent).getParentStructElem();
//TODO What to do with cells from table-footer? Currently they are mapped on TD.
PDFName type;
if (THEAD.equals(grandParent.getStructureType())) {
type = (PDFName) STANDARD_STRUCTURE_TYPES.get("TH");
type = STANDARD_STRUCTURE_TYPES.get("TH");
} else {
type = (PDFName) STANDARD_STRUCTURE_TYPES.get("TD");
type = STANDARD_STRUCTURE_TYPES.get("TD");
}
assert type != null;
return type;

+ 2
- 19
src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java View File

@@ -19,8 +19,6 @@

package org.apache.fop.render.pdf;

import java.util.Locale;

import org.apache.fop.pdf.PDFArray;
import org.apache.fop.pdf.PDFDictionary;
import org.apache.fop.pdf.PDFDocument;
@@ -29,7 +27,6 @@ import org.apache.fop.pdf.PDFName;
import org.apache.fop.pdf.PDFPage;
import org.apache.fop.pdf.PDFParentTree;
import org.apache.fop.pdf.PDFStructElem;
import org.apache.fop.pdf.PDFStructTreeRoot;


/**
@@ -59,8 +56,6 @@ class PDFLogicalStructureHandler {
*/
private PDFArray pageParentTreeArray;

private PDFStructElem rootStructureElement;

/**
* Class providing the necessary information for bracketing content
* associated to a structure element as a marked-content sequence.
@@ -95,22 +90,10 @@ class PDFLogicalStructureHandler {
*/
PDFLogicalStructureHandler(PDFDocument pdfDoc) {
this.pdfDoc = pdfDoc;
PDFStructTreeRoot structTreeRoot = pdfDoc.makeStructTreeRoot(parentTree);
rootStructureElement = pdfDoc.makeStructureElement(
FOToPDFRoleMap.mapFormattingObject("root", structTreeRoot), structTreeRoot);
structTreeRoot.addKid(rootStructureElement);
}


PDFStructElem createPageSequence(Locale language) {
PDFStructElem structElemPart = pdfDoc.makeStructureElement(
FOToPDFRoleMap.mapFormattingObject("page-sequence", rootStructureElement),
rootStructureElement);
rootStructureElement.addKid(structElemPart);
if (language != null) {
structElemPart.setLanguage(language);
}
return structElemPart;
PDFParentTree getParentTree() {
return parentTree;
}

private int getNextParentTreeKey() {

+ 43
- 24
src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java View File

@@ -29,7 +29,11 @@ import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.events.EventBroadcaster;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.pdf.PDFFactory;
import org.apache.fop.pdf.PDFName;
import org.apache.fop.pdf.PDFObject;
import org.apache.fop.pdf.PDFParentTree;
import org.apache.fop.pdf.PDFStructElem;
import org.apache.fop.pdf.PDFStructTreeRoot;

class PDFStructureTreeBuilder implements StructureTreeEventHandler {

@@ -41,21 +45,42 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler {

private LinkedList<PDFStructElem> ancestors = new LinkedList<PDFStructElem>();

private PDFStructElem rootStructureElement;

void setPdfFactory(PDFFactory pdfFactory) {
this.pdfFactory = pdfFactory;
}

void setLogicalStructureHandler(PDFLogicalStructureHandler logicalStructureHandler) {
this.logicalStructureHandler = logicalStructureHandler;
createRootStructureElement();
}

private void createRootStructureElement() {
assert rootStructureElement == null;
PDFParentTree parentTree = logicalStructureHandler.getParentTree();
PDFStructTreeRoot structTreeRoot = pdfFactory.getDocument().makeStructTreeRoot(parentTree);
rootStructureElement = createStructureElement("root", structTreeRoot, null);
structTreeRoot.addKid(rootStructureElement);
}

void setEventBroadcaster(EventBroadcaster eventBroadcaster) {
this.eventBroadcaster = eventBroadcaster;
}

public void startPageSequence(Locale locale) {
public void startPageSequence(Locale language, String role) {
ancestors = new LinkedList<PDFStructElem>();
ancestors.add(logicalStructureHandler.createPageSequence(locale));
PDFStructElem structElem = createStructureElement("page-sequence", rootStructureElement, role);
if (language != null) {
structElem.setLanguage(language);
}
rootStructureElement.addKid(structElem);
ancestors.add(structElem);
}

private PDFStructElem createStructureElement(String name, PDFObject parent, String role) {
PDFName structureType = FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster);
return pdfFactory.getDocument().makeStructureElement(structureType, parent);
}

public void endPageSequence() {
@@ -64,12 +89,10 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler {
public StructureTreeElement startNode(String name, Attributes attributes) {
PDFStructElem parent = ancestors.getFirst();
String role = attributes.getValue("role");
PDFStructElem created;
created = pdfFactory.getDocument().makeStructureElement(
FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster), parent);
parent.addKid(created);
ancestors.addFirst(created);
return created;
PDFStructElem structElem = createStructureElement(name, parent, role);
parent.addKid(structElem);
ancestors.addFirst(structElem);
return structElem;
}

public void endNode(String name) {
@@ -83,34 +106,30 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler {
public StructureTreeElement startImageNode(String name, Attributes attributes) {
PDFStructElem parent = ancestors.getFirst();
String role = attributes.getValue("role");
PDFStructElem created;
created = pdfFactory.getDocument().makeStructureElement(
FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster), parent);
parent.addKid(created);
PDFStructElem structElem = createStructureElement(name, parent, role);
parent.addKid(structElem);
String altTextNode = attributes.getValue(ExtensionElementMapping.URI, "alt-text");
if (altTextNode != null) {
created.put("Alt", altTextNode);
structElem.put("Alt", altTextNode);
} else {
created.put("Alt", "No alternate text specified");
structElem.put("Alt", "No alternate text specified");
}
ancestors.addFirst(created);
return created;
ancestors.addFirst(structElem);
return structElem;
}

public StructureTreeElement startReferencedNode(String name, Attributes attributes) {
PDFStructElem parent = ancestors.getFirst();
String role = attributes.getValue("role");
PDFStructElem created;
PDFStructElem structElem;
if ("#PCDATA".equals(name)) {
created = new PDFStructElem.Placeholder(parent, name);
structElem = new PDFStructElem.Placeholder(parent, name);
} else {
created = pdfFactory.getDocument().makeStructureElement(
FOToPDFRoleMap.mapFormattingObject(name, role, parent,
eventBroadcaster), parent);
structElem = createStructureElement(name, parent, role);
}
parent.addKid(created);
ancestors.addFirst(created);
return created;
parent.addKid(structElem);
ancestors.addFirst(structElem);
return structElem;
}

}

+ 5
- 0
status.xml View File

@@ -63,6 +63,11 @@
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="FOP Trunk" date="TBD">
<action context="Code" dev="VH" type="add">
Added support for the role property on fo:page-sequence, fo:flow and fo:static-content. This
allows to change the mapping of those FOs to PDF structure types when enabling
accessibility.
</action>
<action context="Renderers" dev="VH" type="fix" due-to="Martin Koegler">
Stop outputting the Type entry for structure elements in order to create a smaller PDF
ouptut when accessibility is enabled.

+ 3
- 3
test/java/org/apache/fop/render/intermediate/IFStructureTreeBuilderTestCase.java View File

@@ -61,7 +61,7 @@ public class IFStructureTreeBuilderTestCase {
// Expected
}

sut.startPageSequence(null);
sut.startPageSequence(null, null);
sut.endPageSequence();

sut.replayEventsForPageSequence(handler, 0);
@@ -89,7 +89,7 @@ public class IFStructureTreeBuilderTestCase {
final String nodeName = "block";
final ContentHandler handler = mock(ContentHandler.class);

sut.startPageSequence(null);
sut.startPageSequence(null, null);
sut.startNode(nodeName, createSimpleAttributes(attributes));
sut.endPageSequence();

@@ -105,7 +105,7 @@ public class IFStructureTreeBuilderTestCase {
final String nodeName = "block";
final ContentHandler handler = mock(ContentHandler.class);

sut.startPageSequence(null);
sut.startPageSequence(null, null);
sut.endNode(nodeName);
sut.endPageSequence();


+ 9
- 4
test/pdf/accessibility/role.fo View File

@@ -19,12 +19,17 @@
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" language="en" country="GB">
<fo:layout-master-set>
<fo:simple-page-master master-name="page"
page-height="220pt" page-width="320pt" margin="10pt">
<fo:region-body/>
page-height="240pt" page-width="320pt" margin="10pt" margin-bottom="8pt">
<fo:region-body margin-bottom="20pt"/>
<fo:region-after extent="10pt"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="page">
<fo:flow flow-name="xsl-region-body" hyphenate="true" font-family="sans-serif">
<fo:page-sequence master-reference="page" role="Art">
<fo:static-content flow-name="xsl-region-after" role="NonStruct" font-size="8pt">
<fo:block text-align="center"><fo:page-number/></fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body" role="NonStruct" hyphenate="true"
font-family="sans-serif">
<fo:block role="H1" font-weight="bold" font-size="150%"
space-before.minimum="1.5em"
space-before.optimum="2em"

Loading…
Cancel
Save