</li>
</ul>
</section>
+ <section id="customTags">
+ <title>Customized Tagging</title>
+ <p>The <a href="#PDFReference">PDF Reference</a> defines a set of standard Structure Types to
+ tag content. For example, ‘P’ is used for identifying paragraphs, ‘H1’ to ‘H6’ for headers,
+ ‘L’ for lists, ‘Div’ for block-level groups of elements, etc. This standard set is aimed at
+ improving interoperability between applications producing or consuming PDF. </p>
+ <p>FOP provides a default mapping of Formatting Objects to elements from that standard set.
+ For example, <code>fo:page-sequence</code> is mapped to ‘Part’, <code>fo:block</code> is
+ mapped to ‘P’, <code>fo:list-block</code> to ‘L’, etc.</p>
+ <p>You may want to customize that mapping to improve the accuracy of the tagging or deal with
+ particular FO constructs. For example, you may want to make use of the ‘H1’ to ‘H6’ tags to
+ make the hierarchical structure of the document appear in the PDF. This is achieved by using
+ the <code>role</code> XSL-FO property:</p>
+ <source>...
+<fo:block role="H1" font-weight="bold">I. A Level 1 Heading</fo:block>
+<fo:block>This is the first paragraph of the first section...</fo:block>
+...</source>
+ <p>If a non-standard structure type is specified, FOP will issue a warning and fall back to
+ the default tag associated to the Formatting Object.</p>
+ </section>
<section id="testing">
<title>Testing</title>
<p>
SVG graphics (or images in general) are treated as a single figure. Text contained in
SVGs is not accessible. It's only possible to work with <code>fox:alt-text</code>.
</li>
- <li>
- XSL-FO's role property is currently not supported. It could theoretically be used to
- differentiate between headings and normal text. At the moment, the two are simply
- identified as paragraphs.
- </li>
<li>
The side regions (region-before, region-after etc.) are currently not specially
identified. Screen readers may read their content at page changes.
<li><a href="http://www.section508.gov/">US Government - Website on Section 508</a></li>
<li><a href="http://en.wikipedia.org/wiki/Accessibility">Wikipedia on Accessibility in general</a></li>
<li><a href="http://en.wikipedia.org/wiki/Portable_Document_Format#Accessibility">Wikipedia on Accessibility in PDF</a></li>
- <li>
+ <li id="PDFReference">
<a href="http://partners.adobe.com/public/developer/en/pdf/PDFReference.pdf">PDF
Reference 1.4</a> (look up chapters 9.7 "Tagged PDF" and 9.8 "Accessibility Support")
</li>
<xsl:template match="fo:leader"/>
- <!-- Keep foi:ptr and fox:alt-text attributes, discard everything else -->
- <xsl:template match="@foi:ptr|@fox:alt-text">
+ <!-- Keep foi:ptr, fox:alt-text and role attributes, discard everything else -->
+ <xsl:template match="@foi:ptr|@fox:alt-text|@role">
<xsl:copy-of select="."/>
</xsl:template>
package org.apache.fop.render.pdf;
+import java.util.HashMap;
import java.util.Map;
+import org.w3c.dom.Node;
+
+import org.apache.fop.events.EventBroadcaster;
import org.apache.fop.pdf.PDFName;
import org.apache.fop.pdf.PDFObject;
import org.apache.fop.pdf.PDFStructElem;
*/
final class FOToPDFRoleMap {
- private static final Map STANDARD_MAPPINGS = new java.util.HashMap();
-
- private static final PDFName TFOOT = new PDFName("TFoot");
- private static final PDFName THEAD = new PDFName("THead");
- private static final PDFName NON_STRUCT = new PDFName("NonStruct");
-
- static {
- addMapping("block", "P");
+ /**
+ * Standard structure types defined by the PDF Reference, Fourth Edition (PDF 1.5).
+ */
+ private static final Map STANDARD_STRUCTURE_TYPES = new HashMap();
- PDFName st = new PDFName("Div");
- addMapping("block-container", st);
- addMapping("inline-container", st);
- addMapping("table-and-caption", st);
- addMapping("float", st);
+ private static final Map DEFAULT_MAPPINGS = new java.util.HashMap();
- st = new PDFName("Span");
- addMapping("inline", st);
- addMapping("wrapper", st);
- addMapping("character", st);
+ private static final PDFName THEAD;
+ private static final PDFName NON_STRUCT;
+ static {
+ // Create PDFNames for the standard structure types
+ // Table 10.18: Grouping elements
+ addStructureType("Document");
+ addStructureType("Part");
+ addStructureType("Art");
+ addStructureType("Sect");
+ addStructureType("Div");
+ addStructureType("BlockQuote");
+ addStructureType("Caption");
+ addStructureType("TOC");
+ addStructureType("TOCI");
+ addStructureType("Index");
+ addStructureType("NonStruct");
+ addStructureType("Private");
+ // Table 10.20: Paragraphlike elements
+ addStructureType("H");
+ addStructureType("H1");
+ addStructureType("H2");
+ addStructureType("H3");
+ addStructureType("H4");
+ addStructureType("H5");
+ addStructureType("H6");
+ addStructureType("P");
+ // Table 10.21: List elements
+ addStructureType("L");
+ addStructureType("LI");
+ addStructureType("Lbl");
+ addStructureType("LBody");
+ // Table 10.22: Table elements
+ addStructureType("Table");
+ addStructureType("TR");
+ addStructureType("TH");
+ addStructureType("TD");
+ addStructureType("THead");
+ addStructureType("TBody");
+ addStructureType("TFoot");
+ // Table 10.23: Inline-level structure elements
+ addStructureType("Span");
+ addStructureType("Quote");
+ addStructureType("Note");
+ addStructureType("Reference");
+ addStructureType("BibEntry");
+ addStructureType("Code");
+ addStructureType("Link");
+ addStructureType("Annot");
+ // Table 10.24: Ruby and Warichu elements
+ addStructureType("Ruby");
+ addStructureType("RB");
+ addStructureType("RT");
+ addStructureType("RP");
+ addStructureType("Warichu");
+ addStructureType("WT");
+ addStructureType("WP");
+ // Table 10.25: Illustration elements
+ addStructureType("Figure");
+ addStructureType("Formula");
+ addStructureType("Form");
+
+ NON_STRUCT = (PDFName) STANDARD_STRUCTURE_TYPES.get("NonStruct");
+ assert NON_STRUCT != null;
+ THEAD = (PDFName) STANDARD_STRUCTURE_TYPES.get("THead");
+ assert THEAD != null;
+
+ // Create the standard mappings
+ // Declarations and Pagination and Layout Formatting Objects
addMapping("root", "Document");
addMapping("page-sequence", "Part");
addMapping("flow", "Sect");
addMapping("static-content", "Sect");
-
- st = new PDFName("Quote");
- addMapping("page-number", st);
- addMapping("page-number-citation", st);
- addMapping("page-number-citation-last", st);
-
- st = new PDFName("Figure");
- addMapping("external-graphic", st);
- addMapping("instream-foreign-object", st);
-
- addMapping("table-caption", "Caption");
+ // Block-level Formatting Objects
+ addMapping("block", "P");
+ addMapping("block-container", "Div");
+ // Inline-level Formatting Objects
+ addMapping("character", "Span");
+ addMapping("external-graphic", "Figure");
+ addMapping("instream-foreign-object", "Figure");
+ addMapping("inline", "Span");
+ addMapping("inline-container", "Div");
+ addMapping("page-number", "Quote");
+ addMapping("page-number-citation", "Quote");
+ addMapping("page-number-citation-last", "Quote");
+ // Formatting Objects for Tables
+ addMapping("table-and-caption", "Div");
addMapping("table", "Table");
+ addMapping("table-caption", "Caption");
+ addMapping("table-header", "THead");
+ addMapping("table-footer", "TFoot");
addMapping("table-body", "TBody");
- addMapping("table-header", THEAD);
- addMapping("table-footer", TFOOT);
addMapping("table-row", "TR");
addMapping("table-cell", new TableCellMapper());
-
+ // Formatting Objects for Lists
addMapping("list-block", "L");
addMapping("list-item", "LI");
- addMapping("list-item-label", "Lbl");
addMapping("list-item-body", "LBody");
-
+ addMapping("list-item-label", "Lbl");
+ // Dynamic Effects: Link and Multi Formatting Objects
addMapping("basic-link", "Link");
+ // Out-of-Line Formatting Objects
+ addMapping("float", "Div");
addMapping("footnote", "Note");
addMapping("footnote-body", "Sect");
+ addMapping("wrapper", "Span");
addMapping("marker", "Private");
}
- private static void addMapping(String fo, String pdfName) {
- addMapping(fo, new PDFName(pdfName));
+ private static void addStructureType(String structureType) {
+ STANDARD_STRUCTURE_TYPES.put(structureType, new PDFName(structureType));
}
- private static void addMapping(String fo, PDFName pdfName) {
- addMapping(fo, new SimpleMapper(pdfName));
+ private static void addMapping(String fo, String structureType) {
+ PDFName type = (PDFName) STANDARD_STRUCTURE_TYPES.get(structureType);
+ assert type != null;
+ addMapping(fo, new SimpleMapper(type));
}
private static void addMapping(String fo, Mapper mapper) {
- STANDARD_MAPPINGS.put(fo, 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
* @return the structure type or null if no match could be found
*/
public static PDFName mapFormattingObject(String fo, PDFObject parent) {
- Mapper mapper = (Mapper)STANDARD_MAPPINGS.get(fo);
+ Mapper mapper = (Mapper)DEFAULT_MAPPINGS.get(fo);
if (mapper != null) {
return mapper.getStructureType(parent);
} else {
}
}
- private interface Mapper {
+ public static PDFName mapFormattingObject(Node fo, PDFObject parent,
+ EventBroadcaster eventBroadcaster) {
+ PDFName type = null;
+ Node role = fo.getAttributes().getNamedItemNS(null, "role");
+ if (role == null) {
+ type = mapFormattingObject(fo.getLocalName(), parent);
+ } else {
+ String customType = role.getNodeValue();
+ type = (PDFName) STANDARD_STRUCTURE_TYPES.get(customType);
+ if (type == null) {
+ String foName = fo.getLocalName();
+ type = mapFormattingObject(foName, parent);
+ PDFEventProducer.Provider.get(eventBroadcaster).nonStandardStructureType(fo,
+ foName, customType, type.toString().substring(1));
+ }
+ }
+ assert type != null;
+ return type;
+ }
+
+ private static interface Mapper {
PDFName getStructureType(PDFObject parent);
}
private static class TableCellMapper implements Mapper {
- private static final PDFName TD = new PDFName("TD");
- private static final PDFName TH = new PDFName("TH");
-
public PDFName getStructureType(PDFObject parent) {
PDFStructElem grandParent = (PDFStructElem)
((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())) {
- return TH;
+ type = (PDFName) STANDARD_STRUCTURE_TYPES.get("TH");
} else {
- return TD;
+ type = (PDFName) STANDARD_STRUCTURE_TYPES.get("TD");
}
+ assert type != null;
+ return type;
}
}
this.accessEnabled = getUserAgent().isAccessibilityEnabled();
if (accessEnabled) {
pdfDoc.getRoot().makeTagged();
- logicalStructureHandler = new PDFLogicalStructureHandler(pdfDoc);
+ logicalStructureHandler = new PDFLogicalStructureHandler(pdfDoc,
+ getUserAgent().getEventBroadcaster());
}
} catch (IOException e) {
throw new IFException("I/O error in startDocument()", e);
*/
void nonFullyResolvedLinkTargets(Object source, int count);
+
+ /**
+ * 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);
}
<?xml version="1.0" encoding="UTF-8"?>
<catalogue xml:lang="en">
<message key="org.apache.fop.render.pdf.PDFEventProducer.nonFullyResolvedLinkTargets">{count} link target{count,equals,1,,s} could not be fully resolved and now point{count,equals,1,,s} to the top of the page or {count,equals,1,is,are} dysfunctional.</message>
+ <message key="org.apache.fop.render.pdf.PDFEventProducer.nonStandardStructureType">‘{type}’ is not a standard structure type defined by the PDF Reference. Falling back to ‘{fallback}’.</message>
</catalogue>
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
+import org.apache.fop.events.EventBroadcaster;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.fo.extensions.InternalElementMapping;
import org.apache.fop.pdf.PDFArray;
private final PDFDocument pdfDoc;
+ private final EventBroadcaster eventBroadcaster;
+
/**
* Map of references to the corresponding structure elements.
*/
*
* @param pdfDoc a document
*/
- PDFLogicalStructureHandler(PDFDocument pdfDoc) {
+ PDFLogicalStructureHandler(PDFDocument pdfDoc, EventBroadcaster eventBroadcaster) {
this.pdfDoc = pdfDoc;
+ this.eventBroadcaster = eventBroadcaster;
PDFStructTreeRoot structTreeRoot = pdfDoc.getFactory().makeStructTreeRoot(parentTree);
rootStructureElement = pdfDoc.getFactory().makeStructureElement(
FOToPDFRoleMap.mapFormattingObject("root", structTreeRoot), structTreeRoot);
Node attr = node.getAttributes().getNamedItemNS(InternalElementMapping.URI, "ptr");
assert attr != null;
String ptr = attr.getNodeValue();
- String nodeName = node.getLocalName();
PDFStructElem structElem = pdfDoc.getFactory().makeStructureElement(
- FOToPDFRoleMap.mapFormattingObject(nodeName, parent), parent);
+ FOToPDFRoleMap.mapFormattingObject(node, parent, eventBroadcaster), parent);
// TODO necessary? If a page-sequence is empty (e.g., contains a single
// empty fo:block), should the block still be added to the structure
// tree? This is not being done for descendant empty elements...
if (addKid) {
parent.addKid(structElem);
}
+ String nodeName = node.getLocalName();
if (nodeName.equals("external-graphic") || nodeName.equals("instream-foreign-object")) {
Node altTextNode = node.getAttributes().getNamedItemNS(
ExtensionElementMapping.URI, "alt-text");
this.pdfDoc = pdfUtil.setupPDFDocument(stream);
if (accessEnabled) {
pdfDoc.getRoot().makeTagged();
- logicalStructureHandler = new PDFLogicalStructureHandler(pdfDoc);
+ logicalStructureHandler = new PDFLogicalStructureHandler(pdfDoc,
+ userAgent.getEventBroadcaster());
}
}
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="add">
+ Added possibility to customize PDF tagging via the ‘role’ property.
+ </action>
<action context="Renderers" dev="CB" type="fix" fixes-bug="48237" due-to="Peter Hancock">
Bugfix: AFP Renderer: Respect image color settings for svg
</action>
<action context="Renderers" dev="CB" type="fix" fixes-bug="48376" due-to="Venkat Reddy">
Bugfix: AFP Renderer: Page Overlays not generated when using Intermediate Format
</action>
- <action context="Renderers" dev="CB" type="fix" fixes-bug="48456">
+ <action context="Renderers" dev="CB" type="fix" fixes-bug="48456">
Bugfix: AFP Renderer: Underline is incorrectly placed when reference-orientation != 0
</action>
<action context="Renderers" dev="CB" type="fix" fixes-bug="48453">
-his directory contains sample FO files for testing the accessibility features of
-FOP.
+This directory contains sample FO files for testing the accessibility features
+of FOP.
To every FO file in this directory correspond two PDF files in the pdf/
sub-directory: one generated by the painter, one by the renderer. For example,
<fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify">
<fo:block>(There’s another page sequence <fo:wrapper color="blue"><fo:basic-link
internal-destination="second">below</fo:basic-link></fo:wrapper>.)</fo:block>
- <fo:block>Apache FOP (Formatting Objects Processor) is a print formatter driven by XSL
- formatting objects (XSL-FO) and an output independent formatter<fo:footnote><fo:inline
- baseline-shift="super" font-size="70%">1</fo:inline><fo:footnote-body><fo:block>See the
- <fo:wrapper color="blue"><fo:basic-link
+ <fo:block font-family="sans-serif" font-weight="bold" space-before="1em" space-after="0.2em"
+ role="H1"><fo:block>About Apache FOP</fo:block></fo:block>
+ <fo:block>It is a print formatter driven by XSL formatting objects (XSL-FO) and an output
+ independent formatter<fo:footnote><fo:inline baseline-shift="super"
+ font-size="70%">1</fo:inline><fo:footnote-body><fo:block>See the <fo:wrapper
+ color="blue"><fo:basic-link
external-destination="http://xmlgraphics.apache.org/fop/">FOP
website</fo:basic-link></fo:wrapper> for more
information</fo:block></fo:footnote-body></fo:footnote>. FOP has a nice logo:
--- /dev/null
+<?xml version="1.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$ -->
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="page"
+ page-height="220pt" page-width="320pt" margin="10pt">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="page" language="en" country="GB">
+ <fo:flow flow-name="xsl-region-body" 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"
+ space-before.maximum="3em"
+ space-before.precedence="1000"
+ space-after.minimum="0.2em"
+ space-after.optimum="0.2em"
+ space-after.maximum="0.5em"
+ space-after.precedence="1000">Title 1: To Start With</fo:block>
+ <fo:block role="H2" font-weight="bold" font-size="120%"
+ space-before.minimum="1.2em"
+ space-before.optimum="1.5em"
+ space-before.maximum="2em"
+ space-before.precedence="900"
+ space-after.minimum="0.2em"
+ space-after.optimum="0.2em"
+ space-after.maximum="0.5em"
+ space-after.precedence="900">Title 2: A Sub-Title</fo:block>
+ <fo:block text-align="justify">Apache FOP (Formatting Objects Processor) is a print formatter
+ driven by XSL formatting objects (XSL-FO) and an output independent formatter. 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 role="H2" font-weight="bold" font-size="120%"
+ space-before.minimum="1.2em"
+ space-before.optimum="1.5em"
+ space-before.maximum="2em"
+ space-before.precedence="900"
+ space-after.minimum="0.2em"
+ space-after.optimum="0.2em"
+ space-after.maximum="0.5em"
+ space-after.precedence="900">Title 2: Another Sub-Title</fo:block>
+ <fo:block text-align="justify">Apache FOP (Formatting Objects Processor) is a print formatter
+ driven by XSL formatting objects (XSL-FO) and an output independent formatter. 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 role="H1" font-weight="bold" font-size="150%"
+ space-before.minimum="1.5em"
+ space-before.optimum="2em"
+ space-before.maximum="3em"
+ space-before.precedence="1000"
+ space-after.minimum="0.2em"
+ space-after.optimum="0.2em"
+ space-after.maximum="0.5em"
+ space-after.precedence="1000">Title 1: Second Title</fo:block>
+ <fo:block role="H2" font-weight="bold" font-size="120%"
+ space-before.minimum="1.2em"
+ space-before.optimum="1.5em"
+ space-before.maximum="2em"
+ space-before.precedence="900"
+ space-after.minimum="0.2em"
+ space-after.optimum="0.2em"
+ space-after.maximum="0.5em"
+ space-after.precedence="900">Title 2: A Sample Table</fo:block>
+ <fo:block>See data below:</fo:block>
+ <fo:table table-layout="fixed" width="70%" start-indent="15%" border="0.5pt solid black">
+ <fo:table-header start-indent="0" font-weight="bold">
+ <fo:table-row>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Header 1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Header 2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-header>
+ <fo:table-footer start-indent="0" font-weight="bold">
+ <fo:table-row>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Footer 1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Footer 2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-footer>
+ <fo:table-body start-indent="0">
+ <fo:table-row>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Cell 1.1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Cell 1.2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Cell 2.1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Cell 2.2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:block text-align="justify" space-before="0.5em">That’s all folks.</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>
--- /dev/null
+<?xml version="1.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$ -->
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="page"
+ page-height="220pt" page-width="320pt" margin="10pt">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="page" language="en" country="GB">
+ <fo:flow flow-name="xsl-region-body" 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"
+ space-before.maximum="3em"
+ space-before.precedence="1000"
+ space-after.minimum="0.2em"
+ space-after.optimum="0.2em"
+ space-after.maximum="0.5em"
+ space-after.precedence="1000">Title 1: To Start With</fo:block>
+ <fo:block role="Header2" font-weight="bold" font-size="120%"
+ space-before.minimum="1.2em"
+ space-before.optimum="1.5em"
+ space-before.maximum="2em"
+ space-before.precedence="900"
+ space-after.minimum="0.2em"
+ space-after.optimum="0.2em"
+ space-after.maximum="0.5em"
+ space-after.precedence="900">A Sub-Title With a Non-Standard Role</fo:block>
+ <fo:block text-align="justify">Apache FOP (Formatting Objects Processor) is a print formatter
+ driven by XSL formatting objects (XSL-FO) and an output independent formatter. 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 role="H2" font-weight="bold" font-size="120%"
+ space-before.minimum="1.2em"
+ space-before.optimum="1.5em"
+ space-before.maximum="2em"
+ space-before.precedence="900"
+ space-after.minimum="0.2em"
+ space-after.optimum="0.2em"
+ space-after.maximum="0.5em"
+ space-after.precedence="900">Title 2: Another Sub-Title</fo:block>
+ <fo:block text-align="justify">Apache FOP (Formatting Objects Processor) is a print formatter
+ driven by XSL formatting objects (XSL-FO) and an output independent formatter. 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 role="H1" font-weight="bold" font-size="150%"
+ space-before.minimum="1.5em"
+ space-before.optimum="2em"
+ space-before.maximum="3em"
+ space-before.precedence="1000"
+ space-after.minimum="0.2em"
+ space-after.optimum="0.2em"
+ space-after.maximum="0.5em"
+ space-after.precedence="1000">Title 1: Second Title</fo:block>
+ <fo:block role="H2" font-weight="bold" font-size="120%"
+ space-before.minimum="1.2em"
+ space-before.optimum="1.5em"
+ space-before.maximum="2em"
+ space-before.precedence="900"
+ space-after.minimum="0.2em"
+ space-after.optimum="0.2em"
+ space-after.maximum="0.5em"
+ space-after.precedence="900">Title 2: A Sample Table</fo:block>
+ <fo:block>See data below:</fo:block>
+ <fo:table table-layout="fixed" width="70%" start-indent="15%" border="0.5pt solid black">
+ <fo:table-header start-indent="0" font-weight="bold" role="TableHeader">
+ <fo:table-row>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Header 1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Header 2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-header>
+ <fo:table-footer start-indent="0" font-weight="bold">
+ <fo:table-row>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Footer 1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Footer 2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-footer>
+ <fo:table-body start-indent="0">
+ <fo:table-row>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Cell 1.1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Cell 1.2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Cell 2.1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell border="0.5pt solid black" padding="2pt">
+ <fo:block>Cell 2.2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:block text-align="justify" space-before="0.5em">That’s all folks.</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+</fo:root>