Merged changes from Trunk up to revision 830265


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_Accessibility@830280 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/build.xml b/build.xml
index 8dcedba..0e0b989 100644
--- a/build.xml
+++ b/build.xml
@@ -365,9 +365,16 @@
         <include name="**/*.java"/>
         <exclude name="org/apache/fop/render/*/**/*.java"/>
         <exclude name="org/apache/fop/afp/**/*.java"/>
+        <exclude name="org/apache/fop/accessibility/**/*.java"/>
       </fileset>
     </eventResourceGenerator>
     <fixcrlf file="${src.java.dir}/org/apache/fop/events/EventFormatter.xml" tab="remove" tablength="2"/>
+    <eventResourceGenerator modelfile="${build.gensrc.dir}/org/apache/fop/accessibility/event-model.xml" translationfile="${src.java.dir}/org/apache/fop/accessibility/AccessibilityEventProducer.xml">
+      <fileset dir="${src.java.dir}">
+        <include name="org/apache/fop/accessibility/**/*.java"/>
+      </fileset>
+    </eventResourceGenerator>
+    <fixcrlf file="${src.java.dir}/org/apache/fop/afp/AFPEventProducer.xml" tab="remove" tablength="2"/>
     <eventResourceGenerator modelfile="${build.gensrc.dir}/org/apache/fop/afp/event-model.xml" translationfile="${src.java.dir}/org/apache/fop/afp/AFPEventProducer.xml">
       <fileset dir="${src.java.dir}">
         <include name="org/apache/fop/afp/**/*.java"/>
diff --git a/src/documentation/content/xdocs/site.xml b/src/documentation/content/xdocs/site.xml
index d87b68a..f65b7fa 100644
--- a/src/documentation/content/xdocs/site.xml
+++ b/src/documentation/content/xdocs/site.xml
@@ -159,6 +159,7 @@
       <extensions label="Extensions" href="extensions.html"/>
       <events label="Events" href="events.html"/>
       <metadata label="Metadata" href="metadata.html"/>
+      <accessibility label="Accessibility" href="accessibility.html"/>
     </features>
     
   </trunk>
diff --git a/src/documentation/content/xdocs/trunk/accessibility.xml b/src/documentation/content/xdocs/trunk/accessibility.xml
new file mode 100644
index 0000000..1eb7826
--- /dev/null
+++ b/src/documentation/content/xdocs/trunk/accessibility.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" standalone="no"?>
+<!--
+  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$ -->
+<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V2.0//EN" "document-v20.dtd">
+<document>
+  <header>
+    <title>Accessibility</title>
+  </header>
+  <body>
+    <section id="overview">
+      <title>Overview</title>
+      <p>
+        This page describes the
+        <a href="http://en.wikipedia.org/wiki/Accessibility">accessibility</a>
+        features of Apache FOP.
+        <a href="http://www.section508.gov/">Section 508</a> defines accessibility in the context
+        of electronic documents for the USA but other countries have similar requirements.
+      </p>
+      <p>
+        Accessibility features are available only for the PDF output format and there are some 
+        implementation limitations. Also, certain actions must be undertaken by the content creator 
+        to ensure that FOP can create a truly accessible document. 
+      </p>
+    </section>
+    <section>
+      <title>Enabling accessibility</title>
+      <p>There are 3 ways to enable accessibility:</p>
+      <ol>
+        <li>
+          <strong>Command line:</strong> The command line option -a turns on accessibility:
+          <code>fop -a -fo mydocument.fo -pdf mydocument.pdf</code>            
+        </li>
+        <li>
+          <strong>Embedding:</strong> <code>userAgent.setAccessibility(true);</code>
+        </li>
+        <li>
+          <strong>Optional setting in fop.xconf file:</strong>
+          <pre>
+          &lt;fop version="1.0"&gt;
+              &lt;accessibility&gt;true&lt;/accessibility&gt;
+              ...
+          &lt;/fop&gt;
+          </pre>
+        </li>
+      </ol>
+      <p>
+        When accessibility is enabled, additional information relating to the logical structure of 
+        the document is added to the PDF. That information allows the PDF viewer (or a 
+        text-to-speech application) to retrieve the natural reading order of the document.
+      </p>
+      <note>The processing of the logical structure is memory-hungry. You may need to adjust the 
+        Java heap size in order to process larger files.</note>
+    </section>
+    <section id="source">
+      <title>Changes to your XSL-FO input files</title>
+      <p>
+        Apache FOP cannot automatically generate accessible PDFs. Some of the work can only be
+        performed by the content provider. Following are some changes that may be necessary to
+        your XSL-FO content in order to generate really accessible documents:
+      </p>
+      <ul>
+        <li>Table cells must have a table row as their parent.</li>
+        <li>
+          Images must have an alternate text: use the <code>fox:alt-text</code> extension attribute 
+          (in the <a href="extensions.html#fox-namespace">fox namespace</a>) on 
+          <code>fo:external-graphic</code> and <code>fo:instream-foreign-object</code> to specify a 
+          short text describing the image.
+        </li>
+        <li>
+          Ensure that the order of <code>fo:block-container</code> elements in a page corresponds to
+          the reading order.
+        </li>
+        <li>
+          Specify the natural language of the document using the language and country properties
+          (or via the <code>xml:lang</code> shorthand property).
+        </li>
+      </ul>
+    </section>
+    <section id="testing">
+      <title>Testing</title>
+      <p>
+        Accessible PDFs can be tested, for example, using Adobe Acrobat Professional. Its 
+        Accessibility Check feature creates a report indicating any deficiencies with a PDF 
+        document. Alternatively, you can just let a screen reader read the document aloud.
+      </p>
+    </section>
+    <section id="limitations">
+      <title>Limitations</title>
+      <p>
+        Accessibility support in Apache FOP is relatively new, so there are certain
+        limitations. Please help us identify and close any gaps.
+      </p>
+      <ul>
+        <li>
+          The natural language can currently only be specified at the page-sequence level. The
+          document language is derived from the language of the first page-sequence. It is
+          currently not possible to override the language inside the content below the
+          page-sequence level.
+        </li>
+        <li>
+          It's currently not possible to specify the expanded form of an abbreviation or acronym.
+        </li>
+        <li>
+          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>
+      </ul>
+    </section>
+    <section id="links">
+      <title>Related Links</title>
+      <p>
+        Many resources providing guidance about creating accessible documents can be found on the 
+        web. Here are a few links, along with additional resources around the topic:
+      </p>
+      <ul>
+        <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>
+          <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>
+        <li><a href="pdfa.html">PDF/A support in Apache FOP</a></li>
+        <li><a href="http://wiki.apache.org/xmlgraphics-fop/PDF_Accessibility">Developer-oriented details on the accessibility features (on the Wiki)</a></li>
+      </ul>
+    </section>
+  </body>
+</document>
diff --git a/src/documentation/content/xdocs/trunk/pdfa.xml b/src/documentation/content/xdocs/trunk/pdfa.xml
index bfa1ae3..db5f564 100644
--- a/src/documentation/content/xdocs/trunk/pdfa.xml
+++ b/src/documentation/content/xdocs/trunk/pdfa.xml
@@ -53,8 +53,9 @@
         lack of a full license to get a detailed error protocol.
       </p>
       <p>
-        <strong>PDF/A-1a</strong> is not implemented, yet. This is mostly because of the requirement
-        for tagged PDF which is not available in FOP, yet.
+        <strong>PDF/A-1a</strong> is based on PDF-A-1b and adds accessibility features
+        (such as Tagged PDF). This format is available within the limitation described on
+        the <a href="accessibility.html">Accessibility page</a>.
       </p>
     </section>
     <section id="command-line">
@@ -64,6 +65,9 @@
         as a parameter. If there is a violation of one of the validation rules for 
         PDF/A, an error message is presented and the processing stops.
       </p>
+      <p>
+        PDF/A-1a is enabled by specifying "-pdfprofile PDF/A-1a".
+      </p>
     </section>
     <section id="embedded">
       <title>Usage (embedded)</title>
@@ -80,6 +84,9 @@
         If one of the validation rules of PDF/A is violated, an PDFConformanceException
         (descendant of RuntimeException) is thrown.
       </p>
+      <p>
+        For PDF/A-1a, just use the string "PDF/A-1a" instead of "PDF/A-1b".
+      </p>
     </section>
     <section id="rules">
       <title>PDF/A in Action</title>
@@ -118,12 +125,17 @@
           embedded in clear text so non-PDF-aware applications can extract the XMP metadata.
         </li>
       </ul>
+      <note>
+        There are additional requirements if you want to enabled PDF/A-1a (Tagged PDF). This is
+        particularly the specification of the natural language and alternative descriptions for
+        images. Please refer to the <a href="accessibility.html">Accessibility page</a> for details.
+      </note> 
     </section>
     <section id="profile-compatibility">
       <title>PDF profile compatibility</title>
       <p>
-        The PDF profiles "PDF/X-3:2003" and "PDF/A-1b" are compatible and can both be 
-        activated at the same time.
+        The PDF profiles "PDF/X-3:2003" and "PDF/A-1b" (or "PDF/A-1a") are compatible and can
+        both be activated at the same time.
       </p>
     </section>
     <section id="interoperability">
diff --git a/src/java/META-INF/services/org.apache.fop.fo.ElementMapping b/src/java/META-INF/services/org.apache.fop.fo.ElementMapping
index 0194b19..09c79f5 100644
--- a/src/java/META-INF/services/org.apache.fop.fo.ElementMapping
+++ b/src/java/META-INF/services/org.apache.fop.fo.ElementMapping
@@ -1,10 +1,11 @@
-org.apache.fop.fo.FOElementMapping
-org.apache.fop.fo.extensions.svg.SVGElementMapping
-org.apache.fop.fo.extensions.svg.BatikExtensionElementMapping
-org.apache.fop.fo.extensions.ExtensionElementMapping
-org.apache.fop.fo.extensions.OldExtensionElementMapping
-org.apache.fop.fo.extensions.xmp.XMPElementMapping
-org.apache.fop.fo.extensions.xmp.RDFElementMapping
-org.apache.fop.render.ps.extensions.PSExtensionElementMapping
-org.apache.fop.render.afp.extensions.AFPElementMapping
+org.apache.fop.fo.FOElementMapping

+org.apache.fop.fo.extensions.svg.SVGElementMapping

+org.apache.fop.fo.extensions.svg.BatikExtensionElementMapping

+org.apache.fop.fo.extensions.ExtensionElementMapping

+org.apache.fop.fo.extensions.InternalElementMapping

+org.apache.fop.fo.extensions.OldExtensionElementMapping

+org.apache.fop.fo.extensions.xmp.XMPElementMapping

+org.apache.fop.fo.extensions.xmp.RDFElementMapping

+org.apache.fop.render.ps.extensions.PSExtensionElementMapping

+org.apache.fop.render.afp.extensions.AFPElementMapping

 org.apache.fop.render.pcl.extensions.PCLElementMapping
\ No newline at end of file
diff --git a/src/java/org/apache/fop/accessibility/Accessibility.java b/src/java/org/apache/fop/accessibility/Accessibility.java
new file mode 100644
index 0000000..d550b43
--- /dev/null
+++ b/src/java/org/apache/fop/accessibility/Accessibility.java
@@ -0,0 +1,87 @@
+/*
+ * 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;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamSource;
+
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.FOUserAgent;
+
+/**
+ * Helper class for FOP's accessibility features.
+ */
+public final class Accessibility {
+
+    /** Constant string for the rendering options key to enable accessibility features. */
+    public static final String ACCESSIBILITY = "accessibility";
+
+    // TODO what if the default factory is not a SAXTransformerFactory?
+    private static SAXTransformerFactory tfactory
+            = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+
+    private static Templates addPtrTemplates;
+
+    private static Templates reduceFOTreeTemplates;
+
+    private Accessibility() { }
+
+    /**
+     * Decorates the given handler so the structure tree used for accessibility
+     * features can be branched off the main content stream.
+     * @param handler the handler to decorate
+     * @param userAgent the user agent
+     * @return the decorated handler
+     * @throws FOPException if an error occurs setting up the decoration
+     */
+    public static DefaultHandler decorateDefaultHandler(DefaultHandler handler,
+            FOUserAgent userAgent) throws FOPException {
+        try {
+            setupTemplates();
+            TransformerHandler addPtr = tfactory.newTransformerHandler(addPtrTemplates);
+            Transformer reduceFOTree = reduceFOTreeTemplates.newTransformer();
+            return new AccessibilityPreprocessor(addPtr, reduceFOTree, userAgent, handler);
+        } catch (TransformerConfigurationException e) {
+            throw new FOPException(e);
+        }
+    }
+
+    private static synchronized void setupTemplates() throws TransformerConfigurationException {
+        if (addPtrTemplates == null) {
+            addPtrTemplates = loadTemplates("addPtr.xsl");
+        }
+        if (reduceFOTreeTemplates == null) {
+            reduceFOTreeTemplates = loadTemplates("reduceFOTree.xsl");
+        }
+    }
+
+    private static Templates loadTemplates(String source) throws TransformerConfigurationException {
+        Source src = new StreamSource(Accessibility.class.getResource(source).toExternalForm());
+        return tfactory.newTemplates(src);
+    }
+
+}
diff --git a/src/java/org/apache/fop/accessibility/AccessibilityEventProducer.java b/src/java/org/apache/fop/accessibility/AccessibilityEventProducer.java
new file mode 100644
index 0000000..9a74b7c
--- /dev/null
+++ b/src/java/org/apache/fop/accessibility/AccessibilityEventProducer.java
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+import org.apache.fop.events.EventBroadcaster;
+import org.apache.fop.events.EventProducer;
+
+/**
+ * Event producer for accessibility-related events.
+ */
+public interface AccessibilityEventProducer extends EventProducer {
+
+    /** Provider class for the event producer. */
+    public final class Provider {
+
+        private Provider() { }
+
+        /**
+         * Returns an event producer.
+         *
+         * @param broadcaster the event broadcaster to use
+         * @return the event producer
+         */
+        public static AccessibilityEventProducer get(EventBroadcaster broadcaster) {
+            return (AccessibilityEventProducer) broadcaster.getEventProducerFor(
+                    AccessibilityEventProducer.class);
+        }
+    }
+
+    /**
+     * The structure tree is missing in the XML file.
+     *
+     * @param source the event source
+     * @event.severity FATAL
+     */
+    void noStructureTreeInXML(Object source);
+}
diff --git a/src/java/org/apache/fop/accessibility/AccessibilityEventProducer.xml b/src/java/org/apache/fop/accessibility/AccessibilityEventProducer.xml
new file mode 100644
index 0000000..70466c2
--- /dev/null
+++ b/src/java/org/apache/fop/accessibility/AccessibilityEventProducer.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="en">
+  <message key="org.apache.fop.accessibility.AccessibilityEventProducer.noStructureTreeInXML">Accessibility is enabled but structure tree is missing in XML file. Please disable accessibility, or re-generate XML file in accessibility mode.</message>
+</catalogue>
diff --git a/src/java/org/apache/fop/accessibility/AccessibilityPreprocessor.java b/src/java/org/apache/fop/accessibility/AccessibilityPreprocessor.java
new file mode 100644
index 0000000..1958b74
--- /dev/null
+++ b/src/java/org/apache/fop/accessibility/AccessibilityPreprocessor.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.accessibility;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.apache.commons.io.output.ByteArrayOutputStream;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.util.TransformerDefaultHandler;
+
+/**
+ * This class prepares an XSL-FO document for accessibility. It adds a unique
+ * identifier to every applicable FO, then creates the structure tree, before
+ * handing the document over to the regular handler.
+ */
+class AccessibilityPreprocessor extends TransformerDefaultHandler {
+
+    private final ByteArrayOutputStream enrichedFOBuffer = new ByteArrayOutputStream();
+
+    private final Transformer reduceFOTree;
+
+    private final FOUserAgent userAgent;
+
+    private final DefaultHandler fopHandler;
+
+    public AccessibilityPreprocessor(TransformerHandler addPtr, Transformer reduceFOTree,
+            FOUserAgent userAgent, DefaultHandler fopHandler) {
+        super(addPtr);
+        this.reduceFOTree = reduceFOTree;
+        this.userAgent = userAgent;
+        this.fopHandler = fopHandler;
+        getTransformerHandler().setResult(new StreamResult(enrichedFOBuffer));
+    }
+
+    /** {@inheritDoc} */
+    public void endDocument() throws SAXException {
+        super.endDocument();
+        // do the second transform to struct
+        try {
+            //TODO this must be optimized, no buffering (ex. SAX-based tee-proxy)
+            byte[] enrichedFO = enrichedFOBuffer.toByteArray();
+            Source src = new StreamSource(new ByteArrayInputStream(enrichedFO));
+            DOMResult res = new DOMResult();
+            reduceFOTree.transform(src, res);
+            StructureTree structureTree = new StructureTree();
+            NodeList pageSequences = res.getNode().getFirstChild().getChildNodes();
+            for (int i = 0; i < pageSequences.getLength(); i++) {
+                structureTree.addPageSequenceStructure(pageSequences.item(i).getChildNodes());
+            }
+            userAgent.setStructureTree(structureTree);
+
+            SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+            saxParserFactory.setNamespaceAware(true);
+            saxParserFactory.setValidating(false);
+            SAXParser saxParser = saxParserFactory.newSAXParser();
+            InputStream in = new ByteArrayInputStream(enrichedFO);
+            saxParser.parse(in, fopHandler);
+        } catch (Exception e) {
+            throw new SAXException(e);
+        }
+    }
+
+}
diff --git a/src/java/org/apache/fop/accessibility/StructureTree.java b/src/java/org/apache/fop/accessibility/StructureTree.java
new file mode 100644
index 0000000..a0fdaac
--- /dev/null
+++ b/src/java/org/apache/fop/accessibility/StructureTree.java
@@ -0,0 +1,102 @@
+/*
+ * 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;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * A reduced version of the document's FO tree, containing only its logical
+ * structure. Used by accessible output formats.
+ */
+public final class StructureTree {
+
+    private final List pageSequenceStructures = new ArrayList();
+
+    /**
+     * Package-private default constructor.
+     */
+    StructureTree() { }
+
+    private static boolean flowOrStaticContentNodes(NodeList nodes) {
+        for (int i = 0; i < nodes.getLength(); i++) {
+            Node node = nodes.item(i);
+            if (node.getNodeType() != Node.ELEMENT_NODE) {
+                return false;
+            }
+            String name = node.getLocalName();
+            if (!(name.equals("flow") || name.equals("static-content"))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    void addPageSequenceStructure(NodeList structureTree) {
+        assert flowOrStaticContentNodes(structureTree);
+        pageSequenceStructures.add(structureTree);
+    }
+
+    /**
+     * Returns the list of nodes that are the children of the given page sequence.
+     *
+     * @param index index of the page sequence, 0-based
+     * @return its children nodes
+     */
+    public NodeList getPageSequence(int index) {
+        return (NodeList) pageSequenceStructures.get(index);
+    }
+
+    /**
+     * Returns an XML-like representation of the structure trees.
+     * <p>
+     * <strong>Note:</strong> use only for debugging purpose, as this method
+     * performs non-trivial operations.
+     * </p>
+     * @return a string representation of this object
+     */
+    public String toString() {
+        try {
+            Transformer t = TransformerFactory.newInstance().newTransformer();
+            Writer str = new StringWriter();
+            for (Iterator iter = pageSequenceStructures.iterator(); iter.hasNext();) {
+                NodeList nodes = (NodeList) iter.next();
+                for (int i = 0, c = nodes.getLength(); i < c; i++) {
+                    t.transform(new DOMSource(nodes.item(i)), new StreamResult(str));
+                }
+            }
+            return str.toString();
+        } catch (Exception e) {
+            return e.toString();
+        }
+    }
+
+}
diff --git a/src/java/org/apache/fop/accessibility/StructureTreeBuilder.java b/src/java/org/apache/fop/accessibility/StructureTreeBuilder.java
new file mode 100644
index 0000000..cd9aa01
--- /dev/null
+++ b/src/java/org/apache/fop/accessibility/StructureTreeBuilder.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.accessibility;
+
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+import org.apache.fop.util.DelegatingContentHandler;
+
+/**
+ * Helper class that re-builds a structure tree from what is stored in an
+ * intermediate XML file (IF XML or Area Tree XML).
+ */
+public final class StructureTreeBuilder {
+
+    private final SAXTransformerFactory factory;
+
+    private final StructureTree structureTree = new StructureTree();
+
+    /**
+     * Creates a new instance.
+     *
+     * @param factory a factory internally used to build the structures of page
+     * sequences
+     */
+    public StructureTreeBuilder(SAXTransformerFactory factory) {
+        this.factory = factory;
+    }
+
+    /**
+     * Returns the structure tree that will result from the parsing.
+     *
+     * @return the structure tree built by this object
+     */
+    public StructureTree getStructureTree() {
+        return structureTree;
+    }
+
+    /**
+     * Returns a ContenHandler for parsing the structure of a new page sequence.
+     * It is assumed that page sequences are being parsed in the document order.
+     *
+     * @return a handler for parsing the &lt;structure-tree&gt; or
+     * &lt;structureTree&gt; element and its descendants
+     * @throws SAXException if there is an error when creating the handler
+     */
+    public ContentHandler getHandlerForNextPageSequence() throws SAXException {
+        TransformerHandler structureTreeBuilder;
+        try {
+            structureTreeBuilder = factory.newTransformerHandler();
+        } catch (TransformerConfigurationException e) {
+            throw new SAXException(e);
+        }
+        final DOMResult domResult = new DOMResult();
+        structureTreeBuilder.setResult(domResult);
+        return new DelegatingContentHandler(structureTreeBuilder) {
+
+            public void characters(char[] ch, int start, int length) throws SAXException {
+                /*
+                 * There's no text node in the structure tree. This is just
+                 * whitespace => ignore
+                 */
+            }
+
+            public void endDocument() throws SAXException {
+                super.endDocument();
+                structureTree.addPageSequenceStructure(domResult.getNode().getFirstChild()
+                        .getChildNodes());
+            }
+        };
+    }
+
+}
diff --git a/src/java/org/apache/fop/accessibility/addPtr.xsl b/src/java/org/apache/fop/accessibility/addPtr.xsl
new file mode 100644
index 0000000..b3984d4
--- /dev/null
+++ b/src/java/org/apache/fop/accessibility/addPtr.xsl
@@ -0,0 +1,88 @@
+<?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"
+  xmlns:foi="http://xmlgraphics.apache.org/fop/internal">
+
+  <xsl:template name="addPtr">
+    <xsl:copy>
+      <xsl:apply-templates select="@*"/>
+      <xsl:attribute name="foi:ptr">
+        <xsl:value-of select="generate-id()"/>
+      </xsl:attribute>
+      <xsl:apply-templates/>
+    </xsl:copy>
+  </xsl:template>
+
+  <!-- Block-level Formatting Objects -->
+  <xsl:template match="fo:block|fo:block-container">
+    <xsl:call-template name="addPtr"/>
+  </xsl:template>
+
+  <!-- Inline-level Formatting Objects -->
+  <xsl:template match="fo:character|fo:inline|fo:inline-container">
+    <xsl:call-template name="addPtr"/>
+  </xsl:template>
+
+  <xsl:template match="fo:external-graphic|fo:instream-foreign-object">
+    <xsl:call-template name="addPtr"/>
+  </xsl:template>
+
+  <xsl:template match="fo:page-number|fo:page-number-citation|fo:page-number-citation-last">
+    <xsl:call-template name="addPtr"/>
+  </xsl:template>
+
+  <!-- Formatting Objects for Tables -->
+  <xsl:template match="fo:table-and-caption|fo:table-caption|fo:table">
+    <xsl:call-template name="addPtr"/>
+  </xsl:template>
+
+  <xsl:template match="fo:table-header|fo:table-footer|fo:table-body|fo:table-row|fo:table-cell">
+    <xsl:call-template name="addPtr"/>
+  </xsl:template>
+
+  <!-- Formatting Objects for Lists -->
+  <xsl:template match="fo:list-block|fo:list-item|fo:list-item-label|fo:list-item-body">
+    <xsl:call-template name="addPtr"/>
+  </xsl:template>
+
+  <!-- Dynamic Effects: Link and Multi Formatting Objects -->
+  <xsl:template match="fo:basic-link">
+    <xsl:call-template name="addPtr"/>
+  </xsl:template>
+
+  <!-- Out-of-Line Formatting Objects -->
+  <xsl:template match="fo:float|fo:footnote|fo:footnote-body">
+    <xsl:call-template name="addPtr"/>
+  </xsl:template>
+
+  <!-- Other Formatting Objects -->
+  <xsl:template match="fo:wrapper|fo:marker">
+    <xsl:call-template name="addPtr"/>
+  </xsl:template>
+
+
+  <xsl:template match="@*|node()">
+    <xsl:copy>
+      <xsl:apply-templates select="@*|node()"/>
+    </xsl:copy>
+  </xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/java/org/apache/fop/accessibility/reduceFOTree.xsl b/src/java/org/apache/fop/accessibility/reduceFOTree.xsl
new file mode 100644
index 0000000..8e9bcfc
--- /dev/null
+++ b/src/java/org/apache/fop/accessibility/reduceFOTree.xsl
@@ -0,0 +1,100 @@
+<?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"
+  xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"
+  xmlns:foi="http://xmlgraphics.apache.org/fop/internal">
+
+  <xsl:output method="xml" indent="no"/>
+
+  <xsl:template name="copy">
+    <xsl:copy>
+      <xsl:apply-templates select="@*|node()"/>
+    </xsl:copy>
+  </xsl:template>
+
+  <!-- Declarations and Pagination and Layout Formatting Objects -->
+  <xsl:template match="fo:root|fo:page-sequence|fo:static-content|fo:flow">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <!-- Block-level Formatting Objects -->
+  <xsl:template match="fo:block|fo:block-container">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <!-- Inline-level Formatting Objects -->
+  <xsl:template match="fo:character|fo:inline|fo:inline-container">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <xsl:template match="fo:external-graphic|fo:instream-foreign-object">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <xsl:template match="fo:page-number|fo:page-number-citation|fo:page-number-citation-last">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <!-- Formatting Objects for Tables -->
+  <xsl:template match="fo:table-and-caption|fo:table-caption|fo:table">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <xsl:template match="fo:table-header|fo:table-footer|fo:table-body|fo:table-row|fo:table-cell">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <!-- Formatting Objects for Lists -->
+  <xsl:template match="fo:list-block|fo:list-item|fo:list-item-label|fo:list-item-body">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <!-- Dynamic Effects: Link and Multi Formatting Objects -->
+  <xsl:template match="fo:basic-link">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <!-- Out-of-Line Formatting Objects -->
+  <xsl:template match="fo:float|fo:footnote|fo:footnote-body">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+  <!-- Other Formatting Objects -->
+  <xsl:template match="fo:wrapper|fo:marker">
+    <xsl:call-template name="copy"/>
+  </xsl:template>
+
+
+  <!-- Discard descendants of fo:leader -->
+  <xsl:template match="fo:leader"/>
+      
+
+  <!-- Keep foi:ptr and fox:alt-text attributes, discard everything else -->
+  <xsl:template match="@foi:ptr|@fox:alt-text">
+    <xsl:copy-of select="."/>
+  </xsl:template>
+
+  <xsl:template match="@*"/>
+
+
+  <!-- Discard text -->
+  <xsl:template match="text()"/>
+
+</xsl:stylesheet>
diff --git a/src/java/org/apache/fop/apps/FOUserAgent.java b/src/java/org/apache/fop/apps/FOUserAgent.java
index 8ee472d..0a65203 100644
--- a/src/java/org/apache/fop/apps/FOUserAgent.java
+++ b/src/java/org/apache/fop/apps/FOUserAgent.java
@@ -37,6 +37,8 @@
 import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext;
 
 import org.apache.fop.Version;
+import org.apache.fop.accessibility.Accessibility;
+import org.apache.fop.accessibility.StructureTree;
 import org.apache.fop.events.DefaultEventBroadcaster;
 import org.apache.fop.events.Event;
 import org.apache.fop.events.EventBroadcaster;
@@ -99,6 +101,8 @@
     private boolean conserveMemoryPolicy = false;
     private EventBroadcaster eventBroadcaster = new FOPEventBroadcaster();
 
+    private StructureTree structureTree;
+
     /** Producer:  Metadata element for the system/software that produces
      * the document. (Some renderers can store this in the document.)
      */
@@ -152,6 +156,7 @@
         this.factory = factory;
         setBaseURL(factory.getBaseURL());
         setTargetResolution(factory.getTargetResolution());
+        setAccessibility(factory.isAccessibilityEnabled());
     }
 
     /** @return the associated FopFactory instance */
@@ -196,6 +201,7 @@
         return rendererOverride;
     }
 
+
     /**
      * Sets an explicit FOEventHandler instance which overrides the one
      * defined by the render type setting.
@@ -642,5 +648,49 @@
         this.conserveMemoryPolicy = conserveMemoryPolicy;
     }
 
+    /**
+     * Activates accessibility (for output formats that support it).
+     *
+     * @param accessibility <code>true</code> to enable accessibility support
+     */
+    public void setAccessibility(boolean accessibility) {
+        if (accessibility) {
+            getRendererOptions().put(Accessibility.ACCESSIBILITY, Boolean.TRUE);
+        }
+    }
+
+    /**
+     * Check if accessibility is enabled.
+     * @return true if accessibility is enabled
+     */
+    public boolean isAccessibilityEnabled() {
+        Boolean enabled = (Boolean)this.getRendererOptions().get(Accessibility.ACCESSIBILITY);
+        if (enabled != null) {
+            return enabled.booleanValue();
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Sets the document's structure tree, for use by accessible output formats.
+     *
+     * @param structureTree a simplified version of the FO tree, retaining only
+     * its logical structure
+     */
+    public void setStructureTree(StructureTree structureTree) {
+        this.structureTree = structureTree;
+    }
+
+    /**
+     * Returns the document's structure tree, for use by accessible output
+     * formats.
+     *
+     * @return a simplified version of the FO tree, retaining only its logical
+     * structure
+     */
+    public StructureTree getStructureTree() {
+        return this.structureTree;
+    }
 }
 
diff --git a/src/java/org/apache/fop/apps/Fop.java b/src/java/org/apache/fop/apps/Fop.java
index 0527ea2..07fd4c0 100644
--- a/src/java/org/apache/fop/apps/Fop.java
+++ b/src/java/org/apache/fop/apps/Fop.java
@@ -24,6 +24,7 @@
 
 import org.xml.sax.helpers.DefaultHandler;
 
+import org.apache.fop.accessibility.Accessibility;
 import org.apache.fop.fo.FOTreeBuilder;
 
 /**
@@ -110,7 +111,11 @@
         if (foTreeBuilder == null) {
             createDefaultHandler();
         }
-        return this.foTreeBuilder;
+        if (this.foUserAgent.isAccessibilityEnabled()) {
+            return Accessibility.decorateDefaultHandler(this.foTreeBuilder, foUserAgent);
+        } else {
+            return this.foTreeBuilder;
+        }
     }
 
     /**
diff --git a/src/java/org/apache/fop/apps/FopFactory.java b/src/java/org/apache/fop/apps/FopFactory.java
index 9ddc164..907895c 100644
--- a/src/java/org/apache/fop/apps/FopFactory.java
+++ b/src/java/org/apache/fop/apps/FopFactory.java
@@ -100,6 +100,11 @@
      */
     private String base = null;
 
+    /**
+     *  Controls if accessibility is turned on or off
+     */
+    private boolean accessibility = false;
+
     /** The base URL for all hyphen URL resolutions. */
     private String hyphenBase = null;
 
@@ -185,6 +190,19 @@
     }
 
     /**
+     * Sets accessibility support.
+     *
+     * @param value <code>true</code> to enable accessibility, <code>false</code> otherwise
+     */
+    void setAccessibility(boolean value) {
+        this.accessibility = value;
+    }
+
+    boolean isAccessibilityEnabled() {
+        return accessibility;
+    }
+
+    /**
      * Returns a new {@link Fop} instance. FOP will be configured with a default user agent
      * instance.
      * <p>
diff --git a/src/java/org/apache/fop/apps/FopFactoryConfigurator.java b/src/java/org/apache/fop/apps/FopFactoryConfigurator.java
index 2beb537..a8b964a 100644
--- a/src/java/org/apache/fop/apps/FopFactoryConfigurator.java
+++ b/src/java/org/apache/fop/apps/FopFactoryConfigurator.java
@@ -92,6 +92,15 @@
             log.debug("Initializing FopFactory Configuration");
         }
 
+        if (cfg.getChild("accessibility", false) != null) {
+            try {
+                this.factory.setAccessibility(
+                        cfg.getChild("accessibility").getValueAsBoolean());
+            } catch (ConfigurationException e) {
+                throw new FOPException(e);
+            }
+        }
+
         // strict configuration
         if (cfg.getChild("strict-configuration", false) != null) {
             try {
diff --git a/src/java/org/apache/fop/area/AreaTreeParser.java b/src/java/org/apache/fop/area/AreaTreeParser.java
index aae9b50..ffffda7 100644
--- a/src/java/org/apache/fop/area/AreaTreeParser.java
+++ b/src/java/org/apache/fop/area/AreaTreeParser.java
@@ -39,8 +39,6 @@
 import javax.xml.transform.sax.SAXTransformerFactory;
 import javax.xml.transform.sax.TransformerHandler;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
 import org.w3c.dom.DOMImplementation;
 import org.w3c.dom.Document;
 import org.xml.sax.Attributes;
@@ -50,12 +48,17 @@
 import org.xml.sax.helpers.AttributesImpl;
 import org.xml.sax.helpers.DefaultHandler;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
 import org.apache.xmlgraphics.image.loader.ImageException;
 import org.apache.xmlgraphics.image.loader.ImageInfo;
 import org.apache.xmlgraphics.image.loader.ImageManager;
 import org.apache.xmlgraphics.image.loader.ImageSessionContext;
 import org.apache.xmlgraphics.util.QName;
 
+import org.apache.fop.accessibility.AccessibilityEventProducer;
+import org.apache.fop.accessibility.StructureTreeBuilder;
 import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.area.Trait.Background;
 import org.apache.fop.area.Trait.InternalLink;
@@ -84,6 +87,8 @@
 import org.apache.fop.util.ContentHandlerFactoryRegistry;
 import org.apache.fop.util.ConversionUtils;
 import org.apache.fop.util.DefaultErrorListener;
+import org.apache.fop.util.DelegatingContentHandler;
+import org.apache.fop.util.XMLConstants;
 import org.apache.fop.util.XMLUtil;
 
 /**
@@ -156,6 +161,26 @@
         private Locator locator;
 
 
+        private StructureTreeBuilder structureTreeBuilder;
+
+        private ContentHandler structureTreeBuilderWrapper;
+
+        private Attributes pageSequenceAttributes;
+
+        private final class StructureTreeBuilderWrapper extends DelegatingContentHandler {
+
+            private StructureTreeBuilderWrapper()
+                    throws SAXException {
+                super(structureTreeBuilder.getHandlerForNextPageSequence());
+            }
+
+            public void endDocument() throws SAXException {
+                super.endDocument();
+                startAreaTreeElement("pageSequence", pageSequenceAttributes);
+                pageSequenceAttributes = null;
+            }
+        }
+
         public Handler(AreaTreeModel treeModel, FOUserAgent userAgent,
                 ElementMappingRegistry elementMappingRegistry) {
             this.treeModel = treeModel;
@@ -192,6 +217,11 @@
             makers.put("bookmarkTree", new BookmarkTreeMaker());
             makers.put("bookmark", new BookmarkMaker());
             makers.put("destination", new DestinationMaker());
+
+            if (userAgent.isAccessibilityEnabled()) {
+                structureTreeBuilder = new StructureTreeBuilder(tFactory);
+                userAgent.setStructureTree(structureTreeBuilder.getStructureTree());
+            }
         }
 
         private Area findAreaType(Class clazz) {
@@ -265,19 +295,35 @@
                 delegate.startDocument();
                 delegate.startElement(uri, localName, qName, attributes);
             } else {
-                lastAttributes = new AttributesImpl(attributes);
                 boolean handled = true;
                 if ("".equals(uri)) {
-                    Maker maker = (Maker)makers.get(localName);
-                    content.clear();
-                    ignoreCharacters = true;
-                    if (maker != null) {
-                        ignoreCharacters = maker.ignoreCharacters();
-                        maker.startElement(attributes);
-                    } else if ("extension-attachments".equals(localName)) {
-                        //TODO implement me
+                    if (localName.equals("pageSequence") && userAgent.isAccessibilityEnabled()) {
+                        structureTreeBuilderWrapper = new StructureTreeBuilderWrapper();
+                        pageSequenceAttributes = new AttributesImpl(attributes);
+                    } else if (localName.equals("structureTree")) {
+                        if (userAgent.isAccessibilityEnabled()) {
+                            delegate = structureTreeBuilderWrapper;
+                        } else {
+                            /* Delegate to a handler that does nothing */
+                            delegate = new DefaultHandler();
+                        }
+                        delegateStack.push(qName);
+                        delegate.startDocument();
+                        delegate.startElement(uri, localName, qName, attributes);
                     } else {
-                        handled = false;
+                        if (pageSequenceAttributes != null) {
+                            /*
+                             * This means that no structure-element tag was
+                             * found in the XML, otherwise a
+                             * StructureTreeBuilderWrapper object would have
+                             * been created, which would have reset the
+                             * pageSequenceAttributes field.
+                             */
+                            AccessibilityEventProducer.Provider
+                                    .get(userAgent.getEventBroadcaster())
+                                    .noStructureTreeInXML(this);
+                        }
+                        handled = startAreaTreeElement(localName, attributes);
                     }
                 } else {
                     ContentHandlerFactoryRegistry registry
@@ -304,6 +350,23 @@
             }
         }
 
+        private boolean startAreaTreeElement(String localName, Attributes attributes)
+                throws SAXException {
+            lastAttributes = new AttributesImpl(attributes);
+            Maker maker = (Maker)makers.get(localName);
+            content.clear();
+            ignoreCharacters = true;
+            if (maker != null) {
+                ignoreCharacters = maker.ignoreCharacters();
+                maker.startElement(attributes);
+            } else if ("extension-attachments".equals(localName)) {
+                //TODO implement me
+            } else {
+                return false;
+            }
+            return true;
+        }
+
         /** {@inheritDoc} */
         public void endElement(String uri, String localName, String qName) throws SAXException {
             if (delegate != null) {
@@ -700,6 +763,7 @@
                 setTraits(attributes, ip, SUBSET_BOX);
                 setTraits(attributes, ip, SUBSET_COLOR);
                 setTraits(attributes, ip, SUBSET_LINK);
+                setPtr(ip, attributes);
                 Area parent = (Area)areaStack.peek();
                 parent.addChildArea(ip);
                 areaStack.push(ip);
@@ -748,6 +812,7 @@
                         "tlsadjust", 0));
                 text.setTextWordSpaceAdjust(XMLUtil.getAttributeAsInt(attributes,
                         "twsadjust", 0));
+                setPtr(text, attributes);
                 Area parent = (Area)areaStack.peek();
                 parent.addChildArea(text);
                 areaStack.push(text);
@@ -840,6 +905,7 @@
                 viewport.setContentPosition(XMLUtil.getAttributeAsRectangle2D(attributes, "pos"));
                 viewport.setClip(XMLUtil.getAttributeAsBoolean(attributes, "clip", false));
                 viewport.setOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0));
+                setPtr(viewport, attributes);
                 Area parent = (Area)areaStack.peek();
                 parent.addChildArea(viewport);
                 areaStack.push(viewport);
@@ -858,6 +924,7 @@
                 transferForeignObjects(attributes, image);
                 setAreaAttributes(attributes, image);
                 setTraits(attributes, image, SUBSET_COMMON);
+                setPtr(image, attributes);
                 getCurrentViewport().setContent(image);
             }
         }
@@ -991,10 +1058,10 @@
                     ExtensionAttachment attachment = (ExtensionAttachment)obj;
                     ato.addExtensionAttachment(attachment);
                 } else {
-                    log.warn("Don't know how to handle externally generated object: " + obj);
-                }
+                log.warn("Don't know how to handle externally generated object: " + obj);
             }
         }
+        }
 
         private void setAreaAttributes(Attributes attributes, Area area) {
             area.setIPD(Integer.parseInt(attributes.getValue("ipd")));
@@ -1133,7 +1200,7 @@
             for (int i = 0, c = atts.getLength(); i < c; i++) {
                 String ns = atts.getURI(i);
                 if (ns.length() > 0) {
-                    if ("http://www.w3.org/2000/xmlns/".equals(ns)) {
+                    if (XMLConstants.XMLNS_NAMESPACE_URI.equals(ns)) {
                         continue;
                     }
                     QName qname = new QName(ns, atts.getQName(i));
@@ -1142,6 +1209,13 @@
             }
         }
 
+        private void setPtr(Area area, Attributes attributes) {
+            String ptr = attributes.getValue("ptr");
+            if (ptr != null) {
+                area.addTrait(Trait.PTR, ptr);
+            }
+        }
+
         /** {@inheritDoc} */
         public void characters(char[] ch, int start, int length) throws SAXException {
             if (delegate != null) {
diff --git a/src/java/org/apache/fop/area/Trait.java b/src/java/org/apache/fop/area/Trait.java
index 45c0432..27ce392 100644
--- a/src/java/org/apache/fop/area/Trait.java
+++ b/src/java/org/apache/fop/area/Trait.java
@@ -194,9 +194,12 @@
     public static final Integer OVERLINE_COLOR = new Integer(35);
     /** Trait for color of linethrough decorations when rendering inline parent. */
     public static final Integer LINETHROUGH_COLOR = new Integer(36);
+    
+    /** The ptr trait. Used for accessibility   */
+    public static final Integer PTR = new Integer(37);
 
     /** Maximum value used by trait keys */
-    public static final int MAX_TRAIT_KEY = 36;
+    public static final int MAX_TRAIT_KEY = 37;
 
     private static final TraitInfo[] TRAIT_INFO = new TraitInfo[MAX_TRAIT_KEY + 1];
 
@@ -225,6 +228,7 @@
     static {
         // Create a hashmap mapping trait code to name for external representation
         //put(ID_LINK, new TraitInfo("id-link", String.class));
+        put(PTR, new TraitInfo("ptr", String.class));
         put(INTERNAL_LINK, new TraitInfo("internal-link", InternalLink.class));
         put(EXTERNAL_LINK, new TraitInfo("external-link", ExternalLink.class));
         put(FONT,         new TraitInfo("font", FontTriplet.class));
diff --git a/src/java/org/apache/fop/cli/CommandLineOptions.java b/src/java/org/apache/fop/cli/CommandLineOptions.java
index 8f396c0..011e840 100644
--- a/src/java/org/apache/fop/cli/CommandLineOptions.java
+++ b/src/java/org/apache/fop/cli/CommandLineOptions.java
@@ -36,6 +36,7 @@
 import org.apache.commons.logging.LogFactory;
 
 import org.apache.fop.Version;
+import org.apache.fop.accessibility.Accessibility;
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.apps.FopFactory;
@@ -342,6 +343,8 @@
                 i = i + parseAreaTreeOption(args, i);
             } else if (args[i].equals("-if")) {
                 i = i + parseIntermediateFormatOption(args, i);
+            } else if (args[i].equals("-a")) {
+                this.renderingOptions.put(Accessibility.ACCESSIBILITY, Boolean.TRUE);
             } else if (args[i].equals("-v")) {
                 /* Currently just print the version */
                 printVersion();
@@ -1155,6 +1158,7 @@
             + "  -nocopy           PDF file will be encrypted without copy content permission\n"
             + "  -noedit           PDF file will be encrypted without edit content permission\n"
             + "  -noannotations    PDF file will be encrypted without edit annotation permission\n"
+            + "  -a                enables accessibility features (Tagged PDF etc., default off)\n"
             + "  -pdfprofile prof  PDF file will be generated with the specified profile\n"
             + "                    (Examples for prof: PDF/A-1b or PDF/X-3:2003)\n\n"
             + "  -conserve         Enable memory-conservation policy (trades memory-consumption for disk I/O)\n"
diff --git a/src/java/org/apache/fop/events/EventFormatter.xml b/src/java/org/apache/fop/events/EventFormatter.xml
index 84e3883..341dd13 100644
--- a/src/java/org/apache/fop/events/EventFormatter.xml
+++ b/src/java/org/apache/fop/events/EventFormatter.xml
@@ -107,4 +107,5 @@
   <message key="org.apache.fop.fonts.FontEventAdapter.fontSubstituted">Font "{requested}" not found. Substituting with "{effective}".</message>
   <message key="org.apache.fop.fonts.FontEventAdapter.fontLoadingErrorAtAutoDetection">Unable to load font file: {fontURL}.[ Reason: {e}]</message>
   <message key="org.apache.fop.fonts.FontEventAdapter.glyphNotAvailable">Glyph "{ch}" (0x{ch,hex}[, {ch,glyph-name}]) not available in font "{fontName}".</message>
+  <message key="org.apache.fop.fo.FOValidationEventProducer.altTextMissing">Alternate text is missing on {foElement}.{{locator}}</message>
 </catalogue>
diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java
index aff13a8..5f23502 100644
--- a/src/java/org/apache/fop/fo/Constants.java
+++ b/src/java/org/apache/fop/fo/Constants.java
@@ -771,8 +771,16 @@
      * multi-column layouts.
      */
     int PR_X_DISABLE_COLUMN_BALANCING = 273;
+    /** Property constant - FOP proprietary: FOP internal use for accessibility */
+    int PR_X_PTR = 274;
+    /**
+     * Property constant - FOP proprietary: alternative text for e-g and i-f-o.
+     * Used for accessibility.
+     */
+    int PR_X_ALT_TEXT = 275;
+
     /** Number of property constants defined */
-    int PROPERTY_COUNT = 273;
+    int PROPERTY_COUNT = 275;
 
     // compound property constants
 
diff --git a/src/java/org/apache/fop/fo/FONode.java b/src/java/org/apache/fop/fo/FONode.java
index 9f18ee6..bbe3ce3 100644
--- a/src/java/org/apache/fop/fo/FONode.java
+++ b/src/java/org/apache/fop/fo/FONode.java
@@ -36,6 +36,7 @@
 import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.fo.extensions.ExtensionAttachment;
 import org.apache.fop.fo.extensions.ExtensionElementMapping;
+import org.apache.fop.fo.extensions.InternalElementMapping;
 import org.apache.fop.fo.extensions.svg.SVGElementMapping;
 import org.apache.fop.fo.pagination.Root;
 import org.apache.fop.util.CharUtilities;
@@ -415,6 +416,8 @@
             return "fo:" + localName;
         } else if (namespaceURI.equals(ExtensionElementMapping.URI)) {
             return "fox:" + localName;
+        } else if (namespaceURI.equals(InternalElementMapping.URI)) {
+            return "foi:" + localName;  // used FOP internally for accessibility
         } else if (namespaceURI.equals(SVGElementMapping.URI)) {
             return "svg:" + localName;
         } else {
diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java
index de6ffca..07fb20f 100644
--- a/src/java/org/apache/fop/fo/FOPropertyMapping.java
+++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java
@@ -2515,6 +2515,18 @@
         m.setDefault("");
         addPropertyMaker("id", m);
 
+        // foi:ptr, used for accessibility
+        m = new StringProperty.Maker(PR_X_PTR);
+        m.setInherited(false);
+        m.setDefault("");
+        addPropertyMaker("foi:ptr", m);
+
+        // fox:alt-text, used for accessibility
+        m = new StringProperty.Maker(PR_X_ALT_TEXT);
+        m.setInherited(false);
+        m.setDefault("");
+        addPropertyMaker("fox:alt-text", m);
+
         // provisional-label-separation
         m  = new LengthProperty.Maker(PR_PROVISIONAL_LABEL_SEPARATION);
         m.setInherited(true);
diff --git a/src/java/org/apache/fop/fo/FOValidationEventProducer.java b/src/java/org/apache/fop/fo/FOValidationEventProducer.java
index 889bf70..ff005b1 100644
--- a/src/java/org/apache/fop/fo/FOValidationEventProducer.java
+++ b/src/java/org/apache/fop/fo/FOValidationEventProducer.java
@@ -36,7 +36,9 @@
     /**
      * Provider class for the event producer.
      */
-    class Provider {
+    final class Provider {
+
+        private Provider() { }
 
         /**
          * Returns an event producer.
@@ -354,4 +356,13 @@
     void unknownFormattingObject(Object source, String elementName,
             QName offendingNode, Locator loc);
 
+    /**
+     * Alternate text is missing for a graphic element.
+     *
+     * @param source the event source
+     * @param foElement name of the element (external-graphic or instream-foreign-object)
+     * @param loc the location of the error or null
+     * @event.severity WARN
+     */
+    void altTextMissing(Object source, String foElement, Locator loc);
 }
diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
index 927bed0..f9a5561 100644
--- a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
+++ b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
@@ -46,6 +46,8 @@
         propertyAttributes.add("orphan-content-limit");
         propertyAttributes.add("internal-destination");
         propertyAttributes.add("disable-column-balancing");
+        //These are FOP's extension properties for accessibility
+        propertyAttributes.add("alt-text");
     }
 
     /**
diff --git a/src/java/org/apache/fop/fo/extensions/ExternalDocument.java b/src/java/org/apache/fop/fo/extensions/ExternalDocument.java
index bea6f6f..233714b 100644
--- a/src/java/org/apache/fop/fo/extensions/ExternalDocument.java
+++ b/src/java/org/apache/fop/fo/extensions/ExternalDocument.java
@@ -31,7 +31,7 @@
 import org.apache.fop.fo.properties.LengthRangeProperty;
 
 /**
- * Class for the fox:external-document extenstion element.
+ * Class for the fox:external-document extension element.
  */
 public class ExternalDocument extends AbstractPageSequence implements GraphicsProperties {
 
diff --git a/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java b/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java
new file mode 100644
index 0000000..7704c8d
--- /dev/null
+++ b/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java
@@ -0,0 +1,73 @@
+/*
+ * 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.extensions;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import org.apache.xmlgraphics.util.QName;
+
+import org.apache.fop.fo.ElementMapping;
+
+/**
+ * Element mapping for FOP's internal extension to XSL-FO.
+ */
+public class InternalElementMapping extends ElementMapping {
+
+    /** The FOP extension namespace URI */
+    public static final String URI = "http://xmlgraphics.apache.org/fop/internal";
+
+    private static final Set PROPERTY_ATTRIBUTES = new java.util.HashSet();
+
+    static {
+        //These are FOP's extension properties for accessibility
+        PROPERTY_ATTRIBUTES.add("ptr");
+    }
+
+    /**
+     * Constructor.
+     */
+    public InternalElementMapping() {
+        namespaceURI = URI;
+    }
+
+    /**
+     * Initialize the data structures.
+     */
+    protected void initialize() {
+        if (foObjs == null) {
+            foObjs = new HashMap();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public String getStandardPrefix() {
+        return "foi";
+    }
+
+    /** {@inheritDoc} */
+    public boolean isAttributeProperty(QName attributeName) {
+        if (!URI.equals(attributeName.getNamespaceURI())) {
+            throw new IllegalArgumentException("The namespace URIs don't match");
+        }
+        return PROPERTY_ATTRIBUTES.contains(attributeName.getLocalName());
+    }
+
+}
diff --git a/src/java/org/apache/fop/fo/flow/AbstractGraphics.java b/src/java/org/apache/fop/fo/flow/AbstractGraphics.java
index d726822..eeb06c1 100644
--- a/src/java/org/apache/fop/fo/flow/AbstractGraphics.java
+++ b/src/java/org/apache/fop/fo/flow/AbstractGraphics.java
@@ -29,6 +29,7 @@
 import org.apache.fop.fo.properties.KeepProperty;
 import org.apache.fop.fo.properties.LengthRangeProperty;
 import org.apache.fop.fo.properties.SpaceProperty;
+import org.apache.fop.fo.properties.StructurePointerPropertySet;
 
 /**
  * Common base class for the <a href="http://www.w3.org/TR/xsl/#fo_instream-foreign-object">
@@ -36,7 +37,8 @@
  * and <a href="http://www.w3.org/TR/xsl/#fo_external-graphic">
  * <code>fo:external-graphic</code></a> flow formatting objects.
  */
-public abstract class AbstractGraphics extends FObj implements GraphicsProperties {
+public abstract class AbstractGraphics extends FObj
+        implements GraphicsProperties, StructurePointerPropertySet {
 
     // The value of properties relevant for fo:instream-foreign-object
     // and external-graphics.
@@ -60,6 +62,7 @@
     private int scaling;
     private int textAlign;
     private Length width;
+    private String ptr;   // used for accessibility
     // Unused but valid items, commented out for performance:
     //     private CommonAccessibility commonAccessibility;
     //     private CommonAural commonAural;
@@ -94,6 +97,7 @@
         dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum();
         height = pList.get(PR_HEIGHT).getLength();
         id = pList.get(PR_ID).getString();
+        ptr = pList.get(PR_X_PTR).getString();   // used for accessibility
         inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange();
         keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep();
         keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep();
@@ -102,6 +106,12 @@
         scaling = pList.get(PR_SCALING).getEnum();
         textAlign = pList.get(PR_TEXT_ALIGN).getEnum();
         width = pList.get(PR_WIDTH).getLength();
+        if (getUserAgent().isAccessibilityEnabled()) {
+            String altText = pList.get(PR_X_ALT_TEXT).getString();
+            if (altText.equals("")) {
+                getFOValidationEventProducer().altTextMissing(this, getLocalName(), getLocator());
+            }
+        }
     }
 
     /**
@@ -207,6 +217,11 @@
         return keepWithPrevious;
     }
 
+    /** {@inheritDoc} */
+    public String getPtr() {
+        return ptr;
+    }
+
     /** @return the graphic's intrinsic width in millipoints */
     public abstract int getIntrinsicWidth();
 
diff --git a/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java b/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java
index 5f420ef..0f4575c 100644
--- a/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java
+++ b/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java
@@ -21,8 +21,8 @@
 
 import java.awt.Color;
 
-import org.xml.sax.Locator;
 import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
 
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.datatypes.Length;
@@ -35,6 +35,7 @@
 import org.apache.fop.fo.properties.CommonFont;
 import org.apache.fop.fo.properties.CommonTextDecoration;
 import org.apache.fop.fo.properties.SpaceProperty;
+import org.apache.fop.fo.properties.StructurePointerPropertySet;
 
 /**
  * Common base class for the <a href="http://www.w3.org/TR/xsl/#fo_page-number-citation">
@@ -42,7 +43,8 @@
  * <a href="http://www.w3.org/TR/xsl/#fo_page-number-citation-last">
  * <code>fo:page-number-citation-last</code></a> objects.
  */
-public abstract class AbstractPageNumberCitation extends FObj {
+public abstract class AbstractPageNumberCitation extends FObj
+            implements StructurePointerPropertySet {
 
     // The value of properties relevant for fo:page-number-citation(-last).
     private CommonBorderPaddingBackground commonBorderPaddingBackground;
@@ -51,6 +53,7 @@
     private int alignmentBaseline;
     private Length baselineShift;
     private int dominantBaseline;
+    private String ptr;  // used for accessibility
     // private ToBeImplementedProperty letterSpacing;
     private SpaceProperty lineHeight;
     private String refId;
@@ -96,6 +99,7 @@
         dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum();
         // letterSpacing = pList.get(PR_LETTER_SPACING);
         lineHeight = pList.get(PR_LINE_HEIGHT).getSpace();
+        ptr = pList.get(PR_X_PTR).getString();   // used for accessibility
         refId = pList.get(PR_REF_ID).getString();
         textDecoration = pList.getTextDecorationProps();
         // textShadow = pList.get(PR_TEXT_SHADOW);
@@ -138,6 +142,11 @@
         return textDecoration;
     }
 
+    /** {@inheritDoc} */
+    public String getPtr() {
+        return ptr;
+    }
+
     /** @return the "alignment-adjust" property */
     public Length getAlignmentAdjust() {
         return alignmentAdjust;
diff --git a/src/java/org/apache/fop/fo/flow/BasicLink.java b/src/java/org/apache/fop/fo/flow/BasicLink.java
index ee31711..de435f9 100644
--- a/src/java/org/apache/fop/fo/flow/BasicLink.java
+++ b/src/java/org/apache/fop/fo/flow/BasicLink.java
@@ -65,6 +65,7 @@
     /** {@inheritDoc} */
     public void bind(PropertyList pList) throws FOPException {
         super.bind(pList);
+
         // destinationPlacementOffset = pList.get(PR_DESTINATION_PLACEMENT_OFFSET);
         externalDestination = pList.get(PR_EXTERNAL_DESTINATION).getString();
         // indicateDestination = pList.get(PR_INDICATE_DESTINATION);
diff --git a/src/java/org/apache/fop/fo/flow/Block.java b/src/java/org/apache/fop/fo/flow/Block.java
index daaebd6..e317646 100644
--- a/src/java/org/apache/fop/fo/flow/Block.java
+++ b/src/java/org/apache/fop/fo/flow/Block.java
@@ -21,6 +21,8 @@
 
 import java.awt.Color;
 
+import org.xml.sax.Locator;
+
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.datatypes.Length;
 import org.apache.fop.datatypes.Numeric;
@@ -38,13 +40,13 @@
 import org.apache.fop.fo.properties.CommonRelativePosition;
 import org.apache.fop.fo.properties.KeepProperty;
 import org.apache.fop.fo.properties.SpaceProperty;
-import org.xml.sax.Locator;
+import org.apache.fop.fo.properties.StructurePointerPropertySet;
 
  /**
   * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_block">
   * <code>fo:block object</code></a>.
   */
-public class Block extends FObjMixed implements BreakPropertySet {
+public class Block extends FObjMixed implements BreakPropertySet, StructurePointerPropertySet {
 
     // used for FO validation
     private boolean blockOrInlineItemFound = false;
@@ -71,6 +73,7 @@
     private int lineHeightShiftAdjustment;
     private int lineStackingStrategy;
     private Numeric orphans;
+    private String ptr;  //used for accessibility
     private int whiteSpaceTreatment;
     private int span;
     private int textAlign;
@@ -122,6 +125,7 @@
         lineHeightShiftAdjustment = pList.get(PR_LINE_HEIGHT_SHIFT_ADJUSTMENT).getEnum();
         lineStackingStrategy = pList.get(PR_LINE_STACKING_STRATEGY).getEnum();
         orphans = pList.get(PR_ORPHANS).getNumeric();
+        ptr = pList.get(PR_X_PTR).getString();  //used for accessibility
         whiteSpaceTreatment = pList.get(PR_WHITE_SPACE_TREATMENT).getEnum();
         span = pList.get(PR_SPAN).getEnum();
         textAlign = pList.get(PR_TEXT_ALIGN).getEnum();
@@ -171,6 +175,11 @@
         return breakAfter;
     }
 
+    /** {@inheritDoc} */
+    public String getPtr() {
+        return ptr;
+    }
+
     /** @return the "break-before" property. */
     public int getBreakBefore() {
         return breakBefore;
diff --git a/src/java/org/apache/fop/fo/flow/Character.java b/src/java/org/apache/fop/fo/flow/Character.java
index f76b322..7328b56 100644
--- a/src/java/org/apache/fop/fo/flow/Character.java
+++ b/src/java/org/apache/fop/fo/flow/Character.java
@@ -38,12 +38,13 @@
 import org.apache.fop.fo.properties.KeepProperty;
 import org.apache.fop.fo.properties.Property;
 import org.apache.fop.fo.properties.SpaceProperty;
+import org.apache.fop.fo.properties.StructurePointerPropertySet;
 
 /**
  * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_character">
  * <code>fo:character</code></a> object.
  */
-public class Character extends FObj {
+public class Character extends FObj implements StructurePointerPropertySet {
     // The value of properties relevant for fo:character.
     private CommonBorderPaddingBackground commonBorderPaddingBackground;
     private CommonFont commonFont;
@@ -62,6 +63,7 @@
     private CommonTextDecoration textDecoration;
     // private ToBeImplementedProperty textShadow;
     private Property wordSpacing;
+    private String ptr;  // used for accessibility
     // Unused but valid items, commented out for performance:
     //     private CommonAural commonAural;
     //     private CommonMarginInline commonMarginInline;
@@ -108,6 +110,7 @@
         lineHeight = pList.get(PR_LINE_HEIGHT).getSpace();
         textDecoration = pList.getTextDecorationProps();
         wordSpacing = pList.get(PR_WORD_SPACING);
+        ptr = pList.get(PR_X_PTR).getString();  // used for accessibility
     }
 
     /** {@inheritDoc} */
@@ -208,6 +211,11 @@
     }
 
     /** {@inheritDoc} */
+    public String getPtr() {
+        return ptr;
+    }
+
+    /** {@inheritDoc} */
     public String getLocalName() {
         return "character";
     }
diff --git a/src/java/org/apache/fop/fo/flow/Inline.java b/src/java/org/apache/fop/fo/flow/Inline.java
index dae7d30..e6e8e9c 100644
--- a/src/java/org/apache/fop/fo/flow/Inline.java
+++ b/src/java/org/apache/fop/fo/flow/Inline.java
@@ -26,17 +26,19 @@
 import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.PropertyList;
 import org.apache.fop.fo.ValidationException;
+import org.apache.fop.fo.properties.StructurePointerPropertySet;
 
 /**
  * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_inline">
  * <code>fo:inline</code></a> formatting object.
  */
-public class Inline extends InlineLevel {
+public class Inline extends InlineLevel implements StructurePointerPropertySet {
     // The value of properties relevant for fo:inline.
     // See also superclass InlineLevel
     private Length alignmentAdjust;
     private int alignmentBaseline;
     private Length baselineShift;
+    private String ptr;  // used for accessibility
     private int dominantBaseline;
     // Unused but valid items, commented out for performance:
     //     private CommonRelativePosition commonRelativePosition;
@@ -66,6 +68,7 @@
         alignmentBaseline = pList.get(PR_ALIGNMENT_BASELINE).getEnum();
         baselineShift = pList.get(PR_BASELINE_SHIFT).getLength();
         dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum();
+        ptr = pList.get(PR_X_PTR).getString(); // used for accessibility
     }
 
     /** {@inheritDoc} */
@@ -145,6 +148,11 @@
     }
 
     /** {@inheritDoc} */
+    public String getPtr() {
+        return ptr;
+    }
+
+    /** {@inheritDoc} */
     public String getLocalName() {
         return "inline";
     }
diff --git a/src/java/org/apache/fop/fo/flow/PageNumber.java b/src/java/org/apache/fop/fo/flow/PageNumber.java
index dc834d7..34d267a 100644
--- a/src/java/org/apache/fop/fo/flow/PageNumber.java
+++ b/src/java/org/apache/fop/fo/flow/PageNumber.java
@@ -34,12 +34,13 @@
 import org.apache.fop.fo.properties.CommonFont;
 import org.apache.fop.fo.properties.CommonTextDecoration;
 import org.apache.fop.fo.properties.SpaceProperty;
+import org.apache.fop.fo.properties.StructurePointerPropertySet;
 
 /**
  * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_page-number">
  * <code>fo:page-number</code></a> object.
  */
-public class PageNumber extends FObj {
+public class PageNumber extends FObj implements StructurePointerPropertySet {
     // The value of properties relevant for fo:page-number.
     private CommonBorderPaddingBackground commonBorderPaddingBackground;
     private CommonFont commonFont;
@@ -47,6 +48,7 @@
     private int alignmentBaseline;
     private Length baselineShift;
     private int dominantBaseline;
+    private String ptr; // used for accessibility
     // private ToBeImplementedProperty letterSpacing;
     private SpaceProperty lineHeight;
     /** Holds the text decoration values. May be null */
@@ -92,6 +94,7 @@
         // letterSpacing = pList.get(PR_LETTER_SPACING);
         lineHeight = pList.get(PR_LINE_HEIGHT).getSpace();
         textDecoration = pList.getTextDecorationProps();
+        ptr = pList.get(PR_X_PTR).getString(); // used for accessibility
         // textShadow = pList.get(PR_TEXT_SHADOW);
 
         // implicit properties
@@ -166,6 +169,11 @@
     }
 
     /** {@inheritDoc} */
+    public String getPtr() {
+        return ptr;
+    }
+
+    /** {@inheritDoc} */
     public String getLocalName() {
         return "page-number";
     }
diff --git a/src/java/org/apache/fop/fo/flow/table/TableFObj.java b/src/java/org/apache/fop/fo/flow/table/TableFObj.java
index ec50858..ab8676c 100644
--- a/src/java/org/apache/fop/fo/flow/table/TableFObj.java
+++ b/src/java/org/apache/fop/fo/flow/table/TableFObj.java
@@ -19,6 +19,9 @@
 
 package org.apache.fop.fo.flow.table;
 
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.datatypes.Numeric;
 import org.apache.fop.datatypes.ValidationPercentBaseContext;
@@ -33,19 +36,19 @@
 import org.apache.fop.fo.properties.NumberProperty;
 import org.apache.fop.fo.properties.Property;
 import org.apache.fop.fo.properties.PropertyMaker;
+import org.apache.fop.fo.properties.StructurePointerPropertySet;
 import org.apache.fop.layoutmgr.table.CollapsingBorderModel;
-import org.xml.sax.Locator;
-import org.xml.sax.Attributes;
 
 /**
  * Common base class for table-related FOs
  */
-public abstract class TableFObj extends FObj {
+public abstract class TableFObj extends FObj implements StructurePointerPropertySet {
 
     private Numeric borderAfterPrecedence;
     private Numeric borderBeforePrecedence;
     private Numeric borderEndPrecedence;
     private Numeric borderStartPrecedence;
+    private String ptr;
 
     ConditionalBorder borderBefore;
     ConditionalBorder borderAfter;
@@ -71,6 +74,7 @@
         borderBeforePrecedence = pList.get(PR_BORDER_BEFORE_PRECEDENCE).getNumeric();
         borderEndPrecedence = pList.get(PR_BORDER_END_PRECEDENCE).getNumeric();
         borderStartPrecedence = pList.get(PR_BORDER_START_PRECEDENCE).getNumeric();
+        ptr = pList.get(PR_X_PTR).getString();
         if (getNameId() != FO_TABLE //Separate check for fo:table in Table.java
                 && getNameId() != FO_TABLE_CELL
                 && getCommonBorderPaddingBackground().hasPadding(
@@ -235,6 +239,11 @@
         }
     }
 
+    /** {@inheritDoc} */
+    public String getPtr() {
+        return ptr;
+    }
+
     /**
      * Prepares the borders of this element if the collapsing-border model is in use.
      * Conflict resolution with parent elements is done where applicable.
diff --git a/src/java/org/apache/fop/fo/properties/StructurePointerPropertySet.java b/src/java/org/apache/fop/fo/properties/StructurePointerPropertySet.java
new file mode 100644
index 0000000..5cce282
--- /dev/null
+++ b/src/java/org/apache/fop/fo/properties/StructurePointerPropertySet.java
@@ -0,0 +1,34 @@
+/*
+ * 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.properties;
+
+/**
+ * Defines property access methods for internal structure pointer extension properties.
+ */
+public interface StructurePointerPropertySet {
+
+    /**
+     * Returns the value of the "foi:ptr" property, the internal structure pointer used
+     * for tagged PDF and other formats that support a structure tree in addition to paged content.
+     * @return the "foi:ptr" property
+     */
+    String getPtr();
+
+}
diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
index 3bdd8ad..d7453a3 100644
--- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
@@ -391,6 +391,7 @@
 
         addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
 
+        TraitSetter.addPtr(curBlockArea, getBlockFO().getPtr());  // used for accessibility
         TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(),
                 effSpaceBefore, effSpaceAfter);
         flush();
diff --git a/src/java/org/apache/fop/layoutmgr/TraitSetter.java b/src/java/org/apache/fop/layoutmgr/TraitSetter.java
index 9cab632..d062ba2 100644
--- a/src/java/org/apache/fop/layoutmgr/TraitSetter.java
+++ b/src/java/org/apache/fop/layoutmgr/TraitSetter.java
@@ -584,6 +584,17 @@
     }
 
     /**
+     * Adds the ptr trait to the area.
+     * @param area the area to set the traits on
+     * @param ptr string
+     */
+    public static void addPtr(Area area, String ptr) {
+        if (ptr != null && ptr.length() > 0) {
+            area.addTrait(Trait.PTR, ptr);
+        }
+    }
+    
+    /**
      * Sets the producer's ID as a trait on the area. This can be used to track back the
      * generating FO node.
      * @param area the area to set the traits on
diff --git a/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java
index 00c628a..fd4d803 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java
@@ -85,6 +85,7 @@
         transferForeignAttributes(viewportArea);
 
         Viewport vp = new Viewport(viewportArea);
+        TraitSetter.addPtr(vp, fobj.getPtr());  // used for accessibility
         TraitSetter.setProducerID(vp, fobj.getId());
         vp.setIPD(imageLayout.getViewportSize().width);
         vp.setBPD(imageLayout.getViewportSize().height);
diff --git a/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java
index b659784..a74fac7 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java
@@ -138,6 +138,7 @@
         text.setBaselineOffset(font.getAscender());
         TraitSetter.addFontTraits(text, font);
         text.addTrait(Trait.COLOR, fobj.getColor());
+        TraitSetter.addPtr(text, fobj.getPtr());   // used for accessibility
         TraitSetter.addTextDecoration(text, fobj.getTextDecoration());
     }
 
diff --git a/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java
index c369df8..eef649c 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java
@@ -19,13 +19,14 @@
 
 package org.apache.fop.layoutmgr.inline;
 
-import org.apache.fop.datatypes.URISpecification;
-import org.apache.fop.fo.flow.BasicLink;
-import org.apache.fop.fo.Constants;
-import org.apache.fop.layoutmgr.PageSequenceLayoutManager;
-import org.apache.fop.area.inline.InlineArea;
-import org.apache.fop.area.Trait;
 import org.apache.fop.area.LinkResolver;
+import org.apache.fop.area.Trait;
+import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.datatypes.URISpecification;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.flow.BasicLink;
+import org.apache.fop.layoutmgr.PageSequenceLayoutManager;
+import org.apache.fop.layoutmgr.TraitSetter;
 
 /**
  * LayoutManager for the fo:basic-link formatting object
@@ -56,6 +57,7 @@
     private void setupBasicLinkArea(InlineArea area) {
         BasicLink fobj = (BasicLink) this.fobj;
         // internal destinations take precedence:
+        TraitSetter.addPtr(area, fobj.getPtr()); // used for accessibility
         if (fobj.hasInternalDestination()) {
             String idref = fobj.getInternalDestination();
             PageSequenceLayoutManager pslm = getPSLM();
diff --git a/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java
index 383ca01..dcd9934 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java
@@ -86,6 +86,7 @@
         }
         TraitSetter.setProducerID(text, node.getId());
         TraitSetter.addTextDecoration(text, node.getTextDecoration());
+        TraitSetter.addPtr(text, node.getPtr()); // used for accessibility
         return text;
     }
 
diff --git a/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java
index dc8a020..5cae072 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java
@@ -85,7 +85,7 @@
         text.setBaselineOffset(font.getAscender());
         TraitSetter.addFontTraits(text, font);
         text.addTrait(Trait.COLOR, fobj.getColor());
-
+        TraitSetter.addPtr(text, fobj.getPtr()); // used for accessibility
         TraitSetter.addTextDecoration(text, fobj.getTextDecoration());
 
         return text;
diff --git a/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java
index 03f2726..f793bb3 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java
@@ -25,10 +25,13 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+
 import org.apache.fop.area.Trait;
 import org.apache.fop.area.inline.TextArea;
 import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.FOText;
+import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.properties.StructurePointerPropertySet;
 import org.apache.fop.fonts.Font;
 import org.apache.fop.fonts.FontSelector;
 import org.apache.fop.layoutmgr.InlineKnuthSequence;
@@ -506,12 +509,26 @@
         }
         TraitSetter.addFontTraits(textArea, font);
         textArea.addTrait(Trait.COLOR, this.foText.getColor());
-
+        TraitSetter.addPtr(textArea, getPtr()); // used for accessibility
         TraitSetter.addTextDecoration(textArea, this.foText.getTextDecoration());
 
         return textArea;
     }
 
+    /**
+     * used for accessibility
+     * @return ptr of fobj
+     */
+    private String getPtr() {
+        FObj fobj = this.parentLM.getFObj();
+        if (fobj instanceof StructurePointerPropertySet) {
+            return (((StructurePointerPropertySet) fobj).getPtr());
+        } else {
+            //No structure pointer applicable
+            return null;
+        }
+    }
+
     private void addToLetterAdjust(final int index, final int width) {
         if (this.letterAdjustArray[index] == null) {
             this.letterAdjustArray[index] = new MinOptMax(width);
diff --git a/src/java/org/apache/fop/pdf/PDFAMode.java b/src/java/org/apache/fop/pdf/PDFAMode.java
index 1b433e6..18c4a24 100644
--- a/src/java/org/apache/fop/pdf/PDFAMode.java
+++ b/src/java/org/apache/fop/pdf/PDFAMode.java
@@ -44,7 +44,18 @@
         return this.name;
     }
 
-    /** @return true if this mode obey the restrictions established by PDF/A-1b. */
+    /**
+     * Indicates whether this mode obeys the restrictions established by PDF/A-1a.
+     * @return true if this mode obeys the restrictions established by PDF/A-1a.
+     */
+    public boolean isPDFA1LevelA() {
+        return (this != DISABLED);
+    }
+
+    /**
+     * Indicates whether this mode obeys the restrictions established by PDF/A-1b.
+     * @return true if this mode obeys the restrictions established by PDF/A-1b.
+     */
     public boolean isPDFA1LevelB() {
         return (this != DISABLED);
         //PDF/A-1a is a superset of PDF/A-1b!
diff --git a/src/java/org/apache/fop/pdf/PDFArray.java b/src/java/org/apache/fop/pdf/PDFArray.java
index 7c5f8ba..a7dfc38 100644
--- a/src/java/org/apache/fop/pdf/PDFArray.java
+++ b/src/java/org/apache/fop/pdf/PDFArray.java
@@ -107,6 +107,15 @@
     }
 
     /**
+     * Indicates whether the given object exists in the array.
+     * @param obj the object to look for
+     * @return true if obj is contained
+     */
+    public boolean contains(Object obj) {
+        return this.values.contains(obj);
+    }
+
+    /**
      * Returns the length of the array
      * @return the length of the array
      */
diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java
index 46effdf..f0a777b 100644
--- a/src/java/org/apache/fop/pdf/PDFDocument.java
+++ b/src/java/org/apache/fop/pdf/PDFDocument.java
@@ -355,6 +355,25 @@
     }
 
     /**
+     * Makes sure a Lang entry has been set on the document catalog, setting it
+     * to a default value if necessary. When accessibility is enabled the
+     * language must be specified for any text element in the document.
+     */
+    public void enforceLanguageOnRoot() {
+        if (root.getLanguage() == null) {
+            String fallbackLanguage;
+            if (getProfile().getPDFAMode().isPDFA1LevelA()) {
+                //According to Annex B of ISO-19005-1:2005(E), section B.2
+                fallbackLanguage = "x-unknown";
+            } else {
+                //No language has been set on the first page-sequence, so fall back to "en".
+                fallbackLanguage = "en";
+            }
+            root.setLanguage(fallbackLanguage);
+        }
+    }
+
+    /**
      * Get the {@link PDFInfo} object for this document.
      *
      * @return the {@link PDFInfo} object
diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java
index a8d7a07..bf3399b 100644
--- a/src/java/org/apache/fop/pdf/PDFFactory.java
+++ b/src/java/org/apache/fop/pdf/PDFFactory.java
@@ -185,6 +185,10 @@
     public PDFPage makePage(PDFResources resources, int pageIndex,
                             Rectangle2D mediaBox, Rectangle2D cropBox,
                             Rectangle2D bleedBox, Rectangle2D trimBox) {
+        /*
+         * create a PDFPage with the next object number, the given
+         * resources, contents and dimensions
+         */
         PDFPage page = new PDFPage(resources, pageIndex, mediaBox, cropBox, bleedBox, trimBox);
 
         getDocument().assignObjectNumber(page);
@@ -883,6 +887,35 @@
     }
 
     /**
+     * Creates and returns a StructTreeRoot object. Used for accessibility.
+     * @param parentTree the value of the ParenTree entry
+     * @return structure Tree Root element
+     */
+    public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) {
+        PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree);
+        getDocument().assignObjectNumber(structTreeRoot);
+        getDocument().addTrailerObject(structTreeRoot);
+        getDocument().getRoot().setStructTreeRoot(structTreeRoot);
+        return structTreeRoot;
+    }
+
+    /**
+     * Creates and returns a StructElem object.
+     *
+     * @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 the newly created element
+     */
+    public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) {
+        PDFStructElem structElem = new PDFStructElem(parent, structureType);
+        getDocument().assignObjectNumber(structElem);
+        getDocument().addTrailerObject(structElem);
+        return structElem;
+    }
+
+    /**
      * Make a the head object of the name dictionary (the /Dests object).
      *
      * @param destinationList a list of PDFDestination instances
diff --git a/src/java/org/apache/fop/pdf/PDFLink.java b/src/java/org/apache/fop/pdf/PDFLink.java
index 6f5ffeb..9349326 100644
--- a/src/java/org/apache/fop/pdf/PDFLink.java
+++ b/src/java/org/apache/fop/pdf/PDFLink.java
@@ -42,6 +42,7 @@
     private float bry;
     private String color;
     private PDFAction action;
+    private Integer structParent;
 
     /**
      * create objects associated with a link annotation (GoToR)
@@ -68,6 +69,16 @@
         this.action = action;
     }
 
+
+    /**
+     * Sets the value of the StructParent entry for this link.
+     *
+     * @param structParent key in the structure parent tree
+     */
+    public void setStructParent(int structParent) {
+        this.structParent = new Integer(structParent);
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -87,6 +98,8 @@
                    + (brx) + " " + (bry) + " ]\n" + "/C [ "
                    + this.color + " ]\n" + "/Border [ 0 0 0 ]\n" + "/A "
                    + this.action.getAction() + "\n" + "/H /I\n"
+                   + (this.structParent != null
+                           ? "/StructParent " + this.structParent.toString() + "\n" : "")
                    + fFlag + "\n>>\nendobj\n";
         return s;
     }
diff --git a/src/java/org/apache/fop/pdf/PDFNumsArray.java b/src/java/org/apache/fop/pdf/PDFNumsArray.java
index e1c38dd..6db7b02 100644
--- a/src/java/org/apache/fop/pdf/PDFNumsArray.java
+++ b/src/java/org/apache/fop/pdf/PDFNumsArray.java
@@ -57,8 +57,26 @@
      * @param key the key of the value to set
      * @param obj the new value
      */
+    public void put(Integer key, Object obj) {
+        this.map.put(key, obj);
+    }
+
+    /**
+     * Sets an entry.
+     * @param key the key of the value to set
+     * @param obj the new value
+     */
     public void put(int key, Object obj) {
-        this.map.put(new Integer(key), obj);
+        put(new Integer(key), obj);
+    }
+
+    /**
+     * Gets an entry.
+     * @param key the key of requested value
+     * @return the requested value
+     */
+    public Object get(Integer key) {
+        return this.map.get(key);
     }
 
     /**
@@ -67,7 +85,7 @@
      * @return the requested value
      */
     public Object get(int key) {
-        return this.map.get(new Integer(key));
+        return get(new Integer(key));
     }
 
     /** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/pdf/PDFPage.java b/src/java/org/apache/fop/pdf/PDFPage.java
index 4fa3b0a..7a53657 100644
--- a/src/java/org/apache/fop/pdf/PDFPage.java
+++ b/src/java/org/apache/fop/pdf/PDFPage.java
@@ -154,4 +154,33 @@
         return this.pageIndex;
     }
 
+    /**
+     * Sets the "StructParents" value.
+     * @param structParents the integer key of this object's entry in the structural parent tree.
+     */
+    public void setStructParents(int structParents) {
+        put("StructParents", structParents);
+        //This is a PDF 1.5 feature. It is set as a work-around for a bug in Adobe Acrobat
+        //which reports this missing even if the PDF file is PDF 1.4.
+        setTabs(new PDFName("S"));
+    }
+
+    /**
+     * Returns the value of the StructParents entry.
+     *
+     * @return the StructParents value, <code>null</code> if the entry has not been set
+     */
+    public Integer getStructParents() {
+        return (Integer) get("StructParents");
+    }
+
+    /**
+     * Specifies the tab order for annotations on a page.
+     * @param value one of the allowed values (see PDF 1.5)
+     * @since PDF 1.5
+     */
+    public void setTabs(PDFName value) {
+        put("Tabs", value);
+    }
+
 }
diff --git a/src/java/org/apache/fop/pdf/PDFParentTree.java b/src/java/org/apache/fop/pdf/PDFParentTree.java
new file mode 100644
index 0000000..7876bbc
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFParentTree.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+/**
+ * Class representing a PDF /ParentTree.
+ */
+public class PDFParentTree extends PDFNumberTreeNode {
+
+    /**
+     * Returns the number tree corresponding to this parent tree.
+     *
+     * @return the number tree
+     */
+    public PDFNumsArray getNums() {
+        PDFNumsArray nums = super.getNums();
+        if (nums == null) {
+            nums = new PDFNumsArray(this);
+            setNums(nums);
+        }
+        return nums;
+    }
+}
+
+
+
+
diff --git a/src/java/org/apache/fop/pdf/PDFProfile.java b/src/java/org/apache/fop/pdf/PDFProfile.java
index 20af4e2..b645cb8 100644
--- a/src/java/org/apache/fop/pdf/PDFProfile.java
+++ b/src/java/org/apache/fop/pdf/PDFProfile.java
@@ -58,9 +58,6 @@
      */
     protected void validateProfileCombination() {
         if (pdfAMode != PDFAMode.DISABLED) {
-            if (pdfAMode == PDFAMode.PDFA_1A) {
-                throw new UnsupportedOperationException("PDF/A-1a is not implemented, yet");
-            }
             if (pdfAMode == PDFAMode.PDFA_1B) {
                 if (pdfXMode != PDFXMode.DISABLED && pdfXMode != PDFXMode.PDFX_3_2003) {
                     throw new PDFConformanceException(
@@ -192,6 +189,32 @@
         }
     }
 
+    /**
+     * Checks a few things required for tagged PDF.
+     */
+    public void verifyTaggedPDF() {
+        if (getPDFAMode().isPDFA1LevelA()) {
+            final String err = "{0} requires the {1} dictionary entry to be set";
+            PDFDictionary markInfo = getDocument().getRoot().getMarkInfo();
+            if (markInfo == null) {
+                throw new PDFConformanceException(format(
+                        "{0} requires the MarkInfo dictionary to be present", getPDFAMode()));
+            }
+            if (!Boolean.TRUE.equals(markInfo.get("Marked"))) {
+                throw new PDFConformanceException(format(err,
+                        new Object[] {getPDFAMode(), "Marked"}));
+            }
+            if (getDocument().getRoot().getStructTreeRoot() == null) {
+                throw new PDFConformanceException(format(err,
+                        new Object[] {getPDFAMode(), "StructTreeRoot"}));
+            }
+            if (getDocument().getRoot().getLanguage() == null) {
+                throw new PDFConformanceException(format(err,
+                        new Object[] {getPDFAMode(), "Lang"}));
+            }
+        }
+    }
+
     /** @return true if the ID entry must be present in the trailer. */
     public boolean isIDEntryRequired() {
         return isPDFAActive() || isPDFXActive();
diff --git a/src/java/org/apache/fop/pdf/PDFRoot.java b/src/java/org/apache/fop/pdf/PDFRoot.java
index 1ea3163..f718410 100644
--- a/src/java/org/apache/fop/pdf/PDFRoot.java
+++ b/src/java/org/apache/fop/pdf/PDFRoot.java
@@ -19,6 +19,9 @@
 
 package org.apache.fop.pdf;
 
+import java.io.IOException;
+import java.io.OutputStream;
+
 /**
  * Class representing a Root (/Catalog) object.
  */
@@ -56,7 +59,7 @@
      * object must be created before the PDF document is
      * generated, but it is not assigned an object ID until
      * it is about to be written (immediately before the xref
-     * table as part of the trsailer). (mark-fop@inomial.com)
+     * table as part of the trailer). (mark-fop@inomial.com)
      *
      * @param objnum the object's number
      * @param pages the PDFPages object
@@ -68,6 +71,12 @@
         setRootPages(pages);
     }
 
+    /** {@inheritDoc} */
+    protected int output(OutputStream stream) throws IOException {
+        getDocument().getProfile().verifyTaggedPDF();
+        return super.output(stream);
+    }
+
     /**
      * Set the page mode for the PDF document.
      *
@@ -252,4 +261,39 @@
         put("Lang", lang);
     }
 
+    /**
+     * Sets the StructTreeRoot object. Used for accessibility.
+     * @param structTreeRoot of this document
+     */
+    public void setStructTreeRoot(PDFStructTreeRoot structTreeRoot) {
+        if (structTreeRoot == null) {
+            throw new NullPointerException("structTreeRoot must not be null");
+        }
+        put("StructTreeRoot", structTreeRoot);
+    }
+
+    /**
+     * Returns the StructTreeRoot object.
+     * @return the structure tree root (or null if accessibility is not enabled)
+     */
+    public PDFStructTreeRoot getStructTreeRoot() {
+        return (PDFStructTreeRoot)get("StructTreeRoot");
+    }
+
+    /**
+     * Marks this document as conforming to the Tagged PDF conventions.
+     */
+    public void makeTagged() {
+        PDFDictionary dict = new PDFDictionary();
+        dict.put("Marked", Boolean.TRUE);
+        put("MarkInfo", dict);  //new PDFMarkInfo()
+    }
+
+    /**
+     * Returns the MarkInfo dictionary.
+     * @return the MarkInfo dictionary (or null if it's not present)
+     */
+    public PDFDictionary getMarkInfo() {
+        return (PDFDictionary)get("MarkInfo");
+    }
 }
diff --git a/src/java/org/apache/fop/pdf/PDFStructElem.java b/src/java/org/apache/fop/pdf/PDFStructElem.java
new file mode 100644
index 0000000..4fb8cbc
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFStructElem.java
@@ -0,0 +1,159 @@
+/*
+ * 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;
+
+import java.util.Locale;
+
+import org.apache.fop.util.XMLUtil;
+
+/**
+ * Class representing a PDF Structure Element.
+ */
+public class PDFStructElem extends PDFDictionary {
+
+    private PDFStructElem parentElement;
+
+    /**
+     * Creates a new structure element.
+     *
+     * @param parent parent of this element
+     * @param structureType the structure type of this element
+     */
+    PDFStructElem(PDFObject parent, PDFName structureType) {
+        if (parent instanceof PDFStructElem) {
+            parentElement = (PDFStructElem) parent;
+        }
+        put("Type", new PDFName("StructElem"));
+        put("S", structureType);
+        setParent(parent);
+    }
+
+    /**
+     * Returns the parent of this structure element.
+     *
+     * @return the parent, <code>null</code> if the parent is not a structure
+     * element (i.e., is the structure tree root)
+     */
+    public PDFStructElem getParentStructElem() {
+        return parentElement;
+    }
+
+    /** {@inheritDoc} */
+    public void setParent(PDFObject parent) {
+        if (parent != null) {
+           put("P", new PDFReference(parent));
+        }
+    }
+
+    /**
+     * Returns the kids of this structure element.
+     *
+     * @return the value of the K entry
+     */
+    private PDFArray getKids() {
+        return (PDFArray) get("K");
+    }
+
+    /**
+     * Add a kid to this structure element. This element will then add itself to
+     * its parent structure element if it has not already, and so will the
+     * parent, and so on.
+     *
+     * @param kid element to be added
+     */
+    public void addKid(PDFObject kid) {
+        PDFArray kids = getKids();
+        if (kids == null) {
+            kids = new PDFArray();
+            put("K", kids);
+        }
+        kids.add(kid);
+        joinHierarchy();
+    }
+
+    private boolean containsKid(PDFObject kid) {
+        PDFArray kids = getKids();
+        return kids != null && kids.contains(kid);
+    }
+
+    private void joinHierarchy() {
+        if (parentElement != null && !parentElement.containsKid(this)) {
+            parentElement.addKid(this);
+        }
+    }
+
+    /**
+     * Sets the given mcid as the kid of this structure element. This element
+     * will then add itself to its parent structure element if it has not
+     * already, and so will the parent, and so on.
+     *
+     * @param mcid mcid of the marked-content sequence corresponding to this
+     * structure element's kid
+     */
+    public void setMCIDKid(int mcid) {
+        put("K", mcid);
+        joinHierarchy();
+    }
+
+    /**
+     * Sets the page reference of this structure element.
+     *
+     * @param page value for the Pg entry
+     */
+    public void setPage(PDFPage page) {
+        put("Pg", page);
+    }
+
+    /**
+     * Returns the structure type of this structure element.
+     *
+     * @return the value of the S entry
+     */
+    public PDFName getStructureType() {
+        return (PDFName)get("S");
+    }
+
+    /**
+     * Sets the language of this structure element.
+     * @param language the language (as defined in the section about
+     *                          "Natural Language Specification")
+     */
+    private void setLanguage(String language) {
+        put("Lang", language);
+    }
+
+    /**
+     * Sets the language of this structure element.
+     *
+     * @param language a value for the Lang entry
+     */
+    public void setLanguage(Locale language) {
+        setLanguage(XMLUtil.toRFC3066(language));
+    }
+
+    /**
+     * Returns the language of this structure element.
+     *
+     * @return the value of the Lang entry (<code>null</code> if no language was specified)
+     */
+    public String getLanguage() {
+        return (String)get("Lang");
+    }
+}
diff --git a/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java b/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java
new file mode 100644
index 0000000..5b3f631
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+/**
+ * Class representing a PDF /StructTreeRoot dictionary.
+ */
+public class PDFStructTreeRoot extends PDFDictionary {
+
+    /**
+     * Creates a new /StructTreeRoot dictionary.
+     *
+     * @param parentTree the value of the ParenTree entry
+     */
+    PDFStructTreeRoot(PDFParentTree parentTree) {
+        put("Type", new PDFName("StructTreeRoot"));
+        put("K", new PDFArray());
+        put("ParentTree", parentTree);
+    }
+
+    /**
+     * Returns the children element of this StructTreeRoot.
+     *
+     * @return the value of the K entry
+     */
+    public PDFArray getKids() {
+        return (PDFArray)get("K");
+    }
+
+    /**
+     * Adds the given object to the array of kids.
+     *
+     * @param kid an object to be added to the K entry
+     */
+    public void addKid(PDFObject kid) {
+        getKids().add(kid);
+    }
+}
\ No newline at end of file
diff --git a/src/java/org/apache/fop/render/AbstractRendererConfigurator.java b/src/java/org/apache/fop/render/AbstractRendererConfigurator.java
index b31e5bf..e1bc104 100644
--- a/src/java/org/apache/fop/render/AbstractRendererConfigurator.java
+++ b/src/java/org/apache/fop/render/AbstractRendererConfigurator.java
@@ -20,6 +20,7 @@
 package org.apache.fop.render;
 
 import org.apache.avalon.framework.configuration.Configuration;
+
 import org.apache.fop.apps.FOUserAgent;
 
 /**
@@ -29,7 +30,7 @@
 public abstract class AbstractRendererConfigurator extends AbstractConfigurator {
 
     private static final String TYPE = "renderer";
-    
+
     /**
      * Default constructor
      * @param userAgent user agent
@@ -55,7 +56,7 @@
     protected Configuration getRendererConfig(String mimeType) {
         return super.getConfig(mimeType);
     }
-    
+
     /**
      * {@inheritDoc}
      */
diff --git a/src/java/org/apache/fop/render/afp/AFPEventProducer.xml b/src/java/org/apache/fop/render/afp/AFPEventProducer.xml
new file mode 100644
index 0000000..23bd9a1
--- /dev/null
+++ b/src/java/org/apache/fop/render/afp/AFPEventProducer.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><catalogue xml:lang="en"/>
diff --git a/src/java/org/apache/fop/render/bitmap/BitmapRendererEventProducer.xml b/src/java/org/apache/fop/render/bitmap/BitmapRendererEventProducer.xml
index a05af3e..47acd74 100644
--- a/src/java/org/apache/fop/render/bitmap/BitmapRendererEventProducer.xml
+++ b/src/java/org/apache/fop/render/bitmap/BitmapRendererEventProducer.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<catalogue xml:lang="en">
+<?xml version="1.0" encoding="UTF-8"?><catalogue xml:lang="en">
   <message key="org.apache.fop.render.bitmap.BitmapRendererEventProducer.stoppingAfterFirstPageNoFilename">No filename information available. Stopping early after the first page.</message>
   <message key="org.apache.fop.render.bitmap.BitmapRendererEventProducer.stoppingAfterFirstPageNoMultiWriter">Image writer does not support multiple images. Only the first page has been produced.</message>
   <message key="org.apache.fop.render.bitmap.BitmapRendererEventProducer.noImageWriterFound">Could not get an ImageWriter to produce "{mime}". The most likely explanation for this is a class loading problem.</message>
diff --git a/src/java/org/apache/fop/render/intermediate/IFConstants.java b/src/java/org/apache/fop/render/intermediate/IFConstants.java
index e7f7e1a..fa234b4 100644
--- a/src/java/org/apache/fop/render/intermediate/IFConstants.java
+++ b/src/java/org/apache/fop/render/intermediate/IFConstants.java
@@ -50,4 +50,6 @@
     String EL_BORDER_RECT = "border-rect";
     String EL_FONT = "font";
     String EL_TEXT = "text";
+    /** Parent element of the logical structure tree. */
+    String EL_STRUCTURE_TREE = "structure-tree";
 }
diff --git a/src/java/org/apache/fop/render/intermediate/IFContext.java b/src/java/org/apache/fop/render/intermediate/IFContext.java
index b05db13..804b353 100644
--- a/src/java/org/apache/fop/render/intermediate/IFContext.java
+++ b/src/java/org/apache/fop/render/intermediate/IFContext.java
@@ -20,6 +20,7 @@
 package org.apache.fop.render.intermediate;
 
 import java.util.Collections;
+import java.util.Locale;
 import java.util.Map;
 
 import org.apache.xmlgraphics.util.QName;
@@ -43,6 +44,10 @@
     /** foreign attributes: Map<QName, Object> */
     private Map foreignAttributes = Collections.EMPTY_MAP;
 
+    private Locale language;
+
+    private String structurePointer;
+
     /**
      * Main constructor.
      * @param ua the user agent
@@ -108,4 +113,46 @@
         setForeignAttributes(null);
     }
 
+    /**
+     * Sets the currently applicable language.
+     * @param lang the language
+     */
+    public void setLanguage(Locale lang) {
+        this.language = lang;
+    }
+
+    /**
+     * Returns the currently applicable language.
+     * @return the language (or null if the language is undefined)
+     */
+    public Locale getLanguage() {
+        return this.language;
+    }
+
+    /**
+     * Sets the structure pointer for the following painted marks. This method is used when
+     * accessibility features are enabled.
+     * @param ptr the structure pointer
+     */
+    public void setStructurePointer(String ptr) {
+        this.structurePointer = ptr;
+    }
+
+    /**
+     * Resets the current structure pointer.
+     * @see #setStructurePointer(String)
+     */
+    public void resetStructurePointer() {
+        setStructurePointer(null);
+    }
+
+    /**
+     * Returns the current structure pointer.
+     * @return the structure pointer (or null if no pointer is active)
+     * @see #setStructurePointer(String)
+     */
+    public String getStructurePointer() {
+        return this.structurePointer;
+    }
+
 }
diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java
index 38344c3..da5d8a6 100644
--- a/src/java/org/apache/fop/render/intermediate/IFParser.java
+++ b/src/java/org/apache/fop/render/intermediate/IFParser.java
@@ -25,6 +25,7 @@
 import java.awt.Rectangle;
 import java.awt.geom.AffineTransform;
 import java.util.Map;
+import java.util.Set;
 
 import javax.xml.transform.Source;
 import javax.xml.transform.Transformer;
@@ -34,7 +35,6 @@
 
 import org.w3c.dom.DOMImplementation;
 import org.w3c.dom.Document;
-
 import org.xml.sax.Attributes;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
@@ -46,6 +46,8 @@
 
 import org.apache.xmlgraphics.util.QName;
 
+import org.apache.fop.accessibility.AccessibilityEventProducer;
+import org.apache.fop.accessibility.StructureTreeBuilder;
 import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.fo.ElementMapping;
 import org.apache.fop.fo.ElementMappingRegistry;
@@ -59,6 +61,7 @@
 import org.apache.fop.util.ContentHandlerFactoryRegistry;
 import org.apache.fop.util.DOMBuilderContentHandlerFactory;
 import org.apache.fop.util.DefaultErrorListener;
+import org.apache.fop.util.DelegatingContentHandler;
 import org.apache.fop.util.XMLUtil;
 
 /**
@@ -73,6 +76,15 @@
     private static SAXTransformerFactory tFactory
         = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
 
+    private static Set handledNamespaces = new java.util.HashSet();
+
+    static {
+        handledNamespaces.add(XMLNS_NAMESPACE_URI);
+        handledNamespaces.add(XML_NAMESPACE);
+        handledNamespaces.add(NAMESPACE);
+        handledNamespaces.add(XLINK_NAMESPACE);
+    }
+
     /**
      * Parses an intermediate file and paints it.
      * @param src the Source instance pointing to the intermediate file
@@ -140,6 +152,26 @@
 
         private ContentHandler navParser;
 
+        private StructureTreeBuilder structureTreeBuilder;
+
+        private ContentHandler structureTreeBuilderWrapper;
+
+        private Attributes pageSequenceAttributes;
+
+        private final class StructureTreeBuilderWrapper extends DelegatingContentHandler {
+
+            private StructureTreeBuilderWrapper()
+                    throws SAXException {
+                super(structureTreeBuilder.getHandlerForNextPageSequence());
+            }
+
+            public void endDocument() throws SAXException {
+                super.endDocument();
+                startIFElement(EL_PAGE_SEQUENCE, pageSequenceAttributes);
+                pageSequenceAttributes = null;
+            }
+        }
+
         public Handler(IFDocumentHandler documentHandler, FOUserAgent userAgent,
                 ElementMappingRegistry elementMappingRegistry) {
             this.documentHandler = documentHandler;
@@ -163,6 +195,11 @@
             elementHandlers.put(EL_LINE, new LineHandler());
             elementHandlers.put(EL_BORDER_RECT, new BorderRectHandler());
             elementHandlers.put(EL_IMAGE, new ImageHandler());
+
+            if (userAgent.isAccessibilityEnabled()) {
+                structureTreeBuilder = new StructureTreeBuilder(tFactory);
+                userAgent.setStructureTree(structureTreeBuilder.getStructureTree());
+            }
         }
 
         private void establishForeignAttributes(Map foreignAttributes) {
@@ -173,31 +210,50 @@
             documentHandler.getContext().resetForeignAttributes();
         }
 
+        private void establishStructurePointer(String ptr) {
+            documentHandler.getContext().setStructurePointer(ptr);
+        }
+
+        private void resetStructurePointer() {
+            documentHandler.getContext().resetStructurePointer();
+        }
+
         /** {@inheritDoc} */
         public void startElement(String uri, String localName, String qName, Attributes attributes)
                     throws SAXException {
             if (delegate != null) {
-                //delegateStack.push(qName);
                 delegateDepth++;
                 delegate.startElement(uri, localName, qName, attributes);
             } else {
                 boolean handled = true;
                 if (NAMESPACE.equals(uri)) {
-                    lastAttributes = new AttributesImpl(attributes);
-                    ElementHandler elementHandler = (ElementHandler)elementHandlers.get(localName);
-                    content.setLength(0);
-                    ignoreCharacters = true;
-                    if (elementHandler != null) {
-                        ignoreCharacters = elementHandler.ignoreCharacters();
-                        try {
-                            elementHandler.startElement(attributes);
-                        } catch (IFException ife) {
-                            handleIFException(ife);
+                    if (localName.equals(EL_PAGE_SEQUENCE) && userAgent.isAccessibilityEnabled()) {
+                        pageSequenceAttributes = new AttributesImpl(attributes);
+                        structureTreeBuilderWrapper = new StructureTreeBuilderWrapper();
+                    } else if (localName.equals(EL_STRUCTURE_TREE)) {
+                        if (userAgent.isAccessibilityEnabled()) {
+                            delegate = structureTreeBuilderWrapper;
+                        } else {
+                            /* Delegate to a handler that does nothing */
+                            delegate = new DefaultHandler();
                         }
-                    } else if ("extension-attachments".equals(localName)) {
-                        //TODO implement me
+                        delegateDepth++;
+                        delegate.startDocument();
+                        delegate.startElement(uri, localName, qName, attributes);
                     } else {
-                        handled = false;
+                        if (pageSequenceAttributes != null) {
+                            /*
+                             * This means that no structure-element tag was
+                             * found in the XML, otherwise a
+                             * StructureTreeBuilderWrapper object would have
+                             * been created, which would have reset the
+                             * pageSequenceAttributes field.
+                             */
+                            AccessibilityEventProducer.Provider
+                                    .get(userAgent.getEventBroadcaster())
+                                    .noStructureTreeInXML(this);
+                        }
+                        handled = startIFElement(localName, attributes);
                     }
                 } else if (DocumentNavigationExtensionConstants.NAMESPACE.equals(uri)) {
                     if (this.navParser == null) {
@@ -241,6 +297,25 @@
             }
         }
 
+        private boolean startIFElement(String localName, Attributes attributes)
+                throws SAXException {
+            lastAttributes = new AttributesImpl(attributes);
+            ElementHandler elementHandler = (ElementHandler)elementHandlers.get(localName);
+            content.setLength(0);
+            ignoreCharacters = true;
+            if (elementHandler != null) {
+                ignoreCharacters = elementHandler.ignoreCharacters();
+                try {
+                    elementHandler.startElement(attributes);
+                } catch (IFException ife) {
+                    handleIFException(ife);
+                }
+                return true;
+            } else {
+                return false;
+            }
+        }
+
         private void handleIFException(IFException ife) throws SAXException {
             if (ife.getCause() instanceof SAXException) {
                 //unwrap
@@ -352,6 +427,11 @@
 
             public void startElement(Attributes attributes) throws IFException {
                 String id = attributes.getValue("id");
+                String xmllang = attributes.getValue(XML_NAMESPACE, "lang");
+                if (xmllang != null) {
+                    documentHandler.getContext().setLanguage(
+                            XMLUtil.convertRFC3066ToLocale(xmllang));
+                }
                 Map foreignAttributes = getForeignAttributes(lastAttributes);
                 establishForeignAttributes(foreignAttributes);
                 documentHandler.startPageSequence(id);
@@ -360,6 +440,7 @@
 
             public void endElement() throws IFException {
                 documentHandler.endPageSequence();
+                documentHandler.getContext().setLanguage(null);
             }
 
         }
@@ -484,7 +565,9 @@
                 s = lastAttributes.getValue("word-spacing");
                 int wordSpacing = (s != null ? Integer.parseInt(s) : 0);
                 int[] dx = XMLUtil.getAttributeAsIntArray(lastAttributes, "dx");
+                setStructurePointer(lastAttributes);
                 painter.drawText(x, y, letterSpacing, wordSpacing, dx, content.toString());
+                resetStructurePointer();
             }
 
             public boolean ignoreCharacters() {
@@ -579,6 +662,7 @@
                 int height = Integer.parseInt(lastAttributes.getValue("height"));
                 Map foreignAttributes = getForeignAttributes(lastAttributes);
                 establishForeignAttributes(foreignAttributes);
+                setStructurePointer(lastAttributes);
                 if (foreignObject != null) {
                     painter.drawImage(foreignObject,
                             new Rectangle(x, y, width, height));
@@ -592,6 +676,7 @@
                     painter.drawImage(uri, new Rectangle(x, y, width, height));
                 }
                 resetForeignAttributes();
+                resetStructurePointer();
                 inForeignObject = false;
             }
 
@@ -632,11 +717,7 @@
             for (int i = 0, c = atts.getLength(); i < c; i++) {
                 String ns = atts.getURI(i);
                 if (ns.length() > 0) {
-                    if ("http://www.w3.org/2000/xmlns/".equals(ns)) {
-                        continue;
-                    } else if (NAMESPACE.equals(ns)) {
-                        continue;
-                    } else if (XLINK_NAMESPACE.equals(ns)) {
+                    if (handledNamespaces.contains(ns)) {
                         continue;
                     }
                     if (foreignAttributes == null) {
@@ -649,6 +730,13 @@
             return foreignAttributes;
         }
 
+        private void setStructurePointer(Attributes attributes) {
+            String ptr = attributes.getValue("ptr");
+            if (ptr != null && ptr.length() > 0) {
+                establishStructurePointer(ptr);
+            }
+        }
+
         /** {@inheritDoc} */
         public void characters(char[] ch, int start, int length) throws SAXException {
             if (delegate != null) {
diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
index 059506d..13ac401 100644
--- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
@@ -30,6 +30,7 @@
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Stack;
 
@@ -493,6 +494,7 @@
         try {
             if (this.inPageSequence) {
                 documentHandler.endPageSequence();
+                documentHandler.getContext().setLanguage(null);
             } else {
                 if (this.documentMetadata == null) {
                     this.documentMetadata = createDefaultDocumentMetadata();
@@ -502,6 +504,7 @@
                 this.inPageSequence = true;
             }
             establishForeignAttributes(pageSequence.getForeignAttributes());
+            documentHandler.getContext().setLanguage(toLocale(pageSequence));
             documentHandler.startPageSequence(null);
             resetForeignAttributes();
             processExtensionAttachments(pageSequence);
@@ -510,6 +513,17 @@
         }
     }
 
+    private Locale toLocale(PageSequence pageSequence) {
+        if (pageSequence.getLanguage() != null) {
+            if (pageSequence.getCountry() != null) {
+                return new Locale(pageSequence.getLanguage(), pageSequence.getCountry());
+            } else {
+                return new Locale(pageSequence.getLanguage());
+            }
+        }
+        return null;
+    }
+
     private Metadata createDefaultDocumentMetadata() {
         Metadata xmp = new Metadata();
         DublinCoreAdapter dc = DublinCoreSchema.getAdapter(xmp);
@@ -604,6 +618,14 @@
         documentHandler.getContext().resetForeignAttributes();
     }
 
+    private void establishStructurePointer(String ptr) {
+        documentHandler.getContext().setStructurePointer(ptr);
+    }
+
+    private void resetStructurePointer() {
+        documentHandler.getContext().resetStructurePointer();
+    }
+
     /** {@inheritDoc} */
     protected void saveGraphicsState() {
         graphicContextStack.push(graphicContext);
@@ -824,17 +846,20 @@
             currentIPPosition = saveIP;
             currentBPPosition = saveBP;
 
-            currentBPPosition += (int)(bv.getAllocBPD());
+            currentBPPosition += bv.getAllocBPD();
         }
         viewportDimensionStack.pop();
     }
 
     /** {@inheritDoc} */
     public void renderViewport(Viewport viewport) {
+        String ptr = (String) viewport.getTrait(Trait.PTR);
+        establishStructurePointer(ptr);
         Dimension dim = new Dimension(viewport.getIPD(), viewport.getBPD());
         viewportDimensionStack.push(dim);
         super.renderViewport(viewport);
         viewportDimensionStack.pop();
+        resetStructurePointer();
     }
 
     /** {@inheritDoc} */
@@ -892,6 +917,7 @@
         // stuff we only need if a link must be created:
         Rectangle ipRect = null;
         AbstractAction action = null;
+        String ptr = (String) ip.getTrait(Trait.PTR); // used for accessibility
         // make sure the rect is determined *before* calling super!
         int ipp = currentIPPosition;
         int bpp = currentBPPosition + ip.getOffset();
@@ -935,6 +961,7 @@
 
         // warn if link trait found but not allowed, else create link
         if (linkTraitFound) {
+            action.setStructurePointer(ptr);  // used for accessibility
             Link link = new Link(action, ipRect);
             this.deferredLinks.add(link);
         }
@@ -969,6 +996,8 @@
 
         String fontName = getInternalFontNameForArea(text);
         int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue();
+        String ptr = (String)text.getTrait(Trait.PTR); // used for accessibility
+        establishStructurePointer(ptr);
 
         // This assumes that *all* CIDFonts use a /ToUnicode mapping
         Typeface tf = getTypeface(fontName);
@@ -990,6 +1019,7 @@
 
         textUtil.flush();
         renderTextDecoration(tf, size, text, bl, rx);
+        resetStructurePointer();
     }
 
     /** {@inheritDoc} */
@@ -1060,10 +1090,10 @@
         private static final int INITIAL_BUFFER_SIZE = 16;
         private int[] dx = new int[INITIAL_BUFFER_SIZE];
         private int lastDXPos = 0;
-        private StringBuffer text = new StringBuffer();
+        private final StringBuffer text = new StringBuffer();
         private int startx, starty;
         private int tls, tws;
-        private boolean combined = false;
+        private final boolean combined = false;
 
         void addChar(char ch) {
             text.append(ch);
diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
index 6955147..2401b12 100644
--- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
@@ -27,16 +27,19 @@
 import java.awt.geom.AffineTransform;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 import org.w3c.dom.Document;
-
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
 import org.apache.xmlgraphics.util.QName;
 import org.apache.xmlgraphics.util.XMLizable;
 
+import org.apache.fop.accessibility.StructureTree;
 import org.apache.fop.fonts.FontInfo;
 import org.apache.fop.render.PrintRendererConfigurator;
 import org.apache.fop.render.RenderingContext;
@@ -60,6 +63,7 @@
         implements IFConstants, IFPainter, IFDocumentNavigationHandler {
 
     private IFDocumentHandler mimicHandler;
+    private int pageSequenceIndex; // used for accessibility
 
     /** Holds the intermediate format state */
     private IFState state;
@@ -210,8 +214,23 @@
             if (id != null) {
                 atts.addAttribute(XML_NAMESPACE, "id", "xml:id", XMLUtil.CDATA, id);
             }
+            Locale lang = getContext().getLanguage();
+            if (lang != null) {
+                atts.addAttribute(XML_NAMESPACE, "lang", "xml:lang", XMLUtil.CDATA,
+                        XMLUtil.toRFC3066(lang));
+            }
             addForeignAttributes(atts);
             handler.startElement(EL_PAGE_SEQUENCE, atts);
+            if (this.getUserAgent().isAccessibilityEnabled()) {
+                StructureTree structureTree = getUserAgent().getStructureTree();
+                handler.startElement(EL_STRUCTURE_TREE); // add structure tree
+                NodeList nodes = structureTree.getPageSequence(pageSequenceIndex++);
+                for (int i = 0, n = nodes.getLength(); i < n; i++) {
+                    Node node = nodes.item(i);
+                    new DOM2SAX(handler).writeFragment(node);
+                }
+                handler.endElement(EL_STRUCTURE_TREE);
+            }
         } catch (SAXException e) {
             throw new IFException("SAX error in startPageSequence()", e);
         }
@@ -392,13 +411,14 @@
             addAttribute(atts, "width", Integer.toString(rect.width));
             addAttribute(atts, "height", Integer.toString(rect.height));
             addForeignAttributes(atts);
+            addStructurePointerAttribute(atts);
             handler.element(EL_IMAGE, atts);
         } catch (SAXException e) {
             throw new IFException("SAX error in startGroup()", e);
         }
     }
 
-    private void addForeignAttributes(AttributesImpl atts) {
+    private void addForeignAttributes(AttributesImpl atts) throws SAXException {
         Map foreignAttributes = getContext().getForeignAttributes();
         if (!foreignAttributes.isEmpty()) {
             Iterator iter = foreignAttributes.entrySet().iterator();
@@ -418,6 +438,7 @@
             addAttribute(atts, "width", Integer.toString(rect.width));
             addAttribute(atts, "height", Integer.toString(rect.height));
             addForeignAttributes(atts);
+            addStructurePointerAttribute(atts);
             handler.startElement(EL_IMAGE, atts);
             new DOM2SAX(handler).writeDocument(doc, true);
             handler.endElement(EL_IMAGE);
@@ -531,6 +552,7 @@
             if (dx != null) {
                 addAttribute(atts, "dx", IFUtil.toString(dx));
             }
+            addStructurePointerAttribute(atts);
             handler.startElement(EL_TEXT, atts);
             char[] chars = text.toCharArray();
             handler.characters(chars, 0, chars.length);
@@ -617,7 +639,8 @@
     }
 
     private void addAttribute(AttributesImpl atts,
-            org.apache.xmlgraphics.util.QName attribute, String value) {
+            org.apache.xmlgraphics.util.QName attribute, String value) throws SAXException {
+        handler.startPrefixMapping(attribute.getPrefix(), attribute.getNamespaceURI());
         XMLUtil.addAttribute(atts, attribute, value);
     }
 
@@ -625,6 +648,13 @@
         XMLUtil.addAttribute(atts, localName, value);
     }
 
+    private void addStructurePointerAttribute(AttributesImpl atts) {
+        String ptr = getContext().getStructurePointer();
+        if (ptr != null) {
+            addAttribute(atts, "ptr", ptr);
+        }
+    }
+
     // ---=== IFDocumentNavigationHandler ===---
 
     private Map incompleteActions = new java.util.HashMap();
@@ -696,6 +726,9 @@
         AttributesImpl atts = new AttributesImpl();
         atts.addAttribute(null, "rect", "rect",
                 XMLConstants.CDATA, IFUtil.toString(link.getTargetRect()));
+        if (getUserAgent().isAccessibilityEnabled()) {
+            addAttribute(atts, "ptr", link.getAction().getStructurePointer());
+        }
         try {
             handler.startElement(DocumentNavigationExtensionConstants.LINK, atts);
             serializeXMLizable(link.getAction());
diff --git a/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java b/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java
index f396fd0..340b2e0 100644
--- a/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java
+++ b/src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java
@@ -27,6 +27,7 @@
 public abstract class AbstractAction implements XMLizable {
 
     private String id;
+    private String structurePointer;
 
     /**
      * Sets an ID to make the action referencable.
@@ -45,6 +46,22 @@
     }
 
     /**
+     * Sets the structure element corresponding to this action.
+     * @param structurePointer a reference to the structure element
+     */
+    public void setStructurePointer(String structurePointer) {
+        this.structurePointer = structurePointer;
+    }
+
+    /**
+     * Returns the structure element corresponding to this action.
+     * @return the reference to the structure element
+     */
+    public String getStructurePointer() {
+        return structurePointer;
+    }
+
+    /**
      * Indicates whether the action has an ID and is therefore referencable.
      * @return true if the action has an ID
      */
diff --git a/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java b/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java
index 3761308..5ca480f 100644
--- a/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java
+++ b/src/java/org/apache/fop/render/intermediate/extensions/DocumentNavigationHandler.java
@@ -48,6 +48,8 @@
 
     private IFDocumentNavigationHandler navHandler;
 
+    private String structurePointer;
+
     /**
      * Main constructor.
      * @param navHandler the navigation handler that will receive the events
@@ -96,6 +98,7 @@
                     throw new SAXException(localName + " must be the root element!");
                 }
                 Rectangle targetRect = XMLUtil.getAttributeAsRectangle(attributes, "rect");
+                structurePointer = attributes.getValue("ptr");
                 Link link = new Link(null, targetRect);
                 objectStack.push(link);
             } else if (GOTO_XY.getLocalName().equals(localName)) {
@@ -118,6 +121,9 @@
                     }
                     action = new GoToXYAction(id, pageIndex, location);
                 }
+                if (structurePointer != null) {
+                    action.setStructurePointer(structurePointer);
+                }
                 objectStack.push(action);
             } else if (GOTO_URI.getLocalName().equals(localName)) {
                 String id = attributes.getValue("id");
@@ -128,6 +134,9 @@
                 if (id != null) {
                     action.setID(id);
                 }
+                if (structurePointer != null) {
+                    action.setStructurePointer(structurePointer);
+                }
                 objectStack.push(action);
             } else {
                 throw new SAXException(
diff --git a/src/java/org/apache/fop/render/pcl/PCLEventProducer.xml b/src/java/org/apache/fop/render/pcl/PCLEventProducer.xml
index a3b36fd..d4fe60b 100644
--- a/src/java/org/apache/fop/render/pcl/PCLEventProducer.xml
+++ b/src/java/org/apache/fop/render/pcl/PCLEventProducer.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<catalogue xml:lang="en">
+<?xml version="1.0" encoding="UTF-8"?><catalogue xml:lang="en">
   <message key="org.apache.fop.render.pcl.PCLEventProducer.paperTypeUnavailable">Paper type ({pageWidth} x {pageHeight} mpt) could not be determined. Falling back to: {fallbackPaper}</message>
 </catalogue>
diff --git a/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java b/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java
new file mode 100644
index 0000000..2c13edc
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java
@@ -0,0 +1,150 @@
+/*
+ * 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.pdf.PDFName;
+import org.apache.fop.pdf.PDFObject;
+import org.apache.fop.pdf.PDFStructElem;
+
+/**
+ * This class provides the standard mappings from Formatting Objects to PDF structure types.
+ */
+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");
+
+        PDFName st = new PDFName("Div");
+        addMapping("block-container",           st);
+        addMapping("inline-container",          st);
+        addMapping("table-and-caption",         st);
+        addMapping("float",                     st);
+
+        st = new PDFName("Span");
+        addMapping("inline",                    st);
+        addMapping("wrapper",                   st);
+        addMapping("character",                 st);
+
+        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");
+        addMapping("table",                     "Table");
+        addMapping("table-body",                "TBody");
+        addMapping("table-header",              THEAD);
+        addMapping("table-footer",              TFOOT);
+        addMapping("table-row",                 "TR");
+        addMapping("table-cell",                new TableCellMapper());
+
+        addMapping("list-block",                "L");
+        addMapping("list-item",                 "LI");
+        addMapping("list-item-label",           "Lbl");
+        addMapping("list-item-body",            "LBody");
+
+        addMapping("basic-link",                "Link");
+        addMapping("footnote",                  "Note");
+        addMapping("footnote-body",             "Sect");
+        addMapping("marker",                    "Private");
+    }
+
+    private static void addMapping(String fo, String pdfName) {
+        addMapping(fo, new PDFName(pdfName));
+    }
+
+    private static void addMapping(String fo, PDFName pdfName) {
+        addMapping(fo, new SimpleMapper(pdfName));
+    }
+
+    private static void addMapping(String fo, Mapper mapper) {
+        STANDARD_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 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)STANDARD_MAPPINGS.get(fo);
+        if (mapper != null) {
+            return mapper.getStructureType(parent);
+        } else {
+            return NON_STRUCT;
+        }
+    }
+
+    private interface Mapper {
+        PDFName getStructureType(PDFObject parent);
+    }
+
+    private static class SimpleMapper implements Mapper {
+
+        private PDFName structureType;
+
+        public SimpleMapper(PDFName structureType) {
+            this.structureType = structureType;
+        }
+
+        public PDFName getStructureType(PDFObject parent) {
+            return structureType;
+        }
+
+    }
+
+    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.
+            if (THEAD.equals(grandParent.getStructureType())) {
+               return TH;
+            } else {
+               return TD;
+            }
+        }
+
+    }
+
+    private FOToPDFRoleMap() { }
+}
diff --git a/src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java b/src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java
index 841dd7e..d5e6b0b 100644
--- a/src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java
+++ b/src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java
@@ -19,6 +19,7 @@
 
 package org.apache.fop.render.pdf;
 
+
 /**
  * Constants used for configuring PDF output.
  */
diff --git a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
index fe5be4a..fb5fc4e 100644
--- a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
+++ b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
@@ -56,6 +56,8 @@
     /** Text generation utility holding the current font status */
     protected PDFTextUtil textutil;
 
+    private boolean inMarkedContentSequence;
+    private boolean inArtifactMode;
 
     /**
      * Main constructor. Creates a new PDF stream and additional helper classes for text painting
@@ -153,6 +155,40 @@
         currentStream.add("q\n");
     }
 
+    /** {@inheritDoc} */
+    protected void saveGraphicsState(String structElemType, int sequenceNum) {
+        endTextObject();
+        currentState.save();
+        beginMarkedContentSequence(structElemType, sequenceNum);
+        currentStream.add("q\n");
+    }
+
+    /**
+     * Begins a new marked content sequence (BDC or BMC). If the parameter structElemType is null,
+     * the sequenceNum is ignored and instead of a BDC with the MCID as parameter, an "Artifact"
+     * and a BMC command is generated.
+     * @param structElemType Structure Element Type
+     * @param mcid    Sequence number
+     */
+    protected void beginMarkedContentSequence(String structElemType, int mcid) {
+        assert !this.inMarkedContentSequence;
+        assert !this.inArtifactMode;
+        if (structElemType != null) {
+            currentStream.add(structElemType + " <</MCID " + String.valueOf(mcid) + ">>\n"
+                    + "BDC\n");
+        } else {
+            currentStream.add("/Artifact\nBMC\n");
+            this.inArtifactMode = true;
+        }
+        this.inMarkedContentSequence = true;
+    }
+
+    void endMarkedContentSequence() {
+        currentStream.add("EMC\n");
+        this.inMarkedContentSequence = false;
+        this.inArtifactMode = false;
+    }
+
     /**
      * Restored the graphics state valid before the previous {@link #saveGraphicsState()}.
      * @param popState true if the state should also be popped, false if only the PDF command
@@ -166,11 +202,42 @@
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * Same as {@link #restoreGraphicsState(boolean)}, with <code>true</code> as
+     * a parameter.
+     */
     protected void restoreGraphicsState() {
         restoreGraphicsState(true);
     }
 
+    /**
+     * Same as {@link #restoreGraphicsState()}, additionally ending the current
+     * marked content sequence if any.
+     */
+    protected void restoreGraphicsStateAccess() {
+        endTextObject();
+        currentStream.add("Q\n");
+        if (this.inMarkedContentSequence) {
+            endMarkedContentSequence();
+        }
+        currentState.restore();
+    }
+
+    /**
+     * Separates 2 text elements, ending the current marked content sequence and
+     * starting a new one.
+     *
+     * @param structElemType structure element type
+     * @param mcid sequence number
+     * @see #beginMarkedContentSequence(String, int)
+     */
+    protected void separateTextElements(String structElemType, int mcid) {
+        textutil.endTextObject();
+        endMarkedContentSequence();
+        beginMarkedContentSequence(structElemType, mcid);
+        textutil.beginTextObject();
+    }
+
     /** Indicates the beginning of a text object. */
     protected void beginTextObject() {
         if (!textutil.isInTextObject()) {
@@ -178,9 +245,27 @@
         }
     }
 
+    /**
+     * Indicates the beginning of a marked-content text object.
+     *
+     * @param structElemType structure element type
+     * @param mcid sequence number
+     * @see #beginTextObject()
+     * @see #beginMarkedContentSequence(String, int)
+     */
+    protected void beginTextObject(String structElemType, int mcid) {
+        if (!textutil.isInTextObject()) {
+            beginMarkedContentSequence(structElemType, mcid);
+            textutil.beginTextObject();
+        }
+    }
+
     /** Indicates the end of a text object. */
     protected void endTextObject() {
         if (textutil.isInTextObject()) {
+            if (this.inMarkedContentSequence) {
+                endMarkedContentSequence();
+            }
             textutil.endTextObject();
         }
     }
@@ -326,5 +411,28 @@
         restoreGraphicsState();
     }
 
+    /**
+     * Places a previously registered image at a certain place on the page,
+     * bracketing it as a marked-content sequence.
+     *
+     * @param x X coordinate
+     * @param y Y coordinate
+     * @param w width for image
+     * @param h height for image
+     * @param xobj the image XObject
+     * @param structElemType structure element type
+     * @param mcid sequence number
+     * @see #beginMarkedContentSequence(String, int)
+     */
+    public void placeImage(float x, float y, float w, float h, PDFXObject xobj,
+            String structElemType, int mcid) {
+        saveGraphicsState(structElemType, mcid);
+        add(format(w) + " 0 0 "
+                          + format(-h) + " "
+                          + format(x) + " "
+                          + format(y + h)
+                          + " cm\n" + xobj.getName() + " Do\n");
+        restoreGraphicsStateAccess();
+    }
 
 }
diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
index 6f4a338..3cd601b 100644
--- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
@@ -28,6 +28,8 @@
 import java.io.IOException;
 import java.util.Map;
 
+import org.w3c.dom.NodeList;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
@@ -50,6 +52,7 @@
 import org.apache.fop.render.intermediate.IFDocumentNavigationHandler;
 import org.apache.fop.render.intermediate.IFException;
 import org.apache.fop.render.intermediate.IFPainter;
+import org.apache.fop.util.XMLUtil;
 
 /**
  * {@link IFDocumentHandler} implementation that produces PDF.
@@ -59,6 +62,12 @@
     /** logging instance */
     private static Log log = LogFactory.getLog(PDFDocumentHandler.class);
 
+    private int pageSequenceIndex;
+
+    private boolean accessEnabled;
+
+    private PDFLogicalStructureHandler logicalStructureHandler;
+
     /** the PDF Document being created */
     protected PDFDocument pdfDoc;
 
@@ -86,7 +95,7 @@
     /** Used for bookmarks/outlines. */
     protected Map pageReferences = new java.util.HashMap();
 
-    private PDFDocumentNavigationHandler documentNavigationHandler
+    private final PDFDocumentNavigationHandler documentNavigationHandler
             = new PDFDocumentNavigationHandler(this);
 
     /**
@@ -97,7 +106,7 @@
 
     /** {@inheritDoc} */
     public boolean supportsPagesOutOfOrder() {
-        return true;
+        return !accessEnabled;
     }
 
     /** {@inheritDoc} */
@@ -125,11 +134,20 @@
         return this.pdfUtil;
     }
 
+    PDFLogicalStructureHandler getLogicalStructureHandler() {
+        return logicalStructureHandler;
+    }
+
     /** {@inheritDoc} */
     public void startDocument() throws IFException {
         super.startDocument();
         try {
             this.pdfDoc = pdfUtil.setupPDFDocument(this.outputStream);
+            this.accessEnabled = getUserAgent().isAccessibilityEnabled();
+            if (accessEnabled) {
+                pdfDoc.getRoot().makeTagged();
+                logicalStructureHandler = new PDFLogicalStructureHandler(pdfDoc);
+            }
         } catch (IOException e) {
             throw new IFException("I/O error in startDocument()", e);
         }
@@ -145,7 +163,6 @@
         try {
             pdfDoc.getResources().addFonts(pdfDoc, fontInfo);
             pdfDoc.outputTrailer(this.outputStream);
-
             this.pdfDoc = null;
 
             pdfResources = null;
@@ -160,7 +177,18 @@
 
     /** {@inheritDoc} */
     public void startPageSequence(String id) throws IFException {
-        //TODO page sequence title, country and language
+        //TODO page sequence title
+
+        if (this.pdfDoc.getRoot().getLanguage() == null
+                && getContext().getLanguage() != null) {
+            //No document-level language set, so we use the first page-sequence's language
+            this.pdfDoc.getRoot().setLanguage(XMLUtil.toRFC3066(getContext().getLanguage()));
+        }
+
+        if (accessEnabled) {
+            NodeList nodes = getUserAgent().getStructureTree().getPageSequence(pageSequenceIndex++);
+            logicalStructureHandler.processStructureTree(nodes, getContext().getLanguage());
+        }
     }
 
     /** {@inheritDoc} */
@@ -198,13 +226,17 @@
                 toPointAndScale(cropBox, scaleX, scaleY),
                 toPointAndScale(bleedBox, scaleX, scaleY),
                 toPointAndScale(trimBox, scaleX, scaleY));
+        if (accessEnabled) {
+            logicalStructureHandler.startPage(currentPage);
+        }
 
         pdfUtil.generatePageLabel(index, name);
 
         currentPageRef = new PageReference(currentPage, size);
         this.pageReferences.put(new Integer(index), currentPageRef);
 
-        this.generator = new PDFContentGenerator(this.pdfDoc, this.outputStream, this.currentPage);
+        this.generator = new PDFContentGenerator(this.pdfDoc, this.outputStream,
+                this.currentPage);
         // Transform the PDF's default coordinate system (0,0 at lower left) to the PDFPainter's
         AffineTransform basicPageTransform = new AffineTransform(1, 0, 0, -1, 0,
                 (scaleY * size.height) / 1000f);
@@ -221,7 +253,7 @@
 
     /** {@inheritDoc} */
     public IFPainter startPageContent() throws IFException {
-        return new PDFPainter(this);
+        return new PDFPainter(this, logicalStructureHandler);
     }
 
     /** {@inheritDoc} */
@@ -231,6 +263,9 @@
 
     /** {@inheritDoc} */
     public void endPage() throws IFException {
+        if (accessEnabled) {
+            logicalStructureHandler.endPage();
+        }
         try {
             this.documentNavigationHandler.commit();
             this.pdfDoc.registerObject(generator.getStream());
@@ -267,8 +302,8 @@
 
     static final class PageReference {
 
-        private PDFReference pageRef;
-        private Dimension pageDimension;
+        private final PDFReference pageRef;
+        private final Dimension pageDimension;
 
         private PageReference(PDFPage page, Dimension dim) {
             this.pageRef = page.makeReference();
diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
index 3e1024d..5e1b1b2 100644
--- a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
@@ -47,10 +47,10 @@
  */
 public class PDFDocumentNavigationHandler implements IFDocumentNavigationHandler {
 
-    private PDFDocumentHandler documentHandler;
+    private final PDFDocumentHandler documentHandler;
 
-    private Map incompleteActions = new java.util.HashMap();
-    private Map completeActions = new java.util.HashMap();
+    private final Map incompleteActions = new java.util.HashMap();
+    private final Map completeActions = new java.util.HashMap();
 
     /**
      * Default constructor.
@@ -111,6 +111,11 @@
         PDFLink pdfLink = getPDFDoc().getFactory().makeLink(
                 targetRect2D, pdfAction);
         if (pdfLink != null) {
+            String ptr = link.getAction().getStructurePointer();
+            if (documentHandler.getUserAgent().isAccessibilityEnabled()
+                    && ptr != null && ptr.length() > 0) {
+                documentHandler.getLogicalStructureHandler().addLinkContentItem(pdfLink, ptr);
+            }
             documentHandler.currentPage.addAnnotation(pdfLink);
         }
     }
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java
index 1871780..c324282 100644
--- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java
@@ -35,6 +35,7 @@
 import org.apache.fop.render.AbstractImageHandlerGraphics2D;
 import org.apache.fop.render.RendererContext;
 import org.apache.fop.render.RenderingContext;
+import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo;
 import org.apache.fop.svg.PDFGraphics2D;
 
 /**
@@ -63,6 +64,9 @@
                 renderer.currentPage,
                 renderer.getFontInfo());
         Rectangle effPos = new Rectangle(origin.x + pos.x, origin.y + pos.y, pos.width, pos.height);
+        if (context.getUserAgent().isAccessibilityEnabled()) {
+            pdfContext.setMarkedContentInfo(renderer.addCurrentImageToStructureTree());
+        }
         handleImage(pdfContext, image, effPos);
         return null;
     }
@@ -87,7 +91,13 @@
         float sy = fheight / (float)imh;
 
         generator.comment("G2D start");
-        generator.saveGraphicsState();
+        boolean accessibilityEnabled = context.getUserAgent().isAccessibilityEnabled();
+        if (accessibilityEnabled) {
+            MarkedContentInfo mci = pdfContext.getMarkedContentInfo();
+            generator.saveGraphicsState(mci.tag, mci.mcid);
+        } else {
+            generator.saveGraphicsState();
+        }
         generator.updateColor(Color.black, false, null);
         generator.updateColor(Color.black, true, null);
 
@@ -115,7 +125,11 @@
         imageG2D.getGraphics2DImagePainter().paint(graphics, area);
 
         generator.add(graphics.getString());
-        generator.restoreGraphicsState();
+        if (accessibilityEnabled) {
+            generator.restoreGraphicsStateAccess();
+        } else {
+            generator.restoreGraphicsState();
+        }
         generator.comment("G2D end");
     }
 
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java
index d47d5a4..02dd98e 100644
--- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java
@@ -34,6 +34,7 @@
 import org.apache.fop.render.ImageHandler;
 import org.apache.fop.render.RendererContext;
 import org.apache.fop.render.RenderingContext;
+import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo;
 
 /**
  * Image handler implementation which handles raw JPEG images for PDF output.
@@ -82,7 +83,12 @@
         float y = (float)pos.getY() / 1000f;
         float w = (float)pos.getWidth() / 1000f;
         float h = (float)pos.getHeight() / 1000f;
-        generator.placeImage(x, y, w, h, xobj);
+        if (context.getUserAgent().isAccessibilityEnabled()) {
+            MarkedContentInfo mci = pdfContext.getMarkedContentInfo();
+            generator.placeImage(x, y, w, h, xobj, mci.tag, mci.mcid);
+        } else {
+            generator.placeImage(x, y, w, h, xobj);
+        }
     }
 
     /** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java
index 3e57c72..3c02cb6 100644
--- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java
@@ -34,6 +34,7 @@
 import org.apache.fop.render.ImageHandler;
 import org.apache.fop.render.RendererContext;
 import org.apache.fop.render.RenderingContext;
+import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo;
 
 /**
  * Image handler implementation which handles RenderedImage instances for PDF output.
@@ -83,7 +84,12 @@
         float y = (float)pos.getY() / 1000f;
         float w = (float)pos.getWidth() / 1000f;
         float h = (float)pos.getHeight() / 1000f;
-        generator.placeImage(x, y, w, h, xobj);
+        if (context.getUserAgent().isAccessibilityEnabled()) {
+            MarkedContentInfo mci = pdfContext.getMarkedContentInfo();
+            generator.placeImage(x, y, w, h, xobj, mci.tag, mci.mcid);
+        } else {
+            generator.placeImage(x, y, w, h, xobj);
+        }
     }
 
     /** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java
index d1b7aa9..e6d2c8a 100644
--- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java
@@ -40,6 +40,7 @@
 import org.apache.fop.image.loader.batik.BatikImageFlavors;
 import org.apache.fop.render.ImageHandler;
 import org.apache.fop.render.RenderingContext;
+import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo;
 import org.apache.fop.svg.PDFAElementBridge;
 import org.apache.fop.svg.PDFBridgeContext;
 import org.apache.fop.svg.PDFGraphics2D;
@@ -101,8 +102,8 @@
         float w = (float)ctx.getDocumentSize().getWidth() * 1000f;
         float h = (float)ctx.getDocumentSize().getHeight() * 1000f;
 
-        float sx = pos.width / (float)w;
-        float sy = pos.height / (float)h;
+        float sx = pos.width / w;
+        float sy = pos.height / h;
 
         //Scaling and translation for the bounding box of the image
         AffineTransform scaling = new AffineTransform(
@@ -121,6 +122,10 @@
          */
         generator.comment("SVG setup");
         generator.saveGraphicsState();
+        if (context.getUserAgent().isAccessibilityEnabled()) {
+            MarkedContentInfo mci = pdfContext.getMarkedContentInfo();
+            generator.beginMarkedContentSequence(mci.tag, mci.mcid);
+        }
         generator.setColor(Color.black, false);
         generator.setColor(Color.black, true);
 
@@ -168,7 +173,11 @@
             eventProducer.svgRenderingError(this, e, image.getInfo().getOriginalURI());
         }
         generator.getState().restore();
-        generator.restoreGraphicsState();
+        if (context.getUserAgent().isAccessibilityEnabled()) {
+            generator.restoreGraphicsStateAccess();
+        } else {
+            generator.restoreGraphicsState();
+        }
         generator.comment("SVG end");
     }
 
diff --git a/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java b/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java
new file mode 100644
index 0000000..d55094d
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java
@@ -0,0 +1,299 @@
+/*
+ * 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.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.apache.fop.fo.extensions.ExtensionElementMapping;
+import org.apache.fop.fo.extensions.InternalElementMapping;
+import org.apache.fop.pdf.PDFArray;
+import org.apache.fop.pdf.PDFDictionary;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFLink;
+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;
+
+
+/**
+ * Handles the creation of the logical structure in the PDF document.
+ */
+class PDFLogicalStructureHandler {
+
+    private static final PDFName MCR = new PDFName("MCR");
+
+    private static final PDFName OBJR = new PDFName("OBJR");
+
+    private static final MarkedContentInfo ARTIFACT = new MarkedContentInfo(null, -1, null);
+
+    private final PDFDocument pdfDoc;
+
+    /**
+     * Map of references to the corresponding structure elements.
+     */
+    private final Map structTreeMap = new HashMap();
+
+    private final PDFParentTree parentTree = new PDFParentTree();
+
+    private int parentTreeKey;
+
+    private PDFPage currentPage;
+
+    /**
+     * The array of references, from marked-content sequences in the current
+     * page, to their parent structure elements. This will be a value in the
+     * structure parent tree, whose corresponding key will be the page's
+     * StructParents entry.
+     */
+    private PDFArray pageParentTreeArray;
+
+    private PDFStructElem rootStructureElement;
+
+    /**
+     * Class providing the necessary information for bracketing content
+     * associated to a structure element as a marked-content sequence.
+     */
+    static final class MarkedContentInfo {
+
+        /**
+         * A value that can be used for the tag operand of a marked-content
+         * operator. This is the structure type of the corresponding structure
+         * element.
+         */
+        final String tag;
+
+        /**
+         * The value for the MCID entry of the marked-content sequence's property list.
+         */
+        final int mcid;
+
+        private final PDFStructElem parent;
+
+        private MarkedContentInfo(String tag, int mcid, PDFStructElem parent) {
+            this.tag = tag;
+            this.mcid = mcid;
+            this.parent = parent;
+        }
+    }
+
+    /**
+     * Creates a new instance for handling the logical structure of the given document.
+     *
+     * @param pdfDoc a document
+     */
+    PDFLogicalStructureHandler(PDFDocument pdfDoc) {
+        this.pdfDoc = pdfDoc;
+        PDFStructTreeRoot structTreeRoot = pdfDoc.getFactory().makeStructTreeRoot(parentTree);
+        rootStructureElement = pdfDoc.getFactory().makeStructureElement(
+                FOToPDFRoleMap.mapFormattingObject("root", structTreeRoot), structTreeRoot);
+        structTreeRoot.addKid(rootStructureElement);
+    }
+
+    /**
+     * Converts the given structure tree into PDF.
+     *
+     * @param structureTree the structure tree of the current page sequence
+     * @param language language set on the page sequence
+     */
+    void processStructureTree(NodeList structureTree, Locale language) {
+        pdfDoc.enforceLanguageOnRoot();
+        PDFStructElem structElemPart = pdfDoc.getFactory().makeStructureElement(
+                FOToPDFRoleMap.mapFormattingObject("page-sequence", rootStructureElement),
+                rootStructureElement);
+        rootStructureElement.addKid(structElemPart);
+        if (language != null) {
+            structElemPart.setLanguage(language);
+        }
+
+        for (int i = 0, n = structureTree.getLength(); i < n; i++) {
+            Node node = structureTree.item(i);
+            assert node.getLocalName().equals("flow")
+                    || node.getLocalName().equals("static-content");
+            PDFStructElem structElemSect = pdfDoc.getFactory().makeStructureElement(
+                    FOToPDFRoleMap.mapFormattingObject(node.getLocalName(), structElemPart),
+                    structElemPart);
+            structElemPart.addKid(structElemSect);
+            NodeList childNodes = node.getChildNodes();
+            for (int j = 0, m = childNodes.getLength(); j < m; j++) {
+                processNode(childNodes.item(j), structElemSect, true);
+            }
+        }
+    }
+
+    private void processNode(Node node, PDFStructElem parent, boolean addKid) {
+        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);
+        // 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);
+        }
+        if (nodeName.equals("external-graphic") || nodeName.equals("instream-foreign-object")) {
+            Node altTextNode = node.getAttributes().getNamedItemNS(
+                    ExtensionElementMapping.URI, "alt-text");
+            if (altTextNode != null) {
+                structElem.put("Alt", altTextNode.getNodeValue());
+            } else {
+                structElem.put("Alt", "No alternate text specified");
+            }
+        }
+        structTreeMap.put(ptr, structElem);
+        NodeList nodes = node.getChildNodes();
+        for (int i = 0, n = nodes.getLength(); i < n; i++) {
+            processNode(nodes.item(i), structElem, false);
+        }
+    }
+
+    private int getNextParentTreeKey() {
+        return parentTreeKey++;
+    }
+
+    /**
+     * Receive notification of the beginning of a new page.
+     *
+     * @param page the page that will be rendered in PDF
+     */
+    void startPage(PDFPage page) {
+        currentPage = page;
+        currentPage.setStructParents(getNextParentTreeKey());
+        pageParentTreeArray = new PDFArray();
+    }
+
+    /**
+     * Receive notification of the end of the current page.
+     */
+    void endPage() {
+        // TODO
+        // Values in a number tree must be indirect references to the PDF
+        // objects associated to the keys. To enforce that the array is
+        // registered to the PDF document. Unfortunately that can't be done
+        // earlier since a call to PDFContentGenerator.flushPDFDoc can be made
+        // before the array is complete, which would result in only part of it
+        // being output to the PDF.
+        // This should really be handled by PDFNumsArray
+        pdfDoc.registerObject(pageParentTreeArray);
+        parentTree.getNums().put(currentPage.getStructParents(), pageParentTreeArray);
+    }
+
+    private MarkedContentInfo addToParentTree(String structurePointer) {
+        PDFStructElem parent = (PDFStructElem) structTreeMap.get(structurePointer);
+        if (parent == null) {
+            return ARTIFACT;
+        } else {
+            pageParentTreeArray.add(parent);
+            String type = parent.getStructureType().toString();
+            int mcid = pageParentTreeArray.length() - 1;
+            return new MarkedContentInfo(type, mcid, parent);
+        }
+    }
+
+    /**
+     * Adds a content item corresponding to text into the structure tree, if
+     * there is a structure element associated to it.
+     *
+     * @param structurePointer reference to the parent structure element of the
+     * piece of text
+     * @return the necessary information for bracketing the content as a
+     * marked-content sequence. If there is no element in the structure tree
+     * associated to that content, returns an instance whose
+     * {@link MarkedContentInfo#tag} value is <code>null</code>. The content
+     * must then be treated as an artifact.
+     */
+    MarkedContentInfo addTextContentItem(String structurePointer) {
+        MarkedContentInfo mci = addToParentTree(structurePointer);
+        if (mci != ARTIFACT) {
+            PDFDictionary contentItem = new PDFDictionary();
+            contentItem.put("Type", MCR);
+            contentItem.put("Pg", this.currentPage);
+            contentItem.put("MCID", mci.mcid);
+            mci.parent.addKid(contentItem);
+        }
+        return mci;
+    }
+
+    /**
+     * Adds a content item corresponding to an image into the structure tree, if
+     * there is a structure element associated to it.
+     *
+     * @param structurePointer reference to the parent structure element of the
+     * image
+     * @return the necessary information for bracketing the content as a
+     * marked-content sequence. If there is no element in the structure tree
+     * associated to that image, returns an instance whose
+     * {@link MarkedContentInfo#tag} value is <code>null</code>. The image
+     * must then be treated as an artifact.
+     */
+    MarkedContentInfo addImageContentItem(String structurePointer) {
+        MarkedContentInfo mci = addToParentTree(structurePointer);
+        if (mci != ARTIFACT) {
+            mci.parent.setMCIDKid(mci.mcid);
+            mci.parent.setPage(this.currentPage);
+        }
+        return mci;
+    }
+
+    // While the PDF spec allows images to be referred as PDF objects, this
+    // makes the Acrobat Pro checker complain that the image is not accessible.
+    // Its alt-text is still read aloud though. Using marked-content sequences
+    // like for text works.
+//    MarkedContentInfo addImageObject(String parentReference) {
+//        MarkedContentInfo mci = addToParentTree(parentReference);
+//        if (mci != ARTIFACT) {
+//            PDFDictionary contentItem = new PDFDictionary();
+//            contentItem.put("Type", OBJR);
+//            contentItem.put("Pg", this.currentPage);
+//            contentItem.put("Obj", null);
+//            mci.parent.addKid(contentItem);
+//        }
+//        return mci;
+//    }
+
+    /**
+     * Adds a content item corresponding to the given link into the structure
+     * tree.
+     *
+     * @param link a link
+     * @param structurePointer reference to the corresponding parent structure element
+     */
+    void addLinkContentItem(PDFLink link, String structurePointer) {
+        int structParent = getNextParentTreeKey();
+        link.setStructParent(structParent);
+        parentTree.getNums().put(structParent, link);
+        PDFDictionary contentItem = new PDFDictionary();
+        contentItem.put("Type", OBJR);
+        contentItem.put("Pg", this.currentPage);
+        contentItem.put("Obj", link);
+        PDFStructElem parent = (PDFStructElem) structTreeMap.get(structurePointer);
+        parent.addKid(contentItem);
+    }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java
index fa00fd7..f72f09a 100644
--- a/src/java/org/apache/fop/render/pdf/PDFPainter.java
+++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java
@@ -29,9 +29,6 @@
 
 import org.w3c.dom.Document;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
 import org.apache.fop.fonts.Font;
 import org.apache.fop.fonts.FontInfo;
 import org.apache.fop.fonts.FontTriplet;
@@ -47,6 +44,7 @@
 import org.apache.fop.render.intermediate.IFContext;
 import org.apache.fop.render.intermediate.IFException;
 import org.apache.fop.render.intermediate.IFState;
+import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo;
 import org.apache.fop.traits.BorderProps;
 import org.apache.fop.traits.RuleStyle;
 import org.apache.fop.util.CharUtilities;
@@ -56,26 +54,33 @@
  */
 public class PDFPainter extends AbstractIFPainter {
 
-    /** logging instance */
-    private static Log log = LogFactory.getLog(PDFPainter.class);
-
-    private PDFDocumentHandler documentHandler;
+    private final PDFDocumentHandler documentHandler;
 
     /** The current content generator */
     protected PDFContentGenerator generator;
 
-    private PDFBorderPainter borderPainter;
+    private final PDFBorderPainter borderPainter;
+
+    private boolean accessEnabled;
+
+    private MarkedContentInfo imageMCI;
+
+    private PDFLogicalStructureHandler logicalStructureHandler;
 
     /**
      * Default constructor.
      * @param documentHandler the parent document handler
+     * @param logicalStructureHandler the logical structure handler
      */
-    public PDFPainter(PDFDocumentHandler documentHandler) {
+    public PDFPainter(PDFDocumentHandler documentHandler,
+            PDFLogicalStructureHandler logicalStructureHandler) {
         super();
         this.documentHandler = documentHandler;
+        this.logicalStructureHandler = logicalStructureHandler;
         this.generator = documentHandler.generator;
         this.borderPainter = new PDFBorderPainter(this.generator);
         this.state = IFState.create();
+        accessEnabled = this.getUserAgent().isAccessibilityEnabled();
     }
 
     /** {@inheritDoc} */
@@ -122,22 +127,36 @@
     }
 
     /** {@inheritDoc} */
-    public void drawImage(String uri, Rectangle rect) throws IFException {
+    public void drawImage(String uri, Rectangle rect)
+            throws IFException {
         PDFXObject xobject = getPDFDoc().getXObject(uri);
         if (xobject != null) {
-            placeImage(rect, xobject);
-            return;
+            if (accessEnabled) {
+                String ptr = getContext().getStructurePointer();
+                prepareImageMCID(ptr);
+                placeImageAccess(rect, xobject);
+            } else {
+                placeImage(rect, xobject);
+            }
+        } else {
+            if (accessEnabled) {
+                String ptr = getContext().getStructurePointer();
+                prepareImageMCID(ptr);
+            }
+            drawImageUsingURI(uri, rect);
+            flushPDFDoc();
         }
+    }
 
-        drawImageUsingURI(uri, rect);
-
-        flushPDFDoc();
+    private void prepareImageMCID(String ptr) {
+        imageMCI = logicalStructureHandler.addImageContentItem(ptr);
     }
 
     /** {@inheritDoc} */
     protected RenderingContext createRenderingContext() {
         PDFRenderingContext pdfContext = new PDFRenderingContext(
                 getUserAgent(), generator, this.documentHandler.currentPage, getFontInfo());
+        pdfContext.setMarkedContentInfo(imageMCI);
         return pdfContext;
     }
 
@@ -158,11 +177,31 @@
                           + " cm " + xobj.getName() + " Do\n");
         generator.restoreGraphicsState();
     }
+    /**
+     * Places a previously registered image at a certain place on the page - Accessibility version
+     * @param x X coordinate
+     * @param y Y coordinate
+     * @param w width for image
+     * @param h height for image
+     * @param xobj the image XObject
+     */
+    private void placeImageAccess(Rectangle rect, PDFXObject xobj) {
+        generator.saveGraphicsState(imageMCI.tag, imageMCI.mcid);
+        generator.add(format(rect.width) + " 0 0 "
+                          + format(-rect.height) + " "
+                          + format(rect.x) + " "
+                          + format(rect.y + rect.height )
+                          + " cm " + xobj.getName() + " Do\n");
+        generator.restoreGraphicsStateAccess();
+    }
 
     /** {@inheritDoc} */
     public void drawImage(Document doc, Rectangle rect) throws IFException {
+        if (accessEnabled) {
+            String ptr = getContext().getStructurePointer();
+            prepareImageMCID(ptr);
+        }
         drawImageUsingDocument(doc, rect);
-
         flushPDFDoc();
     }
 
@@ -253,10 +292,22 @@
     }
 
     /** {@inheritDoc} */
-    public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[] dx, String text)
+    public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[] dx,
+            String text)
             throws IFException {
-        generator.updateColor(state.getTextColor(), true, null);
-        generator.beginTextObject();
+        if (accessEnabled) {
+            String ptr = getContext().getStructurePointer();
+            MarkedContentInfo mci = logicalStructureHandler.addTextContentItem(ptr);
+            if (generator.getTextUtil().isInTextObject()) {
+                generator.separateTextElements(mci.tag, mci.mcid);
+            }
+            generator.updateColor(state.getTextColor(), true, null);
+            generator.beginTextObject(mci.tag, mci.mcid);
+        } else {
+            generator.updateColor(state.getTextColor(), true, null);
+            generator.beginTextObject();
+        }
+
         FontTriplet triplet = new FontTriplet(
                 state.getFontFamily(), state.getFontStyle(), state.getFontWeight());
         //TODO Ignored: state.getFontVariant()
@@ -277,7 +328,7 @@
         PDFTextUtil textutil = generator.getTextUtil();
         textutil.updateTf(fontKey, fontSize, tf.isMultiByte());
 
-        generator.updateCharacterSpacing((float)letterSpacing / 1000f);
+        generator.updateCharacterSpacing(letterSpacing / 1000f);
 
         textutil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, x / 1000f, y / 1000f));
         int l = text.length();
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
index 3b73715..9fe08c2 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
@@ -31,8 +31,12 @@
 import java.io.OutputStream;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+
 import org.apache.xmlgraphics.image.loader.ImageException;
 import org.apache.xmlgraphics.image.loader.ImageFlavor;
 import org.apache.xmlgraphics.image.loader.ImageInfo;
@@ -61,6 +65,7 @@
 import org.apache.fop.area.inline.Leader;
 import org.apache.fop.area.inline.SpaceArea;
 import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.area.inline.Viewport;
 import org.apache.fop.area.inline.WordArea;
 import org.apache.fop.datatypes.URISpecification;
 import org.apache.fop.events.ResourceEventProducer;
@@ -91,9 +96,11 @@
 import org.apache.fop.render.AbstractPathOrientedRenderer;
 import org.apache.fop.render.Graphics2DAdapter;
 import org.apache.fop.render.RendererContext;
+import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo;
 import org.apache.fop.traits.RuleStyle;
 import org.apache.fop.util.AbstractPaintingState;
 import org.apache.fop.util.CharUtilities;
+import org.apache.fop.util.XMLUtil;
 import org.apache.fop.util.AbstractPaintingState.AbstractData;
 
 /**
@@ -127,7 +134,7 @@
      * this is used for prepared pages that cannot be immediately
      * rendered
      */
-    protected Map pages = null;
+    private Map pages;
 
     /**
      * Maps unique PageViewport key to PDF page reference
@@ -193,6 +200,14 @@
     /** Image handler registry */
     private final PDFImageHandlerRegistry imageHandlerRegistry = new PDFImageHandlerRegistry();
 
+    private boolean accessEnabled;
+
+    private PDFLogicalStructureHandler logicalStructureHandler;
+
+    private int pageSequenceIndex;
+
+    /** Reference in the structure tree to the image being rendered. */
+    private String imageReference;
 
     /**
      * create the PDF renderer
@@ -204,6 +219,7 @@
     public void setUserAgent(FOUserAgent agent) {
         super.setUserAgent(agent);
         this.pdfUtil = new PDFRenderingUtil(getUserAgent());
+        accessEnabled = agent.isAccessibilityEnabled();
     }
 
     PDFRenderingUtil getPDFUtil() {
@@ -225,6 +241,10 @@
         }
         ostream = stream;
         this.pdfDoc = pdfUtil.setupPDFDocument(stream);
+        if (accessEnabled) {
+            pdfDoc.getRoot().makeTagged();
+            logicalStructureHandler = new PDFLogicalStructureHandler(pdfDoc);
+        }
     }
 
     /**
@@ -274,8 +294,7 @@
      * {@inheritDoc}
      */
     public boolean supportsOutOfOrder() {
-        //return false;
-        return true;
+        return !accessEnabled;
     }
 
     /**
@@ -394,17 +413,24 @@
                 info.setTitle(str);
             }
         }
+        Locale language = null;
         if (pageSequence.getLanguage() != null) {
             String lang = pageSequence.getLanguage();
             String country = pageSequence.getCountry();
-            String langCode = lang + (country != null ? "-" + country : "");
+            if (lang != null) {
+                language = (country == null) ? new Locale(lang) : new Locale(lang, country);
+            }
             if (pdfDoc.getRoot().getLanguage() == null) {
                 //Only set if not set already (first non-null is used)
                 //Note: No checking is performed whether the values are valid!
-                pdfDoc.getRoot().setLanguage(langCode);
+                pdfDoc.getRoot().setLanguage(XMLUtil.toRFC3066(language));
             }
         }
         pdfUtil.generateDefaultXMPMetadata();
+        if (accessEnabled) {
+            NodeList nodes = getUserAgent().getStructureTree().getPageSequence(pageSequenceIndex++);
+            logicalStructureHandler.processStructureTree(nodes, language);
+        }
     }
 
     /**
@@ -457,6 +483,10 @@
         }
         currentPageRef = currentPage.referencePDF();
 
+        if (accessEnabled) {
+            logicalStructureHandler.startPage(currentPage);
+        }
+
         Rectangle bounds = page.getViewArea();
         pageHeight = bounds.height;
 
@@ -474,6 +504,10 @@
 
         super.renderPage(page);
 
+        if (accessEnabled) {
+            logicalStructureHandler.endPage();
+        }
+
         this.pdfDoc.registerObject(generator.getStream());
         currentPage.setContents(generator.getStream());
         PDFAnnotList annots = currentPage.getAnnotations();
@@ -903,11 +937,22 @@
                          + pdfDoc.getProfile());
             } else if (action != null) {
                 PDFLink pdfLink = factory.makeLink(ipRect, action);
+                if (accessEnabled) {
+                    String ptr = (String) ip.getTrait(Trait.PTR);
+                    logicalStructureHandler.addLinkContentItem(pdfLink, ptr);
+                }
                 currentPage.addAnnotation(pdfLink);
             }
         }
     }
 
+    /** {@inheritDoc} */
+    public void renderViewport(Viewport viewport) {
+        imageReference = (String) viewport.getTrait(Trait.PTR);
+        super.renderViewport(viewport);
+        imageReference = null;
+    }
+
     private Typeface getTypeface(String fontName) {
         Typeface tf = (Typeface) fontInfo.getFonts().get(fontName);
         if (tf instanceof LazyFont) {
@@ -922,7 +967,16 @@
         Color ct = (Color) text.getTrait(Trait.COLOR);
         updateColor(ct, true);
 
-        beginTextObject();
+        if (accessEnabled) {
+            String ptr = (String) text.getTrait(Trait.PTR);
+            MarkedContentInfo mci = logicalStructureHandler.addTextContentItem(ptr);
+            if (generator.getTextUtil().isInTextObject()) {
+                generator.separateTextElements(mci.tag, mci.mcid);
+            }
+            generator.beginTextObject(mci.tag, mci.mcid);
+        } else {
+            beginTextObject();
+        }
 
         String fontName = getInternalFontNameForArea(text);
         int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue();
@@ -1178,13 +1232,22 @@
      * @param xobj the image XObject
      */
     public void placeImage(float x, float y, float w, float h, PDFXObject xobj) {
-        saveGraphicsState();
+        if (accessEnabled) {
+            MarkedContentInfo mci = logicalStructureHandler.addImageContentItem(imageReference);
+            generator.saveGraphicsState(mci.tag, mci.mcid);
+        } else {
+            saveGraphicsState();
+        }
         generator.add(format(w) + " 0 0 "
                           + format(-h) + " "
                           + format(currentIPPosition / 1000f + x) + " "
                           + format(currentBPPosition / 1000f + h + y)
                           + " cm\n" + xobj.getName() + " Do\n");
-        restoreGraphicsState();
+        if (accessEnabled) {
+            generator.restoreGraphicsStateAccess();
+        } else {
+            restoreGraphicsState();
+        }
     }
 
     /** {@inheritDoc} */
@@ -1205,6 +1268,18 @@
         return context;
     }
 
+    /** {@inheritDoc} */
+    public void renderDocument(Document doc, String ns, Rectangle2D pos, Map foreignAttributes) {
+        if (accessEnabled) {
+            MarkedContentInfo mci = logicalStructureHandler.addImageContentItem(imageReference);
+            generator.beginMarkedContentSequence(mci.tag, mci.mcid);
+        }
+        super.renderDocument(doc, ns, pos, foreignAttributes);
+        if (accessEnabled) {
+            generator.endMarkedContentSequence();
+        }
+    }
+
     /**
      * Render leader area.
      * This renders a leader area which is an area with a rule.
@@ -1272,5 +1347,9 @@
     public void setEncryptionParams(PDFEncryptionParams encryptionParams) {
         this.pdfUtil.setEncryptionParams(encryptionParams);
     }
+
+    MarkedContentInfo addCurrentImageToStructureTree() {
+        return logicalStructureHandler.addImageContentItem(imageReference);
+    }
 }
 
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingContext.java b/src/java/org/apache/fop/render/pdf/PDFRenderingContext.java
index 98b0c82..80adfa5 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderingContext.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderingContext.java
@@ -25,6 +25,7 @@
 import org.apache.fop.fonts.FontInfo;
 import org.apache.fop.pdf.PDFPage;
 import org.apache.fop.render.AbstractRenderingContext;
+import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo;
 
 /**
  * Rendering context for PDF production.
@@ -34,6 +35,7 @@
     private PDFContentGenerator generator;
     private FontInfo fontInfo;
     private PDFPage page;
+    private MarkedContentInfo mci;
 
     /**
      * Main constructor.
@@ -79,4 +81,11 @@
         return this.fontInfo;
     }
 
+    void setMarkedContentInfo(MarkedContentInfo mci) {
+        this.mci = mci;
+    }
+
+    MarkedContentInfo getMarkedContentInfo() {
+        return mci;
+    }
 }
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
index 2e3c831..3d68812 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
@@ -37,6 +37,7 @@
 import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter;
 import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema;
 
+import org.apache.fop.accessibility.Accessibility;
 import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.fo.extensions.xmp.XMPMetadata;
 import org.apache.fop.pdf.PDFAMode;
@@ -109,7 +110,7 @@
 
     private void initialize() {
         PDFEncryptionParams params
-        = (PDFEncryptionParams)userAgent.getRendererOptions().get(ENCRYPTION_PARAMS);
+                = (PDFEncryptionParams)userAgent.getRendererOptions().get(ENCRYPTION_PARAMS);
         if (params != null) {
             this.encryptionParams = params; //overwrite if available
         }
@@ -161,6 +162,10 @@
         if (s != null) {
             this.pdfAMode = PDFAMode.valueOf(s);
         }
+        if (this.pdfAMode.isPDFA1LevelA()) {
+            //Enable accessibility if PDF/A-1a is enabled because it requires tagged PDF.
+            userAgent.getRendererOptions().put(Accessibility.ACCESSIBILITY, Boolean.TRUE);
+        }
         s = (String)userAgent.getRendererOptions().get(PDF_X_MODE);
         if (s != null) {
             this.pdfXMode = PDFXMode.valueOf(s);
diff --git a/src/java/org/apache/fop/render/ps/PSEventProducer.xml b/src/java/org/apache/fop/render/ps/PSEventProducer.xml
index a007822..f2fe604 100644
--- a/src/java/org/apache/fop/render/ps/PSEventProducer.xml
+++ b/src/java/org/apache/fop/render/ps/PSEventProducer.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<catalogue xml:lang="en">
+<?xml version="1.0" encoding="UTF-8"?><catalogue xml:lang="en">
   <message key="org.apache.fop.render.ps.PSEventProducer.postscriptDictionaryParseError">Failed to parse dictionary string. Reason: {e}, content = "{content}"</message>
 </catalogue>
diff --git a/src/java/org/apache/fop/render/rtf/RTFEventProducer.xml b/src/java/org/apache/fop/render/rtf/RTFEventProducer.xml
index 8f1f21a..e81a751 100644
--- a/src/java/org/apache/fop/render/rtf/RTFEventProducer.xml
+++ b/src/java/org/apache/fop/render/rtf/RTFEventProducer.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<catalogue xml:lang="en">
+<?xml version="1.0" encoding="UTF-8"?><catalogue xml:lang="en">
   <message key="locator">[ (See position {loc})| (See {#gatherContextInfo})| (No context info available)]</message>
   <message key="org.apache.fop.render.rtf.RTFEventProducer.onlySPMSupported">Only simple-page-masters are supported on page-sequences. Using default simple-page-master from page-sequence-master "{masterReference}".{{locator}}</message>
   <message key="org.apache.fop.render.rtf.RTFEventProducer.noSPMFound">No simple-page-master could be determined.</message>
diff --git a/src/java/org/apache/fop/render/txt/TXTRenderer.java b/src/java/org/apache/fop/render/txt/TXTRenderer.java
index 575f123..3e2fab2 100644
--- a/src/java/org/apache/fop/render/txt/TXTRenderer.java
+++ b/src/java/org/apache/fop/render/txt/TXTRenderer.java
@@ -28,6 +28,8 @@
 import java.util.List;
 import java.util.Map;
 
+import org.apache.xmlgraphics.util.UnitConv;
+
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.CTM;
@@ -37,7 +39,6 @@
 import org.apache.fop.render.AbstractPathOrientedRenderer;
 import org.apache.fop.render.txt.border.AbstractBorderElement;
 import org.apache.fop.render.txt.border.BorderManager;
-import org.apache.xmlgraphics.util.UnitConv;
 
 /**
  * Renderer that renders areas to plain text.
diff --git a/src/java/org/apache/fop/render/xml/XMLRenderer.java b/src/java/org/apache/fop/render/xml/XMLRenderer.java
index 8e8ae3f..a8a1a19 100644
--- a/src/java/org/apache/fop/render/xml/XMLRenderer.java
+++ b/src/java/org/apache/fop/render/xml/XMLRenderer.java
@@ -34,7 +34,8 @@
 import javax.xml.transform.stream.StreamResult;
 
 import org.w3c.dom.Document;
-
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
 
 import org.apache.xmlgraphics.util.QName;
@@ -86,6 +87,7 @@
 import org.apache.fop.render.RendererContext;
 import org.apache.fop.render.XMLHandler;
 import org.apache.fop.util.ColorUtil;
+import org.apache.fop.util.DOM2SAX;
 
 /**
  * Renderer that renders areas to XML for debugging purposes.
@@ -105,6 +107,8 @@
     /** If not null, the XMLRenderer will mimic another renderer by using its font setup. */
     protected Renderer mimic;
 
+    private int pageSequenceIndex;
+
     /**
      * Creates a new XML renderer.
      */
@@ -440,6 +444,20 @@
         }
         transferForeignObjects(pageSequence);
         startElement("pageSequence", atts);
+        if (this.getUserAgent().isAccessibilityEnabled()) {
+            String structureTreeElement = "structureTree";
+            startElement(structureTreeElement);
+            NodeList nodes = getUserAgent().getStructureTree().getPageSequence(pageSequenceIndex++);
+            for (int i = 0, n = nodes.getLength(); i < n; i++) {
+                Node node = nodes.item(i);
+                try {
+                    new DOM2SAX(handler).writeFragment(node);
+                } catch (SAXException e) {
+                    handleSAXException(e);
+                }
+            }
+            endElement(structureTreeElement);
+        }
         handleExtensionAttachments(pageSequence.getExtensionAttachments());
         LineArea seqTitle = pageSequence.getTitle();
         if (seqTitle != null) {
diff --git a/src/java/org/apache/fop/util/DOM2SAX.java b/src/java/org/apache/fop/util/DOM2SAX.java
index 839cf10..39d2af4 100644
--- a/src/java/org/apache/fop/util/DOM2SAX.java
+++ b/src/java/org/apache/fop/util/DOM2SAX.java
@@ -26,7 +26,6 @@
 import org.w3c.dom.Document;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
-
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.ext.LexicalHandler;
@@ -79,6 +78,15 @@
     }
 
     /**
+     * Writes the given fragment using the given ContentHandler.
+     * @param node DOM node
+     * @throws SAXException In case of a problem while writing XML
+     */
+    public void writeFragment(Node node) throws SAXException {
+        writeNode(node);
+    }
+
+    /**
      * Begin the scope of namespace prefix. Forward the event to the SAX handler
      * only if the prefix is unknown or it is mapped to a different URI.
      */
diff --git a/src/java/org/apache/fop/util/TransformerDefaultHandler.java b/src/java/org/apache/fop/util/TransformerDefaultHandler.java
new file mode 100644
index 0000000..cf07cc8
--- /dev/null
+++ b/src/java/org/apache/fop/util/TransformerDefaultHandler.java
@@ -0,0 +1,160 @@
+/*
+ * 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.util;
+
+import javax.xml.transform.sax.TransformerHandler;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.DefaultHandler2;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * A DefaultHandler implementation that delegates all the method calls to a
+ * {@link TransformerHandler} instance.
+ */
+public class TransformerDefaultHandler extends DefaultHandler2 {
+
+    private TransformerHandler transformerHandler;
+
+    /**
+     * Creates a new instance delegating to the given TransformerHandler object.
+     *
+     * @param transformerHandler the object to which all the method calls will
+     * be delegated
+     */
+    public TransformerDefaultHandler(TransformerHandler transformerHandler) {
+        this.transformerHandler = transformerHandler;
+    }
+
+    /**
+     * Returns the delegate TransformerHandler instance.
+     *
+     * @return the object to which all method calls are delegated
+     */
+    public TransformerHandler getTransformerHandler() {
+        return transformerHandler;
+    }
+
+    /** {@inheritDoc} */
+    public void setDocumentLocator(Locator locator) {
+        transformerHandler.setDocumentLocator(locator);
+    }
+
+    /** {@inheritDoc} */
+    public void startDocument() throws SAXException {
+        transformerHandler.startDocument();
+    }
+
+    /** {@inheritDoc} */
+    public void endDocument() throws SAXException {
+        transformerHandler.endDocument();
+    }
+
+    /** {@inheritDoc} */
+    public void startPrefixMapping(String prefix, String uri) throws SAXException {
+        transformerHandler.startPrefixMapping(prefix, uri);
+    }
+
+    /** {@inheritDoc} */
+    public void endPrefixMapping(String string) throws SAXException {
+        transformerHandler.endPrefixMapping(string);
+    }
+
+    /** {@inheritDoc} */
+    public void startElement(String uri, String localName, String qName, Attributes attrs)
+            throws SAXException {
+        AttributesImpl ai = new AttributesImpl(attrs);
+        transformerHandler.startElement(uri, localName, qName, ai);
+    }
+
+    /** {@inheritDoc} */
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+        transformerHandler.endElement(uri, localName, qName);
+    }
+
+    /** {@inheritDoc} */
+    public void characters(char[] ch, int start, int length) throws SAXException {
+        transformerHandler.characters(ch, start, length);
+    }
+
+    /** {@inheritDoc} */
+    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+        transformerHandler.ignorableWhitespace(ch, start, length);
+    }
+
+    /** {@inheritDoc} */
+    public void processingInstruction(String target, String data) throws SAXException {
+        transformerHandler.processingInstruction(target, data);
+    }
+
+    /** {@inheritDoc} */
+    public void skippedEntity(String name) throws SAXException {
+        transformerHandler.skippedEntity(name);
+    }
+
+    /** {@inheritDoc} */
+    public void notationDecl(String name, String publicId, String systemId) throws SAXException {
+        transformerHandler.notationDecl(name, publicId, systemId);
+    }
+
+    /** {@inheritDoc} */
+    public void unparsedEntityDecl(String name, String publicId, String systemId,
+            String notationName) throws SAXException {
+        transformerHandler.unparsedEntityDecl(name, publicId, systemId, notationName);
+    }
+
+    /** {@inheritDoc} */
+    public void startDTD(String name, String pid, String lid) throws SAXException {
+        transformerHandler.startDTD(name, pid, lid);
+    }
+
+    /** {@inheritDoc} */
+    public void endDTD() throws SAXException {
+        transformerHandler.endDTD();
+    }
+
+    /** {@inheritDoc} */
+    public void startEntity(String name) throws SAXException {
+        transformerHandler.startEntity(name);
+    }
+
+    /** {@inheritDoc} */
+    public void endEntity(String name) throws SAXException {
+        transformerHandler.endEntity(name);
+    }
+
+    /** {@inheritDoc} */
+    public void startCDATA() throws SAXException {
+        transformerHandler.startCDATA();
+    }
+
+    /** {@inheritDoc} */
+    public void endCDATA() throws SAXException {
+        transformerHandler.endCDATA();
+    }
+
+    /** {@inheritDoc} */
+    public void comment(char[] charArray, int start, int length) throws SAXException {
+        transformerHandler.comment(charArray, start, length);
+    }
+
+}
diff --git a/src/java/org/apache/fop/util/XMLUtil.java b/src/java/org/apache/fop/util/XMLUtil.java
index d8fe12c..f7c013a 100644
--- a/src/java/org/apache/fop/util/XMLUtil.java
+++ b/src/java/org/apache/fop/util/XMLUtil.java
@@ -21,6 +21,7 @@
 
 import java.awt.Rectangle;
 import java.awt.geom.Rectangle2D;
+import java.util.Locale;
 
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
@@ -170,4 +171,39 @@
         atts.addAttribute("", localName, localName, XMLUtil.CDATA, value);
     }
 
+    /**
+     * Converts a {@link Locale} instance to an RFC 3066 compliant language identifier.
+     * @param language the language
+     * @return the formatted language identifier
+     */
+    public static String toRFC3066(Locale language) {
+        if (language == null || language.getLanguage().length() == 0) {
+            return null;
+        }
+        StringBuffer sb = new StringBuffer();
+        sb.append(language.getLanguage());
+        if (language.getCountry().length() > 0) {
+            sb.append('-');
+            sb.append(language.getCountry());
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Converts an RFC 3066 compliant language identifier to a {@link Locale} instance.
+     * @param lang the language string
+     * @return the converted locale instance
+     */
+    public static Locale convertRFC3066ToLocale(String lang) {
+        if (lang == null || lang.length() == 0) {
+            return null;
+        }
+        String[] parts = lang.split("-");
+        if (parts.length == 1) {
+            return new Locale(parts[0]);
+        } else {
+            return new Locale(parts[0], parts[1]);
+        }
+    }
+
 }
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
index 32df744..aa2e325 100644
--- a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
+++ b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
@@ -324,8 +324,9 @@
     }
 
     /** {@inheritDoc} */
-    public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[] dx, String text)
-                throws IFException {
+
+    public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[] dx,
+            String text) throws IFException {
         try {
             establish(MODE_TEXT);
             AttributesImpl atts = new AttributesImpl();
diff --git a/status.xml b/status.xml
index 31afd44..2586753 100644
--- a/status.xml
+++ b/status.xml
@@ -58,6 +58,9 @@
       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="JM" type="add" fixes-bug="46705" due-to="Jost Klopfstein">
+        Added basic accessibility and Tagged PDF support. 
+      </action>
       <action context="Code" dev="JM" type="add">
         Added support for encoding CMYK bitmap images (IOCA FS45) and TIFF images as embedded objects.
       </action>
@@ -475,7 +478,7 @@
           <p>
             Besides the important changes listed below, the most important areas with
             improvements in this release are:
-          </p>
+         </p>
           <ul>
             <li>
               Many bugfixes in tables, plus some new features (full support for keeps and
diff --git a/test/accessibility/README b/test/accessibility/README
new file mode 100644
index 0000000..cbe2879
--- /dev/null
+++ b/test/accessibility/README
@@ -0,0 +1,52 @@
+his 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, 
+the text_1.fo file has been rendered into pdf/text_1_painter_orig.pdf and 
+pdf/text_1_renderer_orig.pdf. The configuration file config-painter.xconf (resp. 
+config-renderer.xconf) was used.
+
+The PDF files have been checked with Adobe Acrobat Professional 9, using both 
+the full accessibility checker and the read-aloud feature. The checker reports 
+no error /and/ the entire document can be read aloud.
+
+
+!! DO NOT MODIFY THOSE FILES, NEITHER THE FO NOR THE PDF !!
+
+
+... Or at least, know what you are doing
+If the FO files are modified, the resulting PDFs must be checked again, both 
+with the checker and the read-aloud feature. (Sometimes the checker reports no 
+problem yet part or all of the document cannot be read aloud.)
+
+The purpose of this infrastructure is to be able to quickly re-test the 
+accessibility processing chain when any change has been made to it. The 
+configuration files disable the compression of the PDF streams, so it is 
+possible to compare a re-generated PDF with the original one by using a simple 
+diff tool. The files will not be identical because of the different creation 
+dates (and the ID key in the trailer), but apart from that there should be no 
+difference.
+
+The rationale is that using a diff tool is much quicker and less tedious than 
+running Acrobat's accessibility checker and read-aloud feature every time.
+
+
+To re-generate the PDF files using the painter:
+    ../../fop -c config-painter.xconf text_1.fo pdf/text_1_painter.pdf
+    diff pdf/text_1_painter_orig.pdf pdf/text_1_painter.pdf
+Or, going through the intermediate format:
+    ../../fop -c config-painter.xconf text_1.fo -if application/pdf text_1_if.xml
+    ../../fop -c config-painter.xconf -ifin text_1_if.xml pdf/text_1_painter.pdf
+    diff pdf/text_1_painter_orig.pdf pdf/text_1_painter.pdf
+
+To re-generate the PDF files using the legacy renderer:
+    ../../fop -c config-renderer.xconf text_1.fo pdf/text_1_renderer.pdf
+    diff pdf/text_1_renderer_orig.pdf pdf/text_1_renderer.pdf
+Or, going through the intermediate format:
+    ../../fop -c config-renderer.xconf text_1.fo -at application/pdf text_1_at.xml
+    ../../fop -c config-renderer.xconf -atin text_1_at.xml pdf/text_1_renderer.pdf
+    diff pdf/text_1_renderer_orig.pdf pdf/text_1_renderer.pdf
+
+
+$Id$
diff --git a/test/accessibility/background-image_jpg_repeat.fo b/test/accessibility/background-image_jpg_repeat.fo
new file mode 100644
index 0000000..2c8f7f6
--- /dev/null
+++ b/test/accessibility/background-image_jpg_repeat.fo
@@ -0,0 +1,34 @@
+<?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 background-image="../resources/images/bgimg72dpi.jpg"/>
+    </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" text-align="justify">
+      <fo:block>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:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/background-image_jpg_single.fo b/test/accessibility/background-image_jpg_single.fo
new file mode 100644
index 0000000..2250c24
--- /dev/null
+++ b/test/accessibility/background-image_jpg_single.fo
@@ -0,0 +1,36 @@
+<?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 background-image="../resources/images/bgimg72dpi.jpg" 
+        background-repeat="no-repeat" background-position-horizontal="50%" 
+        background-position-vertical="50%"/>
+    </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" text-align="justify">
+      <fo:block>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:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/background-image_png_repeat.fo b/test/accessibility/background-image_png_repeat.fo
new file mode 100644
index 0000000..7506c0f
--- /dev/null
+++ b/test/accessibility/background-image_png_repeat.fo
@@ -0,0 +1,34 @@
+<?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 background-image="../resources/images/bgimg72dpi.png"/>
+    </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" text-align="justify">
+      <fo:block>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:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/background-image_png_single.fo b/test/accessibility/background-image_png_single.fo
new file mode 100644
index 0000000..1f60e94
--- /dev/null
+++ b/test/accessibility/background-image_png_single.fo
@@ -0,0 +1,36 @@
+<?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 background-image="../resources/images/fop-logo-color-24bit.png" 
+        background-repeat="no-repeat" background-position-horizontal="50%" 
+        background-position-vertical="50%"/>
+    </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" text-align="justify">
+      <fo:block>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:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/background-image_svg_repeat.fo b/test/accessibility/background-image_svg_repeat.fo
new file mode 100644
index 0000000..6e71409
--- /dev/null
+++ b/test/accessibility/background-image_svg_repeat.fo
@@ -0,0 +1,34 @@
+<?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 background-image="../resources/images/rgb-circles.svg"/>
+    </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" text-align="justify">
+      <fo:block>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:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/background-image_svg_single.fo b/test/accessibility/background-image_svg_single.fo
new file mode 100644
index 0000000..9dc67c8
--- /dev/null
+++ b/test/accessibility/background-image_svg_single.fo
@@ -0,0 +1,36 @@
+<?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 background-image="../resources/images/rgb-circles.svg" 
+        background-repeat="no-repeat" background-position-horizontal="50%" 
+        background-position-vertical="50%"/>
+    </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" text-align="justify">
+      <fo:block>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:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/complete.fo b/test/accessibility/complete.fo
new file mode 100644
index 0000000..03c57b2
--- /dev/null
+++ b/test/accessibility/complete.fo
@@ -0,0 +1,205 @@
+<?xml version="1.0" standalone="no"?>
+<!--
+  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"
+  xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+  <fo:layout-master-set>
+    <fo:simple-page-master master-name="page"
+      page-height="220pt" page-width="320pt" margin="10pt">
+      <fo:region-body column-count="2" margin-top="15pt"/>
+      <fo:region-before extent="12pt"/>
+    </fo:simple-page-master>
+  </fo:layout-master-set>
+  <fo:page-sequence master-reference="page" language="en" country="GB">
+    <fo:static-content flow-name="xsl-region-before">
+      <fo:block font-size="8pt" text-align-last="justify">This is the page header<fo:leader/>Page 
+        <fo:page-number/></fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="xsl-footnote-separator">
+      <fo:block><fo:leader leader-length="100pt" leader-pattern="rule"/></fo:block>
+    </fo:static-content>
+    <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 
+                  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: 
+        <fo:external-graphic src="../resources/images/fop-logo-color-24bit.png" 
+          inline-progression-dimension.maximum="100%" content-width="scale-to-fit"
+          fox:alt-text="FOP Logo"/></fo:block>
+      <fo:table space-before="10pt" space-after="10pt" width="100%" table-layout="fixed">
+        <fo:table-header>
+          <fo:table-row>
+            <fo:table-cell border="2pt solid black" padding="2pt 2pt 0">
+              <fo:block>Header 1.1</fo:block>
+            </fo:table-cell>
+            <fo:table-cell border="2pt solid black" padding="2pt 2pt 0">
+              <fo:block>Header 1.2</fo:block>
+            </fo:table-cell>
+          </fo:table-row>
+        </fo:table-header>
+        <fo:table-body>
+          <fo:table-row>
+            <fo:table-cell border="1pt solid black" padding="2pt 2pt 0">
+              <fo:block>Cell 1.1</fo:block>
+            </fo:table-cell>
+            <fo:table-cell border="1pt solid black" padding="2pt 2pt 0">
+              <fo:block>Cell 1.2</fo:block>
+            </fo:table-cell>
+          </fo:table-row>
+          <fo:table-row>
+            <fo:table-cell border="1pt solid black" padding="2pt 2pt 0">
+              <fo:block>Cell 2.1</fo:block>
+            </fo:table-cell>
+            <fo:table-cell border="1pt solid black" padding="2pt 2pt 0">
+              <fo:block>Cell 2.2</fo:block>
+            </fo:table-cell>
+          </fo:table-row>
+        </fo:table-body>
+      </fo:table>
+      <fo:block>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 span="all" border-top="1pt solid black" border-bottom="1pt solid black" 
+        padding-before="2pt" padding-after="2pt">This fo:block element spans all the columns of the 
+        document. This is intended to test the abilities of the text-to-speech program.</fo:block>
+      <fo:block>And now we are back to normal content flowing in two columns. Let’s start a numbered 
+        list:</fo:block>
+      <fo:list-block provisional-distance-between-starts="15pt" provisional-label-separation="0mm" 
+        keep-with-previous="auto">
+        <fo:list-item keep-with-previous="always">
+          <fo:list-item-label end-indent="label-end()">
+            <fo:block>1.</fo:block>
+          </fo:list-item-label>
+          <fo:list-item-body start-indent="body-start()">
+            <fo:block>
+              <fo:block>Line 1 of item 1</fo:block>
+              <fo:block>Line 2 of item 1</fo:block>
+              <fo:block>Line 3 of item 1</fo:block>
+            </fo:block>
+          </fo:list-item-body>
+        </fo:list-item>
+        <fo:list-item keep-with-previous="always">
+          <fo:list-item-label end-indent="label-end()">
+            <fo:block>2.</fo:block>
+          </fo:list-item-label>
+          <fo:list-item-body start-indent="body-start()">
+            <fo:block>
+              <fo:block>Line 1 of item 2</fo:block>
+              <fo:block>Line 2 of item 2</fo:block>
+              <fo:block>Line 3 of item 2</fo:block>
+            </fo:block>
+          </fo:list-item-body>
+        </fo:list-item>
+      </fo:list-block>
+      <fo:block>And now we are going to see how a second page sequence is handled.</fo:block>
+    </fo:flow>
+  </fo:page-sequence>
+  <fo:page-sequence master-reference="page" language="en" country="GB">
+    <fo:static-content flow-name="xsl-region-before">
+      <fo:block font-size="8pt" text-align-last="justify">This is the page header<fo:leader/>Page 
+        <fo:page-number/></fo:block>
+    </fo:static-content>
+    <fo:static-content flow-name="xsl-footnote-separator">
+      <fo:block><fo:leader leader-length="100pt" leader-pattern="rule"/></fo:block>
+    </fo:static-content>
+    <fo:flow flow-name="xsl-region-body" hyphenate="true" text-align="justify">
+      <fo:block id="second">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 
+                  external-destination="http://xmlgraphics.apache.org/fop/">FOP 
+                  website</fo:basic-link></fo:wrapper> for more 
+              information</fo:block></fo:footnote-body></fo:footnote>. It is a Java application that 
+        reads a formatting object (FO) tree and renders the resulting pages to a specified 
+        output.</fo:block>
+      <fo:table space-before="10pt" space-after="10pt" width="100%" table-layout="fixed">
+        <fo:table-header>
+          <fo:table-row>
+            <fo:table-cell border="2pt solid black" padding="2pt 2pt 0">
+              <fo:block>Header 1.1</fo:block>
+            </fo:table-cell>
+            <fo:table-cell border="2pt solid black" padding="2pt 2pt 0">
+              <fo:block>Header 1.2</fo:block>
+            </fo:table-cell>
+          </fo:table-row>
+        </fo:table-header>
+        <fo:table-body>
+          <fo:table-row>
+            <fo:table-cell border="1pt solid black" padding="2pt 2pt 0">
+              <fo:block>Cell 1.1</fo:block>
+            </fo:table-cell>
+            <fo:table-cell border="1pt solid black" padding="2pt 2pt 0">
+              <fo:block>Cell 1.2</fo:block>
+            </fo:table-cell>
+          </fo:table-row>
+          <fo:table-row>
+            <fo:table-cell border="1pt solid black" padding="2pt 2pt 0">
+              <fo:block>Cell 2.1</fo:block>
+            </fo:table-cell>
+            <fo:table-cell border="1pt solid black" padding="2pt 2pt 0">
+              <fo:block>Cell 2.2</fo:block>
+            </fo:table-cell>
+          </fo:table-row>
+        </fo:table-body>
+      </fo:table>
+      <fo:block language="fr" country="FR">Apache FOP (Formatting Objects Processor) est une 
+        application de mise en page de documents respectant le standard XSL-FO. À partir d’un 
+        document au format XSL-FO, cette application écrite en Java effectue une mise en page et 
+        renvoie un document prêt pour impression.</fo:block>
+      <fo:block span="all" border-top="1pt solid black" border-bottom="1pt solid black" 
+        padding-before="2pt" padding-after="2pt">This fo:block element spans all the columns of the 
+        document. This is intended to test the abilities of the text-to-speech program.</fo:block>
+      <fo:block>And now we are back to normal content flowing in two columns. Let’s start a numbered 
+        list:</fo:block>
+      <fo:list-block provisional-distance-between-starts="15pt" provisional-label-separation="0mm" 
+        keep-with-previous="auto">
+        <fo:list-item keep-with-previous="always">
+          <fo:list-item-label end-indent="label-end()">
+            <fo:block>1.</fo:block>
+          </fo:list-item-label>
+          <fo:list-item-body start-indent="body-start()">
+            <fo:block>
+              <fo:block>Line 1 of item 1</fo:block>
+              <fo:block>Line 2 of item 1</fo:block>
+              <fo:block>Line 3 of item 1</fo:block>
+            </fo:block>
+          </fo:list-item-body>
+        </fo:list-item>
+        <fo:list-item keep-with-previous="always">
+          <fo:list-item-label end-indent="label-end()">
+            <fo:block>2.</fo:block>
+          </fo:list-item-label>
+          <fo:list-item-body start-indent="body-start()">
+            <fo:block>
+              <fo:block>Line 1 of item 2</fo:block>
+              <fo:block>Line 2 of item 2</fo:block>
+              <fo:block>Line 3 of item 2</fo:block>
+            </fo:block>
+          </fo:list-item-body>
+        </fo:list-item>
+      </fo:list-block>
+      <fo:block>The end of the document has now been reached.</fo:block>
+    </fo:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/config-painter.xconf b/test/accessibility/config-painter.xconf
new file mode 100644
index 0000000..8c5dc2b
--- /dev/null
+++ b/test/accessibility/config-painter.xconf
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fop version="1.0">
+  <accessibility>true</accessibility>
+  <source-resolution>144</source-resolution>
+  <use-cache>false</use-cache>
+  <font-base>../resources/fonts/</font-base>
+  <renderers>
+    <renderer mime="application/pdf">
+      <filterList>
+        <value>null</value>
+      </filterList>
+      <filterList type="image">
+        <value>flate</value>
+        <value>ascii-85</value>
+      </filterList>
+      <fonts>
+        <font embed-url="DejaVuLGCSerif.ttf">
+          <font-triplet name="DejaVu" style="normal" weight="normal"/>
+        </font>
+      </fonts>
+    </renderer>
+  </renderers>
+</fop>
diff --git a/test/accessibility/config-renderer.xconf b/test/accessibility/config-renderer.xconf
new file mode 100644
index 0000000..4b55e39
--- /dev/null
+++ b/test/accessibility/config-renderer.xconf
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fop version="1.0">
+  <prefer-renderer>true</prefer-renderer>
+  <accessibility>true</accessibility>
+  <source-resolution>144</source-resolution>
+  <use-cache>false</use-cache>
+  <font-base>../resources/fonts/</font-base>
+  <renderers>
+    <renderer mime="application/pdf">
+      <filterList>
+        <value>null</value>
+      </filterList>
+      <filterList type="image">
+        <value>flate</value>
+        <value>ascii-85</value>
+      </filterList>
+      <fonts>
+        <font embed-url="DejaVuLGCSerif.ttf">
+          <font-triplet name="DejaVu" style="normal" weight="normal"/>
+        </font>
+      </fonts>
+    </renderer>
+  </renderers>
+</fop>
diff --git a/test/accessibility/image_jpg.fo b/test/accessibility/image_jpg.fo
new file mode 100644
index 0000000..be0e24b
--- /dev/null
+++ b/test/accessibility/image_jpg.fo
@@ -0,0 +1,35 @@
+<?xml version="1.0" standalone="no"?>
+<!--
+  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" 
+  xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+  <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" text-align="justify">
+      <fo:block>This document contains an image in the JPEG format: <fo:external-graphic 
+          src="../resources/images/cmyk.jpg"
+          inline-progression-dimension.maximum="100%" content-width="scale-to-fit"
+          fox:alt-text="CMYK colours"/>. Here is the end of the text.</fo:block>
+    </fo:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/image_png.fo b/test/accessibility/image_png.fo
new file mode 100644
index 0000000..3bcd12f
--- /dev/null
+++ b/test/accessibility/image_png.fo
@@ -0,0 +1,35 @@
+<?xml version="1.0" standalone="no"?>
+<!--
+  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" 
+  xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+  <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" text-align="justify">
+      <fo:block>This document contains an image in the PNG format: <fo:external-graphic 
+          src="../resources/images/fop-logo-color-24bit.png"
+          inline-progression-dimension.maximum="100%" content-width="scale-to-fit"
+          fox:alt-text="FOP Logo"/>. Here is the end of the text.</fo:block>
+    </fo:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/image_svg.fo b/test/accessibility/image_svg.fo
new file mode 100644
index 0000000..4fac16b
--- /dev/null
+++ b/test/accessibility/image_svg.fo
@@ -0,0 +1,45 @@
+<?xml version="1.0" standalone="no"?>
+<!--
+  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" 
+  xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+  <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" text-align="justify">
+      <fo:block>This document contains an image in the SVG format: <fo:external-graphic 
+          src="../resources/images/circles.svg"
+          inline-progression-dimension.maximum="75pt" content-width="scale-to-fit"
+          fox:alt-text="Nice circles"/>. And here is the same image as an instream-foreign-object: 
+        <fo:instream-foreign-object inline-progression-dimension.maximum="75pt" 
+          content-width="scale-down-to-fit" fox:alt-text="The same nice circles">
+<svg xmlns="http://www.w3.org/2000/svg" width="12cm" height="12cm">
+  <g style="fill-opacity:0.7; stroke:black; stroke-width:0.1cm;">
+    <circle cx="6cm" cy="2cm" r="100" style="fill:red;" transform="translate(0,50)" />
+    <circle cx="6cm" cy="2cm" r="100" style="fill:blue;" transform="translate(70,150)" />
+    <circle cx="6cm" cy="2cm" r="100" style="fill:green;" transform="translate(-70,150)"/>
+  </g>
+</svg>
+      </fo:instream-foreign-object>.</fo:block>
+    </fo:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/image_wmf.fo b/test/accessibility/image_wmf.fo
new file mode 100644
index 0000000..729b485
--- /dev/null
+++ b/test/accessibility/image_wmf.fo
@@ -0,0 +1,35 @@
+<?xml version="1.0" standalone="no"?>
+<!--
+  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" 
+  xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+  <fo:layout-master-set>
+    <fo:simple-page-master master-name="page"
+      page-height="320pt" 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" text-align="justify">
+      <fo:block>This document contains an image in the WMF format: <fo:external-graphic 
+          src="../resources/images/testChart.wmf"
+          inline-progression-dimension.maximum="100%" content-width="scale-to-fit"
+          fox:alt-text="Metafile Companion Test Chart"/> Here is the end of the text.</fo:block>
+    </fo:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/leader.fo b/test/accessibility/leader.fo
new file mode 100644
index 0000000..91c9243
--- /dev/null
+++ b/test/accessibility/leader.fo
@@ -0,0 +1,38 @@
+<?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" text-align="justify" 
+      text-align-last="justify">
+      <fo:block>This is a text followed by a leader with leader-pattern=​"use-content", the 
+        content being text:<fo:leader leader-pattern="use-content"><fo:inline> • 
+      </fo:inline></fo:leader>1</fo:block>
+      <fo:block space-before="10pt">This is a text followed by a leader with 
+        leader-pattern=​"use-content", the content being images:<fo:leader 
+          leader-pattern="use-content"><fo:external-graphic 
+            src="../resources/images/list-item.png"/></fo:leader>1</fo:block>
+    </fo:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/links.fo b/test/accessibility/links.fo
new file mode 100644
index 0000000..66f0f66
--- /dev/null
+++ b/test/accessibility/links.fo
@@ -0,0 +1,41 @@
+<?xml version="1.0" standalone="no"?>
+<!--
+  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" 
+  xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+  <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" text-align="justify">
+      <fo:block>This is a <fo:wrapper color="blue"><fo:basic-link 
+            internal-destination="FOP">link</fo:basic-link></fo:wrapper> to the next 
+        paragraph.</fo:block>
+      <fo:block id="FOP">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>For more information, see the <fo:wrapper color="blue"><fo:basic-link 
+            external-destination="http://xmlgraphics.apache.org/fop/">FOP 
+            website</fo:basic-link></fo:wrapper>.</fo:block>
+    </fo:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/pdf/background-image_jpg_repeat_painter_orig.pdf b/test/accessibility/pdf/background-image_jpg_repeat_painter_orig.pdf
new file mode 100644
index 0000000..a1734f8
--- /dev/null
+++ b/test/accessibility/pdf/background-image_jpg_repeat_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_jpg_repeat_renderer_orig.pdf b/test/accessibility/pdf/background-image_jpg_repeat_renderer_orig.pdf
new file mode 100644
index 0000000..52a812b
--- /dev/null
+++ b/test/accessibility/pdf/background-image_jpg_repeat_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_jpg_single_painter_orig.pdf b/test/accessibility/pdf/background-image_jpg_single_painter_orig.pdf
new file mode 100644
index 0000000..1368429
--- /dev/null
+++ b/test/accessibility/pdf/background-image_jpg_single_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_jpg_single_renderer_orig.pdf b/test/accessibility/pdf/background-image_jpg_single_renderer_orig.pdf
new file mode 100644
index 0000000..0816f48
--- /dev/null
+++ b/test/accessibility/pdf/background-image_jpg_single_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_png_repeat_painter_orig.pdf b/test/accessibility/pdf/background-image_png_repeat_painter_orig.pdf
new file mode 100644
index 0000000..f891c0a
--- /dev/null
+++ b/test/accessibility/pdf/background-image_png_repeat_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_png_repeat_renderer_orig.pdf b/test/accessibility/pdf/background-image_png_repeat_renderer_orig.pdf
new file mode 100644
index 0000000..30cdbcc
--- /dev/null
+++ b/test/accessibility/pdf/background-image_png_repeat_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_png_single_painter_orig.pdf b/test/accessibility/pdf/background-image_png_single_painter_orig.pdf
new file mode 100644
index 0000000..377247c
--- /dev/null
+++ b/test/accessibility/pdf/background-image_png_single_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_png_single_renderer_orig.pdf b/test/accessibility/pdf/background-image_png_single_renderer_orig.pdf
new file mode 100644
index 0000000..f05ad6f
--- /dev/null
+++ b/test/accessibility/pdf/background-image_png_single_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_svg_repeat_painter_orig.pdf b/test/accessibility/pdf/background-image_svg_repeat_painter_orig.pdf
new file mode 100644
index 0000000..434ac46
--- /dev/null
+++ b/test/accessibility/pdf/background-image_svg_repeat_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_svg_repeat_renderer_orig.pdf b/test/accessibility/pdf/background-image_svg_repeat_renderer_orig.pdf
new file mode 100644
index 0000000..9aa7a8e
--- /dev/null
+++ b/test/accessibility/pdf/background-image_svg_repeat_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_svg_single_painter_orig.pdf b/test/accessibility/pdf/background-image_svg_single_painter_orig.pdf
new file mode 100644
index 0000000..8c08b3f
--- /dev/null
+++ b/test/accessibility/pdf/background-image_svg_single_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/background-image_svg_single_renderer_orig.pdf b/test/accessibility/pdf/background-image_svg_single_renderer_orig.pdf
new file mode 100644
index 0000000..b6de5d1
--- /dev/null
+++ b/test/accessibility/pdf/background-image_svg_single_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/complete_painter_orig.pdf b/test/accessibility/pdf/complete_painter_orig.pdf
new file mode 100644
index 0000000..856e8ca
--- /dev/null
+++ b/test/accessibility/pdf/complete_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/complete_renderer_orig.pdf b/test/accessibility/pdf/complete_renderer_orig.pdf
new file mode 100644
index 0000000..bcca160
--- /dev/null
+++ b/test/accessibility/pdf/complete_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/image_jpg_painter_orig.pdf b/test/accessibility/pdf/image_jpg_painter_orig.pdf
new file mode 100644
index 0000000..6b54990
--- /dev/null
+++ b/test/accessibility/pdf/image_jpg_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/image_jpg_renderer_orig.pdf b/test/accessibility/pdf/image_jpg_renderer_orig.pdf
new file mode 100644
index 0000000..1621e80
--- /dev/null
+++ b/test/accessibility/pdf/image_jpg_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/image_png_painter_orig.pdf b/test/accessibility/pdf/image_png_painter_orig.pdf
new file mode 100644
index 0000000..2d8f01f
--- /dev/null
+++ b/test/accessibility/pdf/image_png_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/image_png_renderer_orig.pdf b/test/accessibility/pdf/image_png_renderer_orig.pdf
new file mode 100644
index 0000000..f60461f
--- /dev/null
+++ b/test/accessibility/pdf/image_png_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/image_svg_painter_orig.pdf b/test/accessibility/pdf/image_svg_painter_orig.pdf
new file mode 100644
index 0000000..f8cbaca
--- /dev/null
+++ b/test/accessibility/pdf/image_svg_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/image_svg_renderer_orig.pdf b/test/accessibility/pdf/image_svg_renderer_orig.pdf
new file mode 100644
index 0000000..e399c6b
--- /dev/null
+++ b/test/accessibility/pdf/image_svg_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/image_wmf_painter_orig.pdf b/test/accessibility/pdf/image_wmf_painter_orig.pdf
new file mode 100644
index 0000000..5bb1be4
--- /dev/null
+++ b/test/accessibility/pdf/image_wmf_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/image_wmf_renderer_orig.pdf b/test/accessibility/pdf/image_wmf_renderer_orig.pdf
new file mode 100644
index 0000000..8e1dc1e
--- /dev/null
+++ b/test/accessibility/pdf/image_wmf_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/leader_painter_orig.pdf b/test/accessibility/pdf/leader_painter_orig.pdf
new file mode 100644
index 0000000..42b74e6
--- /dev/null
+++ b/test/accessibility/pdf/leader_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/leader_renderer_orig.pdf b/test/accessibility/pdf/leader_renderer_orig.pdf
new file mode 100644
index 0000000..0cc5da9
--- /dev/null
+++ b/test/accessibility/pdf/leader_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/links_painter_orig.pdf b/test/accessibility/pdf/links_painter_orig.pdf
new file mode 100644
index 0000000..78198cf
--- /dev/null
+++ b/test/accessibility/pdf/links_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/links_renderer_orig.pdf b/test/accessibility/pdf/links_renderer_orig.pdf
new file mode 100644
index 0000000..3594a72
--- /dev/null
+++ b/test/accessibility/pdf/links_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/text_1_painter_orig.pdf b/test/accessibility/pdf/text_1_painter_orig.pdf
new file mode 100644
index 0000000..cf3c860
--- /dev/null
+++ b/test/accessibility/pdf/text_1_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/text_1_renderer_orig.pdf b/test/accessibility/pdf/text_1_renderer_orig.pdf
new file mode 100644
index 0000000..ee8dc11
--- /dev/null
+++ b/test/accessibility/pdf/text_1_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/text_2_painter_orig.pdf b/test/accessibility/pdf/text_2_painter_orig.pdf
new file mode 100644
index 0000000..41a0064
--- /dev/null
+++ b/test/accessibility/pdf/text_2_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/text_2_renderer_orig.pdf b/test/accessibility/pdf/text_2_renderer_orig.pdf
new file mode 100644
index 0000000..c75395f
--- /dev/null
+++ b/test/accessibility/pdf/text_2_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/text_font-embedding_painter_orig.pdf b/test/accessibility/pdf/text_font-embedding_painter_orig.pdf
new file mode 100644
index 0000000..31ce697
--- /dev/null
+++ b/test/accessibility/pdf/text_font-embedding_painter_orig.pdf
Binary files differ
diff --git a/test/accessibility/pdf/text_font-embedding_renderer_orig.pdf b/test/accessibility/pdf/text_font-embedding_renderer_orig.pdf
new file mode 100644
index 0000000..337648f
--- /dev/null
+++ b/test/accessibility/pdf/text_font-embedding_renderer_orig.pdf
Binary files differ
diff --git a/test/accessibility/text_1.fo b/test/accessibility/text_1.fo
new file mode 100644
index 0000000..2a90f6c
--- /dev/null
+++ b/test/accessibility/text_1.fo
@@ -0,0 +1,34 @@
+<?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" text-align="justify">
+      <fo:block>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:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/text_2.fo b/test/accessibility/text_2.fo
new file mode 100644
index 0000000..5eff059
--- /dev/null
+++ b/test/accessibility/text_2.fo
@@ -0,0 +1,41 @@
+<?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" text-align="justify">
+      <fo:block>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 language="fr" country="FR">Apache FOP (Formatting Objects Processor) est une 
+        application de mise en page de documents respectant le standard XSL-FO. À partir d’un 
+        document au format XSL-FO, cette application écrite en Java effectue une mise en page et 
+        renvoie un document prêt pour impression.</fo:block>
+      <fo:block>Back to English and let’s say it again: Apache FOP (Formatting Objects Processor) is 
+        a print formatter driven by XSL formatting objects (XSL-FO) and an output independent 
+        formatter.</fo:block>
+    </fo:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/accessibility/text_font-embedding.fo b/test/accessibility/text_font-embedding.fo
new file mode 100644
index 0000000..1d3abe0
--- /dev/null
+++ b/test/accessibility/text_font-embedding.fo
@@ -0,0 +1,34 @@
+<?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" text-align="justify" font-family="DejaVu">
+      <fo:block>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:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/java/org/apache/fop/render/pdf/PDFAConformanceTestCase.java b/test/java/org/apache/fop/render/pdf/PDFAConformanceTestCase.java
index 2fc62c6..6585a0c 100644
--- a/test/java/org/apache/fop/render/pdf/PDFAConformanceTestCase.java
+++ b/test/java/org/apache/fop/render/pdf/PDFAConformanceTestCase.java
@@ -99,7 +99,8 @@
         foFile = new File(foBaseDir, "with-cmyk-images.fo");
         try {
             convertFO(foFile, getUserAgent(), dumpPDF);
-            fail("Expected PDFConformanceException. PDF/A-1 does not allow PostScript XObjects.");
+            fail("Expected PDFConformanceException."
+                    + " PDF/A-1 does not allow mixing DeviceRGB and DeviceCMYK.");
         } catch (PDFConformanceException e) {
             //Good!
         }
diff --git a/test/java/org/apache/fop/util/XMLUtilTestCase.java b/test/java/org/apache/fop/util/XMLUtilTestCase.java
new file mode 100644
index 0000000..3e86b97
--- /dev/null
+++ b/test/java/org/apache/fop/util/XMLUtilTestCase.java
@@ -0,0 +1,45 @@
+/*
+ * 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.util;
+
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests {@link XMLUtil}.
+ */
+public class XMLUtilTestCase extends TestCase {
+
+    public void testLocaleToRFC3066() throws Exception {
+        assertNull(XMLUtil.toRFC3066(null));
+        assertEquals("en", XMLUtil.toRFC3066(new Locale("en")));
+        assertEquals("en-US", XMLUtil.toRFC3066(new Locale("en", "US")));
+        assertEquals("en-US", XMLUtil.toRFC3066(new Locale("EN", "us")));
+    }
+
+    public void testRFC3066ToLocale() throws Exception {
+        assertNull(XMLUtil.convertRFC3066ToLocale(null));
+        assertNull(XMLUtil.convertRFC3066ToLocale(""));
+        assertEquals(new Locale("en"), XMLUtil.convertRFC3066ToLocale("en"));
+        assertEquals(new Locale("en", "US"), XMLUtil.convertRFC3066ToLocale("en-US"));
+        assertEquals(new Locale("en", "US"), XMLUtil.convertRFC3066ToLocale("EN-us"));
+    }
+}
diff --git a/test/layoutengine/standard-testcases/foreign-attributes.xml b/test/layoutengine/standard-testcases/foreign-attributes.xml
index 0928607..3bf71f7 100644
--- a/test/layoutengine/standard-testcases/foreign-attributes.xml
+++ b/test/layoutengine/standard-testcases/foreign-attributes.xml
@@ -31,9 +31,6 @@
       </fo:layout-master-set>
       <fo:page-sequence master-reference="normal">
         <fo:flow flow-name="xsl-region-body">
-          <fo:block id="eg">
-            <fo:external-graphic src="../../resources/images/bgimg300dpi.jpg" fox:alt="description"/>
-          </fo:block>
           <fo:block id="bl">
             <fo:basic-link external-destination="url(http://xmlgraphics.apache.org/fop/)" fox:blah="fop">FOP</fo:basic-link>
           </fo:block>
@@ -43,7 +40,6 @@
   </fo>
   <checks xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
     <eval expected="bar" xpath="//pageViewport[1]/@fox:foo"/>
-    <eval expected="description" xpath="//block[@prod-id='eg']//image/@fox:alt"/>
     <!--eval expected="fop" xpath="//block[@prod-id='bl']/inlineparent/@fox:blah"/> NYI -->
   </checks>
 </testcase>
diff --git a/test/layoutengine/standard-testcases/page-sequence_language.xml b/test/layoutengine/standard-testcases/page-sequence_language.xml
index 93238e2..f2fa610 100644
--- a/test/layoutengine/standard-testcases/page-sequence_language.xml
+++ b/test/layoutengine/standard-testcases/page-sequence_language.xml
@@ -65,4 +65,10 @@
     <eval expected="de" xpath="/areaTree/pageSequence[4]/@language"/>
     <true xpath="not(boolean(/areaTree/pageSequence[4]/@country))"/>
   </checks>
+  <if-checks xmlns:if="http://xmlgraphics.apache.org/fop/intermediate">
+    <eval expected="en" xpath="//if:page-sequence[1]/@xml:lang"/>
+    <eval expected="en-US" xpath="//if:page-sequence[2]/@xml:lang"/>
+    <eval expected="de-CH" xpath="//if:page-sequence[3]/@xml:lang"/>
+    <eval expected="de" xpath="//if:page-sequence[4]/@xml:lang"/>
+  </if-checks>
 </testcase>
diff --git a/test/resources/images/list-item.png b/test/resources/images/list-item.png
new file mode 100644
index 0000000..77ceb3f
--- /dev/null
+++ b/test/resources/images/list-item.png
Binary files differ
diff --git a/test/resources/images/rgb-circles.svg b/test/resources/images/rgb-circles.svg
new file mode 100644
index 0000000..8370008
--- /dev/null
+++ b/test/resources/images/rgb-circles.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="319" height="286.6">
+  <g style="fill-opacity:0.7; stroke:black; stroke-width:3"
+    transform="translate(0, 286.6) scale(1, -1) translate(100, 100)">
+    <circle cx="50"  cy="86.6" r="80" style="fill:red;"/>
+    <circle cx="0"   cy="0"    r="80" style="fill:green;"/>
+    <circle cx="100" cy="0"    r="80" style="fill:blue;"/>
+  </g>
+</svg>