private DummyStructureTreeEventHandler() { }
/** {@inheritDoc} */
- public void startPageSequence(Locale locale) {
+ public void startPageSequence(Locale locale, String role) {
}
/** {@inheritDoc} */
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.
}
/** {@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);
}
* 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.
locale = new Locale(pageSeq.getLanguage());
}
}
- structureTreeEventHandler.startPageSequence(locale);
+ String role = pageSeq.getCommonAccessibility().getRole();
+ structureTreeEventHandler.startPageSequence(locale, role);
}
@Override
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">
* <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 */
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;
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} */
return pageNumberGenerator.makeFormattedPageNumber(pageNumber);
}
+ public CommonAccessibility getCommonAccessibility() {
+ return commonAccessibility;
+ }
+
/**
* Public accessor for the ancestor Root.
* @return the ancestor Root
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;
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
flowName = pList.get(PR_FLOW_NAME).getString();
+ commonAccessibility = CommonAccessibility.getInstance(pList);
}
/** {@inheritDoc} */
return flowName;
}
+ public CommonAccessibility getCommonAccessibility() {
+ return commonAccessibility;
+ }
+
/** {@inheritDoc} */
public String getLocalName() {
return "flow";
private ContentHandler navParser;
- private ContentHandler structureTreeHandler;
+ private StructureTreeHandler structureTreeHandler;
private Attributes pageSequenceAttributes;
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 {
} 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 */
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() {
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
}
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));
}
}
- /**
- * 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
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));
}
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);
}
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;
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;
import org.apache.fop.pdf.PDFPage;
import org.apache.fop.pdf.PDFParentTree;
import org.apache.fop.pdf.PDFStructElem;
-import org.apache.fop.pdf.PDFStructTreeRoot;
/**
*/
private PDFArray pageParentTreeArray;
- private PDFStructElem rootStructureElement;
-
/**
* Class providing the necessary information for bracketing content
* associated to a structure element as a marked-content sequence.
*/
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() {
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 {
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() {
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) {
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;
}
}
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.
// Expected
}
- sut.startPageSequence(null);
+ sut.startPageSequence(null, null);
sut.endPageSequence();
sut.replayEventsForPageSequence(handler, 0);
final String nodeName = "block";
final ContentHandler handler = mock(ContentHandler.class);
- sut.startPageSequence(null);
+ sut.startPageSequence(null, null);
sut.startNode(nodeName, createSimpleAttributes(attributes));
sut.endPageSequence();
final String nodeName = "block";
final ContentHandler handler = mock(ContentHandler.class);
- sut.startPageSequence(null);
+ sut.startPageSequence(null, null);
sut.endNode(nodeName);
sut.endPageSequence();
<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"