aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJeremias Maerki <jeremias@apache.org>2009-02-19 18:04:18 +0000
committerJeremias Maerki <jeremias@apache.org>2009-02-19 18:04:18 +0000
commit223eb5df1f7597ac5269eb8dce45b6a7450144b8 (patch)
treeb6475383dd7fca2a53402b19d22b682276d8d6d1 /src
parent51c210eea6bf627053854359388c1ad14203c0aa (diff)
downloadxmlgraphics-fop-223eb5df1f7597ac5269eb8dce45b6a7450144b8.tar.gz
xmlgraphics-fop-223eb5df1f7597ac5269eb8dce45b6a7450144b8.zip
Bugzilla #46705:
Accessibility and Tagged PDF Support Submitted by: Jost Klopfstein <jost.klopfstein.at.gmail.com> Changes to patch by jeremias: - Some style fixes - Various simplifications - Removal of dead code - Addressed some issues raised in Bugzilla (work in progress) - Fixed a couple of bugs on leader handling detected while testing (an NPE remains in leader_leader-pattern_use-content.xml) git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_Accessibility@745949 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r--src/java/META-INF/services/org.apache.fop.fo.ElementMapping19
-rw-r--r--src/java/org/apache/fop/accessibility/AccessibilityUtil.java89
-rw-r--r--src/java/org/apache/fop/accessibility/TransformerNode.java321
-rw-r--r--src/java/org/apache/fop/accessibility/TransformerNodeEndProcessing.java115
-rw-r--r--src/java/org/apache/fop/accessibility/addPtr.xsl30
-rw-r--r--src/java/org/apache/fop/accessibility/reduceFOTree.xsl86
-rw-r--r--src/java/org/apache/fop/apps/FOUserAgent.java36
-rw-r--r--src/java/org/apache/fop/apps/Fop.java7
-rw-r--r--src/java/org/apache/fop/apps/FopFactory.java14
-rw-r--r--src/java/org/apache/fop/apps/FopFactoryConfigurator.java9
-rw-r--r--src/java/org/apache/fop/area/Trait.java6
-rw-r--r--src/java/org/apache/fop/cli/CommandLineOptions.java3
-rw-r--r--src/java/org/apache/fop/fo/Constants.java10
-rw-r--r--src/java/org/apache/fop/fo/FONode.java3
-rw-r--r--src/java/org/apache/fop/fo/FOPropertyMapping.java12
-rw-r--r--src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java2
-rw-r--r--src/java/org/apache/fop/fo/extensions/ExternalDocument.java2
-rw-r--r--src/java/org/apache/fop/fo/extensions/InternalElementMapping.java85
-rw-r--r--src/java/org/apache/fop/fo/flow/AbstractGraphics.java7
-rw-r--r--src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java7
-rw-r--r--src/java/org/apache/fop/fo/flow/BasicLink.java6
-rw-r--r--src/java/org/apache/fop/fo/flow/Block.java7
-rw-r--r--src/java/org/apache/fop/fo/flow/Character.java7
-rw-r--r--src/java/org/apache/fop/fo/flow/Inline.java7
-rw-r--r--src/java/org/apache/fop/fo/flow/PageNumber.java7
-rw-r--r--src/java/org/apache/fop/fo/flow/table/TableFObj.java7
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java1
-rw-r--r--src/java/org/apache/fop/layoutmgr/TraitSetter.java11
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java1
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java1
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java1
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java1
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java2
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java20
-rw-r--r--src/java/org/apache/fop/pdf/PDFArray.java9
-rw-r--r--src/java/org/apache/fop/pdf/PDFFactory.java42
-rw-r--r--src/java/org/apache/fop/pdf/PDFLink.java12
-rw-r--r--src/java/org/apache/fop/pdf/PDFPage.java17
-rw-r--r--src/java/org/apache/fop/pdf/PDFParentTree.java50
-rw-r--r--src/java/org/apache/fop/pdf/PDFRoot.java30
-rw-r--r--src/java/org/apache/fop/pdf/PDFStructElem.java196
-rw-r--r--src/java/org/apache/fop/pdf/PDFStructTreeRoot.java67
-rw-r--r--src/java/org/apache/fop/pdf/PDFTextUtil.java52
-rw-r--r--src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java6
-rw-r--r--src/java/org/apache/fop/render/AbstractRenderer.java12
-rw-r--r--src/java/org/apache/fop/render/afp/AFPPainter.java6
-rw-r--r--src/java/org/apache/fop/render/afp/AFPRenderer.java4
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFConstants.java2
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFPainter.java9
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFParser.java8
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFRenderer.java36
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFSerializer.java98
-rw-r--r--src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java19
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DPainter.java6
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DRenderer.java2
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLPainter.java9
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java2
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFContentGenerator.java94
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java395
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java19
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java12
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java8
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java15
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFPainter.java132
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderer.java5
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderingContext.java36
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java21
-rw-r--r--src/java/org/apache/fop/render/ps/PSPainter.java6
-rw-r--r--src/java/org/apache/fop/render/ps/PSRenderer.java4
-rw-r--r--src/java/org/apache/fop/render/txt/TXTRenderer.java2
-rw-r--r--src/java/org/apache/fop/util/DOM2SAX.java9
-rw-r--r--src/sandbox/org/apache/fop/render/svg/SVGPainter.java10
72 files changed, 2292 insertions, 112 deletions
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 0194b19db..09c79f5b7 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/AccessibilityUtil.java b/src/java/org/apache/fop/accessibility/AccessibilityUtil.java
new file mode 100644
index 000000000..0dc5840b6
--- /dev/null
+++ b/src/java/org/apache/fop/accessibility/AccessibilityUtil.java
@@ -0,0 +1,89 @@
+/*
+ * 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.TransformerConfigurationException;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.stream.StreamSource;
+
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.FOUserAgent;
+
+/**
+ * Utility class for FOP's accessibility features. It provides the stylesheets used for processing
+ * the incoming XSL-FO stream and for setting up the transformation.
+ */
+public class AccessibilityUtil {
+
+ private static SAXTransformerFactory tfactory
+ = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+
+ private static Templates addPtrTemplates;
+ private static Templates reduceFOTemplates;
+
+ public static DefaultHandler decorateDefaultHandler(DefaultHandler handler,
+ FOUserAgent userAgent) throws FOPException {
+ DefaultHandler transformNode = new TransformerNodeEndProcessing(
+ getAddPtrTemplates(), handler, userAgent);
+ return transformNode;
+ }
+
+ /**
+ * Returns the addPtr.xsl stylesheet.
+ * @return the addPtr.xsl stylesheet
+ * @throws FOPException if transform fails
+ */
+ public static synchronized Templates getAddPtrTemplates() throws FOPException {
+ if (addPtrTemplates == null) {
+ //Load and cache stylesheet
+ Source src = new StreamSource(
+ AccessibilityUtil.class.getResource("addPtr.xsl").toExternalForm());
+ try {
+ addPtrTemplates = tfactory.newTemplates(src);
+ } catch (TransformerConfigurationException e) {
+ throw new FOPException(e);
+ }
+ }
+ return addPtrTemplates;
+ }
+
+ /**
+ * Returns the reduceFOTree.xsl stylesheet
+ * @return the reduceFOTree.xsl stylesheet
+ * @throws FOPException if an error occurs loading the stylesheet
+ */
+ public static synchronized Templates getReduceFOTreeTemplates() throws FOPException {
+ if (reduceFOTemplates == null) {
+ //Load and cache stylesheet
+ Source src = new StreamSource(
+ AccessibilityUtil.class.getResource("reduceFOTree.xsl").toExternalForm());
+ try {
+ reduceFOTemplates = tfactory.newTemplates(src);
+ } catch (TransformerConfigurationException e) {
+ throw new FOPException(e);
+ }
+ }
+ return reduceFOTemplates;
+ }
+ } \ No newline at end of file
diff --git a/src/java/org/apache/fop/accessibility/TransformerNode.java b/src/java/org/apache/fop/accessibility/TransformerNode.java
new file mode 100644
index 000000000..115c13703
--- /dev/null
+++ b/src/java/org/apache/fop/accessibility/TransformerNode.java
@@ -0,0 +1,321 @@
+/*
+ * 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.File;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamSource;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.apache.fop.apps.FOPException;
+
+/**
+ * Used for accessibility to run required xslt transforms
+ */
+class TransformerNode extends DefaultHandler {
+
+ private TransformerHandler transformerHandler;
+
+ /**
+ * happens after setParams have been broadcast.
+ *
+ * @param downstreamHandler
+ * the handler passed in
+ * @param xsltFile
+ * for transform
+ * @throws FOPException
+ * for general errors
+ */
+ public TransformerNode(DefaultHandler downstreamHandler, File xsltFile) throws FOPException {
+ try {
+ TransformerFactory transFact = TransformerFactory.newInstance();
+ SAXTransformerFactory saxTFactory = ((SAXTransformerFactory)transFact);
+ StreamSource ss = new StreamSource(xsltFile);
+ transformerHandler = saxTFactory.newTransformerHandler(ss);
+ SAXResult saxResult = new SAXResult();
+ saxResult.setHandler(downstreamHandler);
+ transformerHandler.setResult(saxResult);
+ } catch (TransformerConfigurationException t) {
+ throw new FOPException(t);
+ }
+ }
+
+ /**
+ *
+ * @param result
+ * of transform
+ * @param xsltFile
+ * for transform
+ * @throws FOPException
+ * for general errors
+ */
+ public TransformerNode(Result result, File xsltFile) throws FOPException {
+ try {
+ TransformerFactory transFact = TransformerFactory.newInstance();
+ SAXTransformerFactory saxTFactory = ((SAXTransformerFactory)transFact);
+ StreamSource ss = new StreamSource(xsltFile);
+ transformerHandler = saxTFactory.newTransformerHandler(ss);
+ transformerHandler.setResult(result);
+ } catch (TransformerConfigurationException t) {
+ throw new FOPException(t);
+ }
+ }
+
+ /**
+ * This is part of a two phase construction. Call this, then call
+ * initResult.
+ *
+ * @param xsltFile
+ * for transform
+ * @throws FOPException
+ * for general errors
+ */
+ public TransformerNode(Source xsltFile) throws FOPException {
+ try {
+ TransformerFactory transFact = TransformerFactory.newInstance();
+ SAXTransformerFactory saxTFactory = ((SAXTransformerFactory)transFact);
+ transformerHandler = saxTFactory.newTransformerHandler(xsltFile);
+ } catch (TransformerConfigurationException t) {
+ throw new FOPException(t);
+ }
+ }
+
+ /**
+ * This is part of a two phase construction. Call this, then call
+ * initResult.
+ *
+ * @param xsltTemplates
+ * for transform
+ * @throws FOPException
+ * for general errors
+ */
+ public TransformerNode(Templates xsltTemplates) throws FOPException {
+ try {
+ TransformerFactory transFact = TransformerFactory.newInstance();
+ SAXTransformerFactory saxTFactory = ((SAXTransformerFactory)transFact);
+ transformerHandler = saxTFactory.newTransformerHandler(xsltTemplates);
+ } catch (TransformerConfigurationException t) {
+ throw new FOPException(t);
+ }
+ }
+
+ /**
+ * Call this after calling constructor for xsltFile only above.
+ *
+ * @param result
+ * of transform
+ */
+ public void initResult(Result result) {
+ transformerHandler.setResult(result);
+ }
+
+ /******************** start of ContentHandler ***************************/
+ /** {@inheritDoc} */
+ public void setDocumentLocator(Locator locator) {
+ if (transformerHandler != null) {
+ transformerHandler.setDocumentLocator(locator);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startDocument() throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.startDocument();
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endDocument() throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.endDocument();
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void processingInstruction(String target, String data) throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.processingInstruction(target, data);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startElement(String uri, String local, String raw, Attributes attrs)
+ throws SAXException {
+ AttributesImpl ai = new AttributesImpl(attrs);
+ if (transformerHandler != null) {
+ transformerHandler.startElement(uri, local, raw, ai);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.characters(ch, start, length);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.ignorableWhitespace(ch, start, length);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endElement(String uri, String local, String raw) throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.endElement(uri, local, raw);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void skippedEntity(String string) throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.skippedEntity(string);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startPrefixMapping(String string, String string1) throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.startPrefixMapping(string, string1);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endPrefixMapping(String string) throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.endPrefixMapping(string);
+ }
+ }
+
+ /***************************** LexicalHandlerImpl **************************/
+ /**
+ * @param name
+ * - param1
+ * @param pid
+ * - param2
+ * @param lid
+ * - param3
+ * @throws SAXException
+ * - if parser fails
+ */
+ public void startDTD(String name, String pid, String lid) throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.startDTD(name, pid, lid);
+ }
+ }
+
+ /**
+ * End of DTD
+ *
+ * @throws SAXException
+ * - if parser fails
+ */
+ public void endDTD() throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.endDTD();
+ }
+ }
+
+ /**
+ * startEnitity.
+ *
+ * @param string
+ * - param 1
+ * @throws SAXException
+ * - if parser fails
+ */
+ public void startEntity(String string) throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.startEntity(string);
+ }
+ }
+
+ /**
+ * end Entity
+ *
+ * @param string
+ * - param 1
+ * @throws SAXException
+ * - if paser fails
+ */
+ public void endEntity(String string) throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.endEntity(string);
+ }
+ }
+
+ /**
+ * Start of CDATA section
+ *
+ * @throws SAXException
+ * - parser fails
+ */
+ public void startCDATA() throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.startCDATA();
+ }
+ }
+
+ /**
+ * endCDATA section
+ *
+ * @throws SAXException
+ * - if paser fails
+ */
+ public void endCDATA() throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.endCDATA();
+ }
+ }
+
+ /**
+ *
+ * @param charArray
+ * - the characters
+ * @param int1
+ * - param 2
+ * @param int2
+ * - param 3
+ * @throws SAXException
+ * - if paser fails
+ */
+ public void comment(char[] charArray, int int1, int int2) throws SAXException {
+ if (transformerHandler != null) {
+ transformerHandler.comment(charArray, int1, int2);
+ }
+ }
+
+ /******************** End of Lexical Handler ***********************/
+}
diff --git a/src/java/org/apache/fop/accessibility/TransformerNodeEndProcessing.java b/src/java/org/apache/fop/accessibility/TransformerNodeEndProcessing.java
new file mode 100644
index 000000000..8e02c67cd
--- /dev/null
+++ b/src/java/org/apache/fop/accessibility/TransformerNodeEndProcessing.java
@@ -0,0 +1,115 @@
+/*
+ * 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.ByteArrayOutputStream;
+import java.io.InputStream;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.FOUserAgent;
+
+/**
+ * An extension of TransformerNode used to run 2nd transform after completion of first
+ */
+class TransformerNodeEndProcessing extends TransformerNode {
+
+ private final ByteArrayOutputStream enrichedFOBuffer = new ByteArrayOutputStream();
+ private DefaultHandler delegateHandler = null;
+ private final FOUserAgent userAgent;
+
+ /**
+ * Do a transform, but perform special processing at the end for the access
+ * stuff.
+ *
+ * @param xsltTemplates Transform to do.
+ * @param fopHandler Used in the end processing
+ * @param userAgent the userAgent
+ * @throws FOPException
+ * if transform fails
+ */
+ public TransformerNodeEndProcessing(Templates xsltTemplates, DefaultHandler fopHandler,
+ FOUserAgent userAgent) throws FOPException {
+ super(xsltTemplates);
+ delegateHandler = fopHandler;
+ this.userAgent = userAgent;
+ Result res1 = new StreamResult(enrichedFOBuffer);
+ super.initResult(res1);
+ }
+
+ /**
+ * Do a transform, but perform special processing at the end for the access
+ * stuff.
+ *
+ * @param xsltFile Transform to do.
+ * @param fopHandler Used in the end processing
+ * @param userAgent the userAgent
+ * @throws FOPException if transform fails
+ */
+
+ public TransformerNodeEndProcessing(Source xsltFile, DefaultHandler fopHandler,
+ FOUserAgent userAgent) throws FOPException {
+ super(xsltFile);
+ delegateHandler = fopHandler;
+ this.userAgent = userAgent;
+ Result res1 = new StreamResult(enrichedFOBuffer);
+ super.initResult(res1);
+ }
+
+ /** {@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();
+ Transformer transformer = AccessibilityUtil.getReduceFOTreeTemplates().newTransformer();
+ Source src = new StreamSource(new ByteArrayInputStream(enrichedFO));
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ Result res = new StreamResult(out);
+ transformer.transform(src, res);
+ userAgent.setReducedFOTree(out.toByteArray());
+
+ SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+ saxParserFactory.setNamespaceAware(true);
+ saxParserFactory.setValidating(false);
+ SAXParser saxParser = saxParserFactory.newSAXParser();
+ InputStream in = new ByteArrayInputStream(enrichedFO);
+ saxParser.parse(in, delegateHandler);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ throw new SAXException(e);
+ }
+
+ }
+
+}
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 000000000..f619817cd
--- /dev/null
+++ b/src/java/org/apache/fop/accessibility/addPtr.xsl
@@ -0,0 +1,30 @@
+<?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 match="fo:block|fo:block-container"><xsl:call-template name="addPtr"/></xsl:template>
+<xsl:template match="fo:list-block|fo:list-item|fo:list-item-label|fo:list-item-body"><xsl:call-template name="addPtr"/></xsl:template>
+<xsl:template match="fo:table|fo:table-body|fo:table-header|fo:table-footer|fo:table-row|fo:table-cell"><xsl:call-template name="addPtr"/></xsl:template>
+<xsl:template match="fo:inline|fo:wrapper|fo:basic-link|fo:character"><xsl:call-template name="addPtr"/></xsl:template>
+<xsl:template match="fo:instream-foreign-object|fo:external-graphic"><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>
+<xsl:template match="fo:footnote|fo:footnote-body"><xsl:call-template name="addPtr"/></xsl:template>
+<xsl:template match="fo:marker"><xsl:call-template name="addPtr"/></xsl:template>
+<xsl:template name="addPtr"><xsl:element name="{name()}" namespace="{namespace-uri()}"><xsl:apply-templates select="@*"/><xsl:attribute name="foi:ptr"><xsl:value-of select="generate-id()"/></xsl:attribute><xsl:apply-templates/></xsl:element></xsl:template>
+<xsl:template match="@*|node()"><xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy></xsl:template>
+</xsl:stylesheet> \ No newline at end of file
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 000000000..eb808286b
--- /dev/null
+++ b/src/java/org/apache/fop/accessibility/reduceFOTree.xsl
@@ -0,0 +1,86 @@
+<?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 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:svg="http://www.w3.org/2000/svg"
+ xmlns:foi="http://xmlgraphics.apache.org/fop/internal"
+ version="1.0">
+ <xsl:output method="xml" indent="no"/>
+
+ <xsl:template match="*">
+ <xsl:element name="{name()}">
+ <xsl:copy-of select="@foi:ptr"/>
+ <xsl:apply-templates/>
+ </xsl:element>
+ </xsl:template>
+
+ <xsl:template match="@master-reference|@flow-name"></xsl:template>
+
+ <xsl:template match="fo:block">
+ <xsl:element name="{name()}">
+ <xsl:copy-of select="@foi:ptr"/>
+ <xsl:apply-templates/>
+ </xsl:element>
+ </xsl:template>
+
+ <xsl:template match="fo:inline|fo:wrapper|fo:page-number|fo:page-number-citation|fo:page-number-citation-last">
+ <xsl:element name="{name()}">
+ <xsl:copy-of select="@foi:ptr"/>
+ <xsl:apply-templates/>
+ </xsl:element>
+ </xsl:template>
+
+ <xsl:template match="fo:table-cell|fo:table|fo:table-body|fo:table-footer|fo:table-row|fo:table-header">
+ <xsl:element name="{name()}">
+ <xsl:copy-of select="@foi:ptr"/>
+ <xsl:apply-templates/>
+ </xsl:element>
+ </xsl:template>
+
+ <xsl:template match="fo:list-block|fo:list-item|fo:list-item-label|fo:list-item-body">
+ <xsl:element name="{name()}">
+ <xsl:copy-of select="@foi:ptr"/>
+ <xsl:apply-templates/>
+ </xsl:element>
+ </xsl:template>
+
+ <xsl:template match="fo:basic-link|fo:block-container|fo:character|fo:instream-foreign-object|fo:marker">
+ <xsl:element name="{name()}">
+ <xsl:copy-of select="@foi:ptr"/>
+ <xsl:apply-templates/>
+ </xsl:element>
+ </xsl:template>
+
+ <xsl:template match="fo:external-graphic|fo:instream-foreign-object">
+ <xsl:element name="{name()}">
+ <xsl:copy-of select="@fox:alt-text|@foi:ptr"/>
+ <xsl:apply-templates/>
+ </xsl:element>
+ </xsl:template>
+
+
+
+ <xsl:template match="text()"></xsl:template>
+
+ <xsl:template match="fo:layout-master-set|comment() | processing-instruction() | fo:simple-page-master | fo:table-column | fo:leader | fo:retrieve-marker "/>
+
+ <xsl:template match="svg:svg | svg | fo:inline-container | fo:float | fo:bidi-override"/>
+
+</xsl:stylesheet>
diff --git a/src/java/org/apache/fop/apps/FOUserAgent.java b/src/java/org/apache/fop/apps/FOUserAgent.java
index ebd2e0594..1e0e3fb86 100644
--- a/src/java/org/apache/fop/apps/FOUserAgent.java
+++ b/src/java/org/apache/fop/apps/FOUserAgent.java
@@ -99,6 +99,9 @@ public class FOUserAgent {
private boolean locatorEnabled = true; // true by default (for error messages).
private EventBroadcaster eventBroadcaster = new FOPEventBroadcaster();
+ //TODO Verify that a byte array is the best solution here
+ private byte[] reducedFOTree; // accessibility: reduced FO
+
/** Producer: Metadata element for the system/software that produces
* the document. (Some renderers can store this in the document.)
*/
@@ -153,6 +156,9 @@ public class FOUserAgent {
setBaseURL(factory.getBaseURL());
setFontBaseURL(factory.getFontManager().getFontBaseURL());
setTargetResolution(factory.getTargetResolution());
+ if (this.getRendererOptions().get("accessibility") == null) {
+ this.rendererOptions.put("accessibility", Boolean.FALSE);
+ }
}
/** @return the associated FopFactory instance */
@@ -197,6 +203,7 @@ public class FOUserAgent {
return rendererOverride;
}
+
/**
* Sets an explicit FOEventHandler instance which overrides the one
* defined by the render type setting.
@@ -615,5 +622,34 @@ public class FOUserAgent {
}
+ /**
+ * check if accessibility is enabled
+ * @return boolean
+ */
+ public boolean accessibilityEnabled() {
+ Boolean enabled = (Boolean)this.getRendererOptions().get("accessibility");
+ if (enabled != null) {
+ return enabled.booleanValue();
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Used for accessibility. Stores the reduced FO tree (the result from the second transform)
+ * for later use.
+ * @param reducedFOTree the result from 2nd transform
+ */
+ public void setReducedFOTree(byte[] reducedFOTree) {
+ this.reducedFOTree = reducedFOTree;
+ }
+
+ /**
+ * Used for accessibility. Returns the reduced FO tree.
+ * @return result from 2nd transform as byte array
+ */
+ public byte[] getReducedFOTree() {
+ return this.reducedFOTree;
+ }
}
diff --git a/src/java/org/apache/fop/apps/Fop.java b/src/java/org/apache/fop/apps/Fop.java
index 0527ea290..e5927fdba 100644
--- a/src/java/org/apache/fop/apps/Fop.java
+++ b/src/java/org/apache/fop/apps/Fop.java
@@ -24,6 +24,7 @@ import java.io.OutputStream;
import org.xml.sax.helpers.DefaultHandler;
+import org.apache.fop.accessibility.AccessibilityUtil;
import org.apache.fop.fo.FOTreeBuilder;
/**
@@ -110,7 +111,11 @@ public class Fop {
if (foTreeBuilder == null) {
createDefaultHandler();
}
- return this.foTreeBuilder;
+ if (this.foUserAgent.accessibilityEnabled()) {
+ return AccessibilityUtil.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 96c22f964..cf22a1e56 100644
--- a/src/java/org/apache/fop/apps/FopFactory.java
+++ b/src/java/org/apache/fop/apps/FopFactory.java
@@ -99,6 +99,11 @@ public class FopFactory implements ImageContext {
* external-graphics.
*/
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;
@@ -181,10 +186,19 @@ public class FopFactory implements ImageContext {
*/
public FOUserAgent newFOUserAgent() {
FOUserAgent userAgent = new FOUserAgent(this);
+ userAgent.getRendererOptions().put("accessibility", Boolean.valueOf(this.accessibility));
return userAgent;
}
/**
+ * Used for accessibility to pass value to newFOUserAgent
+ * @param value set through xconf file
+ */
+ void setAccessibility(boolean value) {
+ this.accessibility = value;
+ }
+
+ /**
* 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 e71173845..e6f1aff16 100644
--- a/src/java/org/apache/fop/apps/FopFactoryConfigurator.java
+++ b/src/java/org/apache/fop/apps/FopFactoryConfigurator.java
@@ -90,6 +90,15 @@ public class FopFactoryConfigurator {
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/Trait.java b/src/java/org/apache/fop/area/Trait.java
index 45c0432f4..27ce39252 100644
--- a/src/java/org/apache/fop/area/Trait.java
+++ b/src/java/org/apache/fop/area/Trait.java
@@ -194,9 +194,12 @@ public class Trait implements Serializable {
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 @@ public class Trait implements Serializable {
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 fef2a1f46..734cd9b98 100644
--- a/src/java/org/apache/fop/cli/CommandLineOptions.java
+++ b/src/java/org/apache/fop/cli/CommandLineOptions.java
@@ -327,6 +327,8 @@ public class CommandLineOptions {
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", Boolean.TRUE);
} else if (args[i].equals("-v")) {
printVersion();
return false;
@@ -1129,6 +1131,7 @@ public class CommandLineOptions {
+ " -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"
+ " [INPUT] \n"
diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java
index a1ccb20e7..8bcaa1538 100644
--- a/src/java/org/apache/fop/fo/Constants.java
+++ b/src/java/org/apache/fop/fo/Constants.java
@@ -771,8 +771,16 @@ public interface Constants {
* 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 07d8f472c..8c4e3416d 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.FOPException;
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;
@@ -419,6 +420,8 @@ public abstract class FONode implements Cloneable {
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 b4a864e43..d214dee99 100644
--- a/src/java/org/apache/fop/fo/FOPropertyMapping.java
+++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java
@@ -2515,6 +2515,18 @@ public final class FOPropertyMapping implements Constants {
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, 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/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
index 927bed0f7..ee2b3886e 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 @@ public class ExtensionElementMapping extends ElementMapping {
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");
}
/**
diff --git a/src/java/org/apache/fop/fo/extensions/ExternalDocument.java b/src/java/org/apache/fop/fo/extensions/ExternalDocument.java
index bea6f6f61..233714bd7 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.pagination.AbstractPageSequence;
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 000000000..c6802b5b3
--- /dev/null
+++ b/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java
@@ -0,0 +1,85 @@
+/*
+ * 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;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.UnknownXMLObj;
+import org.apache.fop.fo.extensions.destination.Destination;
+
+/**
+ * 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 propertyAttributes = new java.util.HashSet();
+
+ static {
+ //These are FOP's extension properties for accessibility
+ propertyAttributes.add("ptr");
+ }
+
+ /**
+ * Constructor.
+ */
+ public InternalElementMapping() {
+ namespaceURI = URI;
+ }
+
+ /**
+ * Initialize the data structures.
+ */
+ protected void initialize() {
+ if (foObjs == null) {
+ foObjs = new HashMap();
+ }
+ }
+
+ /* static class DestinationMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new Destination(parent);
+ }
+ } */
+
+
+ /**
+ * used internally for accessibility
+ */
+ 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 propertyAttributes.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 d72682282..2ed576a32 100644
--- a/src/java/org/apache/fop/fo/flow/AbstractGraphics.java
+++ b/src/java/org/apache/fop/fo/flow/AbstractGraphics.java
@@ -60,6 +60,7 @@ public abstract class AbstractGraphics extends FObj implements GraphicsPropertie
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 +95,7 @@ public abstract class AbstractGraphics extends FObj implements GraphicsPropertie
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();
@@ -207,6 +209,11 @@ public abstract class AbstractGraphics extends FObj implements GraphicsPropertie
return keepWithPrevious;
}
+ /** @return the "foi:ptr" property. */
+ 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 5f420efb8..3554c8e0e 100644
--- a/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java
+++ b/src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java
@@ -51,6 +51,7 @@ public abstract class AbstractPageNumberCitation extends FObj {
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 +97,7 @@ public abstract class AbstractPageNumberCitation extends FObj {
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 +140,11 @@ public abstract class AbstractPageNumberCitation extends FObj {
return textDecoration;
}
+ /** @return the "foi:ptr" property. */
+ 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 ee3171188..2fe0c9a8d 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 @@ public class BasicLink extends Inline {
/** {@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);
@@ -158,6 +159,11 @@ public class BasicLink extends Inline {
return this.showDestination;
}
+ /** @return the "foi:ptr" property. */
+ public String getPtr() {
+ return super.getPtr();
+ }
+
/** {@inheritDoc} */
public String getLocalName() {
return "basic-link";
diff --git a/src/java/org/apache/fop/fo/flow/Block.java b/src/java/org/apache/fop/fo/flow/Block.java
index daaebd6d0..efcd18b71 100644
--- a/src/java/org/apache/fop/fo/flow/Block.java
+++ b/src/java/org/apache/fop/fo/flow/Block.java
@@ -71,6 +71,7 @@ public class Block extends FObjMixed implements BreakPropertySet {
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 +123,7 @@ public class Block extends FObjMixed implements BreakPropertySet {
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 +173,11 @@ public class Block extends FObjMixed implements BreakPropertySet {
return breakAfter;
}
+ /** @return the "foi:ptr" property. */
+ 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 f76b3225d..636b2b1c7 100644
--- a/src/java/org/apache/fop/fo/flow/Character.java
+++ b/src/java/org/apache/fop/fo/flow/Character.java
@@ -62,6 +62,7 @@ public class Character extends FObj {
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 +109,7 @@ public class Character extends FObj {
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} */
@@ -207,6 +209,11 @@ public class Character extends FObj {
return keepWithPrevious;
}
+ /** @return the "foi:ptr" property. */
+ 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 dae7d306b..ea5b49baf 100644
--- a/src/java/org/apache/fop/fo/flow/Inline.java
+++ b/src/java/org/apache/fop/fo/flow/Inline.java
@@ -37,6 +37,7 @@ public class Inline extends 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 +67,7 @@ public class Inline extends InlineLevel {
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} */
@@ -144,6 +146,11 @@ public class Inline extends InlineLevel {
return dominantBaseline;
}
+ /** @return the "foi:ptr" property. */
+ 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 dc834d708..bb251f44f 100644
--- a/src/java/org/apache/fop/fo/flow/PageNumber.java
+++ b/src/java/org/apache/fop/fo/flow/PageNumber.java
@@ -47,6 +47,7 @@ public class PageNumber extends FObj {
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 +93,7 @@ public class PageNumber extends FObj {
// 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
@@ -165,6 +167,11 @@ public class PageNumber extends FObj {
return lineHeight;
}
+ /** @return the "foi:ptr" property. */
+ 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 ec508580c..96b3769a3 100644
--- a/src/java/org/apache/fop/fo/flow/table/TableFObj.java
+++ b/src/java/org/apache/fop/fo/flow/table/TableFObj.java
@@ -46,6 +46,7 @@ public abstract class TableFObj extends FObj {
private Numeric borderBeforePrecedence;
private Numeric borderEndPrecedence;
private Numeric borderStartPrecedence;
+ private String ptr;
ConditionalBorder borderBefore;
ConditionalBorder borderAfter;
@@ -71,6 +72,7 @@ public abstract class TableFObj extends FObj {
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 +237,11 @@ public abstract class TableFObj extends FObj {
}
}
+ /** @return the "foi:ptr" property. */
+ 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/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
index c641c3e69..acfcbe3f0 100644
--- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
@@ -385,6 +385,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
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 9cab6322b..d062ba2ca 100644
--- a/src/java/org/apache/fop/layoutmgr/TraitSetter.java
+++ b/src/java/org/apache/fop/layoutmgr/TraitSetter.java
@@ -584,6 +584,17 @@ public class TraitSetter {
}
/**
+ * 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 00c628a40..fd4d803af 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 @@ public abstract class AbstractGraphicsLayoutManager extends LeafNodeLayoutManage
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 b65978453..a74fac743 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 @@ public abstract class AbstractPageNumberCitationLayoutManager extends LeafNodeLa
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 c369df82b..e1f7475e9 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java
@@ -56,6 +56,7 @@ public class BasicLinkLayoutManager extends InlineLayoutManager {
private void setupBasicLinkArea(InlineArea area) {
BasicLink fobj = (BasicLink) this.fobj;
// internal destinations take precedence:
+ area.addTrait(Trait.PTR, 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 383ca0105..dcd993487 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 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
}
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 dc8a020ae..5cae07207 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 @@ public class PageNumberLayoutManager extends LeafNodeLayoutManager {
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 27ed38b53..5c24dbfcb 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java
@@ -25,10 +25,12 @@ import java.util.ListIterator;
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.fonts.Font;
import org.apache.fop.fonts.FontSelector;
import org.apache.fop.layoutmgr.InlineKnuthSequence;
@@ -504,12 +506,28 @@ public class TextLayoutManager extends LeafNodeLayoutManager {
}
TraitSetter.addFontTraits(textArea, font);
textArea.addTrait(Trait.COLOR, this.foText.getColor());
-
+ textArea.addTrait(Trait.PTR, 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 org.apache.fop.fo.flow.Block) {
+ return (((org.apache.fop.fo.flow.Block) fobj).getPtr());
+ } else if (fobj instanceof org.apache.fop.fo.flow.Inline) {
+ return (((org.apache.fop.fo.flow.Inline) fobj).getPtr());
+ } else {
+ log.warn("Accessibility: TLM.getPtr-no Ptr found");
+ return "";
+ }
+ }
+
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/PDFArray.java b/src/java/org/apache/fop/pdf/PDFArray.java
index 7c5f8ba9b..a7dfc388e 100644
--- a/src/java/org/apache/fop/pdf/PDFArray.java
+++ b/src/java/org/apache/fop/pdf/PDFArray.java
@@ -107,6 +107,15 @@ public class PDFArray extends PDFObject {
}
/**
+ * 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/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java
index 0a4516ce6..087ae4277 100644
--- a/src/java/org/apache/fop/pdf/PDFFactory.java
+++ b/src/java/org/apache/fop/pdf/PDFFactory.java
@@ -177,18 +177,26 @@ public class PDFFactory {
* @param pageWidth width of the page in points
* @param pageHeight height of the page in points
* @param pageIndex index of the page (zero-based)
+ * @param currentPageParentKey the integer key in the structural parent tree
*
* @return the created /Page object
*/
public PDFPage makePage(PDFResources resources,
- int pageWidth, int pageHeight, int pageIndex) {
-
+ int pageWidth, int pageHeight, int pageIndex,
+ int currentPageParentKey) {
/*
* create a PDFPage with the next object number, the given
* resources, contents and dimensions
*/
- PDFPage page = new PDFPage(resources,
+ PDFPage page = new PDFPage(resources, // old numPages
pageWidth, pageHeight, pageIndex);
+ if (currentPageParentKey > -1) {
+ //Accessibility is enabled
+ page.setStructParents(currentPageParentKey);
+ //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.
+ page.setTabs(new PDFName("S"));
+ }
getDocument().assignObjectNumber(page);
getDocument().getPages().addPage(page);
@@ -203,6 +211,23 @@ public class PDFFactory {
* @param resources resources object to use
* @param pageWidth width of the page in points
* @param pageHeight height of the page in points
+ * @param pageIndex index of the page (zero-based)
+ *
+ * @return the created /Page object
+ */
+ public PDFPage makePage(PDFResources resources,
+ int pageWidth, int pageHeight, int pageIndex) {
+ return makePage(resources, pageWidth, pageHeight, pageIndex, -1);
+ }
+
+ /**
+ * Make a /Page object. The page is assigned an object number immediately
+ * so references can already be made. The page must be added to the
+ * PDFDocument later using addObject().
+ *
+ * @param resources resources object to use
+ * @param pageWidth width of the page in points
+ * @param pageHeight height of the page in points
*
* @return the created /Page object
*/
@@ -868,6 +893,17 @@ public class PDFFactory {
}
/**
+ * Creates and returns a StructTreeRoot object. Used for accessibility.
+ * @return structure Tree Root element
+ */
+ public PDFStructTreeRoot makeStructTreeRoot() {
+ PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot();
+ getDocument().assignObjectNumber(structTreeRoot);
+ getDocument().addTrailerObject(structTreeRoot);
+ return structTreeRoot;
+ }
+
+ /**
* 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 a7c4c6548..620e5d51d 100644
--- a/src/java/org/apache/fop/pdf/PDFLink.java
+++ b/src/java/org/apache/fop/pdf/PDFLink.java
@@ -42,6 +42,7 @@ public class PDFLink extends PDFObject {
private float bry;
private String color;
private PDFAction action;
+ private Integer structParent;
/**
* create objects associated with a link annotation (GoToR)
@@ -68,6 +69,15 @@ public class PDFLink extends PDFObject {
this.action = action;
}
+
+ /**
+ * Used for accessibility
+ * @param mcid of this structParent
+ */
+ public void setStructParent(int mcid) {
+ this.structParent = new Integer(mcid);
+ }
+
/**
* {@inheritDoc}
*/
@@ -87,6 +97,8 @@ public class PDFLink extends PDFObject {
+ (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/PDFPage.java b/src/java/org/apache/fop/pdf/PDFPage.java
index d1472e154..6cc8c3e57 100644
--- a/src/java/org/apache/fop/pdf/PDFPage.java
+++ b/src/java/org/apache/fop/pdf/PDFPage.java
@@ -146,4 +146,21 @@ public class PDFPage extends PDFResourceContext {
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);
+ }
+
+ /**
+ * 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 000000000..7528aa299
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFParentTree.java
@@ -0,0 +1,50 @@
+/*
+ * 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 {
+
+ /**
+ * Create the /ParentTree NumberTreeNode
+ */
+ public PDFParentTree() {
+ super();
+ }
+
+ /**
+ * Get the parentTree.
+ * @return parentTree as PDFNumsArray
+ */
+ 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/PDFRoot.java b/src/java/org/apache/fop/pdf/PDFRoot.java
index 1ea316390..c8d94585c 100644
--- a/src/java/org/apache/fop/pdf/PDFRoot.java
+++ b/src/java/org/apache/fop/pdf/PDFRoot.java
@@ -63,7 +63,7 @@ public class PDFRoot extends PDFDictionary {
*/
public PDFRoot(int objnum, PDFPages pages) {
super();
- setObjectNumber(objnum);
+ setObjectNumber(objnum);
put("Type", new PDFName("Catalog"));
setRootPages(pages);
}
@@ -252,4 +252,32 @@ public class PDFRoot extends PDFDictionary {
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()
+ }
+
}
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 000000000..408b1f2c7
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFStructElem.java
@@ -0,0 +1,196 @@
+/*
+ * 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 Structure Element.
+ */
+public class PDFStructElem extends PDFDictionary {
+
+ private PDFObject parentObject = null;
+ private String source = "";
+ private boolean level1 = false;
+
+ /**
+ * Create the /StructTreeRoot dictionary
+ * @param fo passed in fo object
+ * @param parent Parent of this PDFStructElem
+ */
+ public PDFStructElem(String fo, PDFObject parent) {
+ super();
+ if (parent instanceof PDFStructElem) {
+ parentObject = (PDFStructElem) parent;
+ }
+ put("Type", new PDFName("StructElem"));
+ source = fo;
+ //TODO Move this into the render/pdf package. The PDF library shall not contain FO knowledge
+ if ("block".equals(fo)) {
+ put("S", new PDFName("P"));
+ } else if ("inline".equals(fo) || "wrapper".equals(fo) || "character".equals(fo)) {
+ put("S", new PDFName("Span"));
+ } else if ("table-cell".equals(fo)) {
+ PDFStructElem grandParent = (PDFStructElem)
+ ((PDFStructElem)parent).getParentStructElem();
+ String s = grandParent.getSource();
+ if ("table-header".equals(s)) {
+ put("S", new PDFName("TH"));
+ } else {
+ put("S", new PDFName("TD"));
+ }
+ } else if ("table-row".equals(fo)) {
+ put("S", new PDFName("TR"));
+ } else if ("root".equals(fo)) {
+ put("S", new PDFName("Document"));
+ } else if ("page-sequence".equals(fo)) {
+ put("S", new PDFName("Part"));
+ } else if ("flow".equals(fo) || "static-content".equals(fo)) {
+ put("S", new PDFName("Sect"));
+ } else if ("page-number".equals(fo) || "page-number-citation".equals(fo)
+ || "page-number-citation-last".equals(fo)) {
+ put("S", new PDFName("Quote"));
+ } else if ("external-graphic".equals(fo) || "instream-foreign-object".equals(fo)) {
+ put("S", new PDFName("Figure"));
+ } else if ("table".equals(fo)) {
+ put("S", new PDFName("Table"));
+ } else if ("table-body".equals(fo)) {
+ put("S", new PDFName("TBody"));
+ } else if ("table-header".equals(fo)) {
+ put("S", new PDFName("THead"));
+ } else if ("table-footer".equals(fo)) {
+ put("S", new PDFName("TFoot"));
+ } else if ("list-block".equals(fo)) {
+ put("S", new PDFName("L"));
+ } else if ("list-item".equals(fo)) {
+ put("S", new PDFName("LI"));
+ } else if ("list-item-label".equals(fo)) {
+ put("S", new PDFName("Lbl"));
+ } else if ("list-item-body".equals(fo)) {
+ put("S", new PDFName("LBody"));
+ } else if ("block-container".equals(fo)) {
+ put("S", new PDFName("Div"));
+ } else if ("basic-link".equals(fo)) {
+ put("S", new PDFName("Link"));
+ } else if ("footnote".equals(fo)) {
+ put("S", new PDFName("Note"));
+ } else if ("footnote-body".equals(fo)) {
+ put("S", new PDFName("Sect"));
+ } else if ("marker".equals(fo)) {
+ put("S", new PDFName("Private"));
+ } else {
+ log.error("Accessibility: PDFStructElem constructor is missing: " + fo);
+ }
+ setParent(parent);
+ if (!"external-graphic".equals(fo) && !"instream-foreign-object".equals(fo)) {
+ put("K", new PDFArray());
+ }
+ }
+
+ /**
+ * This method is called for PDFStructElements which are direct children of
+ * fo:static-content or fo:flow-section
+ */
+ public void setLevel1() {
+ this.level1 = true;
+ }
+
+ /**
+ *
+ * @return true if the PDFStructElement is a direct child of
+ * fo:static-content or fo:flow-section
+ */
+ public boolean getLevel1() {
+ return this.level1;
+ }
+
+ /**
+ * Get the parent
+ * @return PDFStructElem of parent
+ */
+ public PDFObject getParentStructElem() {
+ return (PDFStructElem)this.parentObject;
+ }
+
+ /**
+ * Set the parent for this StructElem
+ * @param parent to be added
+ */
+ public void setParent(PDFObject parent) {
+ if (parent != null) {
+ put("P", new PDFReference(parent));
+ }
+ }
+
+ /**
+ * Get the source of this StructElem
+ * @return the source
+ */
+ public String getSource() {
+ return source;
+ }
+
+ /**
+ * The kids of this StructElem
+ * @return the kids
+ */
+ public PDFArray getKids() {
+ return (PDFArray)get("K");
+ }
+
+ /**
+ * Add a kid to this strucElem
+ * @param kid to be added
+ */
+ public void addKid(PDFObject kid) {
+ getKids().add(kid);
+ }
+
+ /**
+ * Add a kid, but only if it does not already exist
+ * @param kid to be added
+ * @return true if kid did not already exist
+ */
+ public boolean addUniqueKid(PDFObject kid) {
+ PDFArray mArray = getKids();
+ if (!mArray.contains(kid)) {
+ getKids().add(kid);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Add kid referenced through mcid integer
+ * used fo:external-graphic
+ * @param mcid of this kid
+ */
+ public void addMCIDKid(int mcid) {
+ put("K", mcid);
+ }
+
+ /**
+ * Add a page reference to this structElem
+ * @param pageObject to be added
+ */
+ public void addPage(Object pageObject) {
+ put("Pg", (PDFObject) pageObject);
+ }
+
+}
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 000000000..e530b1582
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java
@@ -0,0 +1,67 @@
+/*
+ * 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 {
+
+ /**
+ * Create the /StructTreeRoot dictionary.
+ */
+ public PDFStructTreeRoot() {
+ super();
+ put("Type", new PDFName("StructTreeRoot"));
+ put("K", new PDFArray());
+ }
+
+ /**
+ * Add parentTree entry.
+ * @param parentTree to be added
+ */
+ public void addParentTree(PDFParentTree parentTree) {
+ put("ParentTree", parentTree);
+ }
+
+ /**
+ * Get the kids.
+ * @return the kids
+ */
+ public PDFArray getKids() {
+ return (PDFArray)get("K");
+ }
+
+ /**
+ * Returns the first child of the kids array (normally the structure tree root element)
+ * @return the first child
+ */
+ public PDFObject getFirstChild() {
+ return (PDFObject)getKids().get(0);
+ }
+
+ /**
+ * Adds a kid.
+ * @param kid to be added
+ */
+ public void addKid(PDFObject kid) {
+ getKids().add(kid);
+ }
+} \ No newline at end of file
diff --git a/src/java/org/apache/fop/pdf/PDFTextUtil.java b/src/java/org/apache/fop/pdf/PDFTextUtil.java
index 6640f9b80..bb8816995 100644
--- a/src/java/org/apache/fop/pdf/PDFTextUtil.java
+++ b/src/java/org/apache/fop/pdf/PDFTextUtil.java
@@ -48,6 +48,7 @@ public abstract class PDFTextUtil {
public static final int TR_CLIP = 7;
private boolean inTextObject = false;
+ private boolean artifactMode = false;
private String startText;
private String endText;
private boolean useMultiByte;
@@ -116,6 +117,15 @@ public abstract class PDFTextUtil {
}
/**
+ * Indicates whether we are in a text object and if that text object represents
+ * an artifact.
+ * @return true if in artifact-mode text object
+ */
+ public boolean inArtifactMode() {
+ return this.artifactMode;
+ }
+
+ /**
* Called when a new text object should be started. Be sure to call setFont() before
* issuing any text painting commands.
*/
@@ -128,11 +138,51 @@ public abstract class PDFTextUtil {
}
/**
+ * Begin of a regular text object, used for accessibility
+ * @param mcid of text object
+ * @param structElemType of parent
+ */
+ public void beginTextObjectAccess(int mcid, String structElemType) {
+ if (inTextObject) {
+ throw new IllegalStateException("Already in text object");
+ }
+ write(structElemType + " <</MCID "
+ + String.valueOf(mcid) + ">>\nBDC\nBT\n");
+ this.inTextObject = true;
+ }
+
+ /**
+ * Begin of a text object marked as artifact (fo:leader in XSL-FO) text object.
+ * Used for accessibility.
+ */
+ public void beginArtifactTextObject() {
+ if (inTextObject) {
+ throw new IllegalStateException("Already in text object");
+ }
+ write("/Artifact\nBMC\nBT\n");
+ this.inTextObject = true;
+ this.artifactMode = true;
+ }
+
+ /**
* Called when a text object should be ended.
*/
public void endTextObject() {
+ endTextObject(false);
+ }
+
+ /**
+ * Called when a text object should be ended.
+ * @param accessEnabled indicating if accessibility is turned on or not
+ */
+ public void endTextObject(boolean accessEnabled) {
checkInTextObject();
- write("ET\n");
+ if (accessEnabled) {
+ write("ET\nEMC\n");
+ } else {
+ write("ET\n");
+ }
+ this.artifactMode = false;
this.inTextObject = false;
initValues();
}
diff --git a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
index 54f6170db..8da4b3973 100644
--- a/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
+++ b/src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java
@@ -834,8 +834,10 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
* @param url the URI/URL of the image
* @param pos the position of the image
* @param foreignAttributes an optional Map with foreign attributes, may be null
+ * @param ptr used for accessibility
*/
- protected abstract void drawImage(String url, Rectangle2D pos, Map foreignAttributes);
+ protected abstract void drawImage(String url, Rectangle2D pos, Map foreignAttributes,
+ String ptr);
/**
* Draw an image at the indicated location.
@@ -843,7 +845,7 @@ public abstract class AbstractPathOrientedRenderer extends PrintRenderer {
* @param pos the position of the image
*/
protected final void drawImage(String url, Rectangle2D pos) {
- drawImage(url, pos, null);
+ drawImage(url, pos, null, "");
}
/**
diff --git a/src/java/org/apache/fop/render/AbstractRenderer.java b/src/java/org/apache/fop/render/AbstractRenderer.java
index 943c8c9da..d7df7596d 100644
--- a/src/java/org/apache/fop/render/AbstractRenderer.java
+++ b/src/java/org/apache/fop/render/AbstractRenderer.java
@@ -737,11 +737,13 @@ public abstract class AbstractRenderer
currentBPPosition += viewport.getOffset();
Rectangle2D contpos = viewport.getContentPosition();
if (content instanceof Image) {
- renderImage((Image) content, contpos);
+ String ptr = (String) viewport.getTrait(Trait.PTR);
+ renderImage((Image) content, contpos, ptr);
} else if (content instanceof Container) {
renderContainer((Container) content);
} else if (content instanceof ForeignObject) {
- renderForeignObject((ForeignObject) content, contpos);
+ String ptr = (String) viewport.getTrait(Trait.PTR);
+ renderForeignObject((ForeignObject) content, contpos, ptr);
} else if (content instanceof InlineBlockParent) {
renderInlineBlockParent((InlineBlockParent) content);
}
@@ -754,9 +756,10 @@ public abstract class AbstractRenderer
*
* @param image The image
* @param pos The target position of the image
+ * @param ptr used for accessibility
* (todo) Make renderImage() protected
*/
- public void renderImage(Image image, Rectangle2D pos) {
+ public void renderImage(Image image, Rectangle2D pos, String ptr) {
// Default: do nothing.
// Some renderers (ex. Text) don't support images.
}
@@ -780,9 +783,10 @@ public abstract class AbstractRenderer
*
* @param fo The foreign object area
* @param pos The target position of the foreign object
+ * @param ptr used for accessibility
* (todo) Make renderForeignObject() protected
*/
- protected void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
+ protected void renderForeignObject(ForeignObject fo, Rectangle2D pos, String ptr) {
// Default: do nothing.
// Some renderers (ex. Text) don't support foreign objects.
}
diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java
index 0c2501b87..1816ff515 100644
--- a/src/java/org/apache/fop/render/afp/AFPPainter.java
+++ b/src/java/org/apache/fop/render/afp/AFPPainter.java
@@ -183,7 +183,7 @@ public class AFPPainter extends AbstractIFPainter {
}
/** {@inheritDoc} */
- public void drawImage(String uri, Rectangle rect) throws IFException {
+ public void drawImage(String uri, Rectangle rect, String ptr) throws IFException {
String name = documentHandler.getPageSegmentNameFor(uri);
if (name != null) {
float[] srcPts = {rect.x, rect.y};
@@ -195,7 +195,7 @@ public class AFPPainter extends AbstractIFPainter {
}
/** {@inheritDoc} */
- public void drawImage(Document doc, Rectangle rect) throws IFException {
+ public void drawImage(Document doc, Rectangle rect, String ptr) throws IFException {
drawImageUsingDocument(doc, rect);
}
@@ -312,7 +312,7 @@ public class AFPPainter extends AbstractIFPainter {
/** {@inheritDoc} */
public void drawText(int x, int y,
final int letterSpacing, final int wordSpacing, final int[] dx,
- final String text) throws IFException {
+ final String text, final String ptr) throws IFException {
final int fontSize = this.state.getFontSize();
getPaintingState().setFontSize(fontSize);
diff --git a/src/java/org/apache/fop/render/afp/AFPRenderer.java b/src/java/org/apache/fop/render/afp/AFPRenderer.java
index d1d2bec3b..1967ae607 100644
--- a/src/java/org/apache/fop/render/afp/AFPRenderer.java
+++ b/src/java/org/apache/fop/render/afp/AFPRenderer.java
@@ -375,7 +375,7 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust
ImageFlavor.GRAPHICS2D, ImageFlavor.BUFFERED_IMAGE, ImageFlavor.RENDERED_IMAGE };
/** {@inheritDoc} */
- public void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) {
+ public void drawImage(String uri, Rectangle2D pos, Map foreignAttributes, String ptr) {
uri = URISpecification.getURL(uri);
paintingState.setImageUri(uri);
@@ -506,7 +506,7 @@ public class AFPRenderer extends AbstractPathOrientedRenderer implements AFPCust
/** {@inheritDoc} */
public void renderImage(Image image, Rectangle2D pos) {
- drawImage(image.getURL(), pos, image.getForeignAttributes());
+ drawImage(image.getURL(), pos, image.getForeignAttributes(),"");
}
/** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/render/intermediate/IFConstants.java b/src/java/org/apache/fop/render/intermediate/IFConstants.java
index e7f7e1a00..4c9b9fc8d 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 @@ public interface IFConstants extends XMLConstants {
String EL_BORDER_RECT = "border-rect";
String EL_FONT = "font";
String EL_TEXT = "text";
+ /** used for accessibility */
+ String EL_STRUCTURE_TREE = "structure-tree";
}
diff --git a/src/java/org/apache/fop/render/intermediate/IFPainter.java b/src/java/org/apache/fop/render/intermediate/IFPainter.java
index 6369b0251..163fe16a1 100644
--- a/src/java/org/apache/fop/render/intermediate/IFPainter.java
+++ b/src/java/org/apache/fop/render/intermediate/IFPainter.java
@@ -153,10 +153,11 @@ public interface IFPainter {
* @param wordSpacing additional spacing between words (may be 0)
* @param dx an array of adjustment values for each character in X-direction (may be null)
* @param text the text
+ * @param ptr used for accessibility
* @throws IFException if an error occurs while handling this event
*/
void drawText(int x, int y, int letterSpacing, int wordSpacing,
- int[] dx, String text) throws IFException;
+ int[] dx, String text, String ptr) throws IFException;
/**
* Restricts the current clipping region with the given rectangle.
@@ -205,18 +206,20 @@ public interface IFPainter {
* an fo:external-graphic in XSL-FO.
* @param uri the image's URI
* @param rect the rectangle in which the image shall be painted
+ * @param ptr used for accessibility
* @throws IFException if an error occurs while handling this event
*/
- void drawImage(String uri, Rectangle rect) throws IFException;
+ void drawImage(String uri, Rectangle rect, String ptr) throws IFException;
/**
* Draws an image (represented by a DOM document) inside a given rectangle. This is the
* equivalent to an fo:instream-foreign-object in XSL-FO.
* @param doc the DOM document containing the foreign object
* @param rect the rectangle in which the image shall be painted
+ * @param ptr used for accessibility
* @throws IFException if an error occurs while handling this event
*/
- void drawImage(Document doc, Rectangle rect) throws IFException;
+ void drawImage(Document doc, Rectangle rect, String ptr) throws IFException;
//Note: For now, all foreign objects are handled as DOM documents. At the moment, all known
//implementations use a DOM anyway, so optimizing this to work with SAX wouldn't result in
//any performance benefits. The IFRenderer itself has a DOM anyway. Only the IFParser could
diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java
index 92e71d105..47e24445c 100644
--- a/src/java/org/apache/fop/render/intermediate/IFParser.java
+++ b/src/java/org/apache/fop/render/intermediate/IFParser.java
@@ -481,7 +481,8 @@ public class IFParser implements IFConstants {
s = lastAttributes.getValue("word-spacing");
int wordSpacing = (s != null ? Integer.parseInt(s) : 0);
int[] dx = XMLUtil.getAttributeAsIntArray(lastAttributes, "dx");
- painter.drawText(x, y, letterSpacing, wordSpacing, dx, content.toString());
+ String ptr = lastAttributes.getValue("ptr"); // used for accessibility
+ painter.drawText(x, y, letterSpacing, wordSpacing, dx, content.toString(), ptr);
}
public boolean ignoreCharacters() {
@@ -576,9 +577,10 @@ public class IFParser implements IFConstants {
int height = Integer.parseInt(lastAttributes.getValue("height"));
Map foreignAttributes = getForeignAttributes(lastAttributes);
establishForeignAttributes(foreignAttributes);
+ String ptr = lastAttributes.getValue("ptr"); // used for accessibility
if (foreignObject != null) {
painter.drawImage(foreignObject,
- new Rectangle(x, y, width, height));
+ new Rectangle(x, y, width, height), ptr);
foreignObject = null;
} else {
String uri = lastAttributes.getValue(
@@ -586,7 +588,7 @@ public class IFParser implements IFConstants {
if (uri == null) {
throw new IFException("xlink:href is missing on image", null);
}
- painter.drawImage(uri, new Rectangle(x, y, width, height));
+ painter.drawImage(uri, new Rectangle(x, y, width, height), ptr);
}
resetForeignAttributes();
inForeignObject = false;
diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
index 558ddfab8..664f1324a 100644
--- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
@@ -818,7 +818,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
currentIPPosition = saveIP;
currentBPPosition = saveBP;
- currentBPPosition += (int)(bv.getAllocBPD());
+ currentBPPosition += (bv.getAllocBPD());
}
viewportDimensionStack.pop();
}
@@ -886,6 +886,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
// 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();
@@ -929,6 +930,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
// warn if link trait found but not allowed, else create link
if (linkTraitFound) {
+ action.setPtr(ptr); // used for accessibility
Link link = new Link(action, ipRect);
this.deferredLinks.add(link);
}
@@ -963,6 +965,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
String fontName = getInternalFontNameForArea(text);
int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue();
+ String ptr = (String)text.getTrait(Trait.PTR); // used for accessibility
// This assumes that *all* CIDFonts use a /ToUnicode mapping
Typeface tf = getTypeface(fontName);
@@ -981,7 +984,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
textUtil.setStartPosition(rx, bl);
textUtil.setSpacing(text.getTextLetterSpaceAdjust(), text.getTextWordSpaceAdjust());
super.renderText(text);
-
+ textUtil.setPtr(ptr); // used for accessibility
textUtil.flush();
renderTextDecoration(tf, size, text, bl, rx);
}
@@ -1054,15 +1057,24 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
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;
+ private String ptr = null; // used for accessibility
void addChar(char ch) {
text.append(ch);
}
+ /**
+ * used for accessibility
+ * @param inPtr to be stored
+ */
+ public void setPtr(String inPtr) {
+ ptr = inPtr;
+ }
+
void adjust(int adjust) {
if (adjust != 0) {
int idx = text.length();
@@ -1105,9 +1117,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
System.arraycopy(dx, 0, effDX, 0, size);
}
if (combined) {
- painter.drawText(startx, starty, 0, 0, effDX, text.toString());
+ painter.drawText(startx, starty, 0, 0, effDX, text.toString(), ptr);
} else {
- painter.drawText(startx, starty, tls, tws, effDX, text.toString());
+ painter.drawText(startx, starty, tls, tws, effDX, text.toString(), ptr);
}
} catch (IFException e) {
handleIFException(e);
@@ -1118,12 +1130,12 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
}
/** {@inheritDoc} */
- public void renderImage(Image image, Rectangle2D pos) {
- drawImage(image.getURL(), pos, image.getForeignAttributes());
+ public void renderImage(Image image, Rectangle2D pos, String ptr) {
+ drawImage(image.getURL(), pos, image.getForeignAttributes(), ptr);
}
/** {@inheritDoc} */
- protected void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) {
+ protected void drawImage(String uri, Rectangle2D pos, Map foreignAttributes, String ptr) {
Rectangle posInt = new Rectangle(
currentIPPosition + (int)pos.getX(),
currentBPPosition + (int)pos.getY(),
@@ -1132,7 +1144,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
uri = URISpecification.getURL(uri);
try {
establishForeignAttributes(foreignAttributes);
- painter.drawImage(uri, posInt);
+ painter.drawImage(uri, posInt, ptr);
resetForeignAttributes();
} catch (IFException ife) {
handleIFException(ife);
@@ -1140,7 +1152,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
}
/** {@inheritDoc} */
- public void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
+ public void renderForeignObject(ForeignObject fo, Rectangle2D pos, String ptr) {
endTextObject();
Rectangle posInt = new Rectangle(
currentIPPosition + (int)pos.getX(),
@@ -1150,7 +1162,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
Document doc = fo.getDocument();
try {
establishForeignAttributes(fo.getForeignAttributes());
- painter.drawImage(doc, posInt);
+ painter.drawImage(doc, posInt, ptr);
resetForeignAttributes();
} catch (IFException ife) {
handleIFException(ife);
diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
index 5076a089f..d7d4a7539 100644
--- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
@@ -25,11 +25,24 @@ import java.awt.Paint;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
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;
@@ -60,10 +73,46 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
implements IFConstants, IFPainter, IFDocumentNavigationHandler {
private IFDocumentHandler mimicHandler;
+ private int pageSequenceCounter; // used for accessibility
+ private DocumentBuilder parser = null; // used for accessibility
+ private Document doc = null; // used for accessibility
/** Holds the intermediate format state */
private IFState state;
+ private static class NamespaceContextImpl implements NamespaceContext {
+
+ public String uri;
+ public String prefix;
+
+ public NamespaceContextImpl() {
+ }
+
+ public NamespaceContextImpl(String prefix, String uri) {
+ this.uri = uri;
+ this.prefix = prefix;
+ }
+
+ public String getNamespaceURI(String prefix) {
+ return uri;
+ }
+ public void setNamespaceURI(String uri) {
+ this.uri = uri;
+ }
+
+ public String getPrefix(String uri) {
+ return prefix;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+ public Iterator getPrefixes(String uri) {
+ return null;
+ }
+
+ }
+
/**
* Default constructor.
*/
@@ -151,8 +200,16 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
handler.startPrefixMapping(DocumentNavigationExtensionConstants.PREFIX,
DocumentNavigationExtensionConstants.NAMESPACE);
handler.startElement(EL_DOCUMENT);
+ if (this.getUserAgent().accessibilityEnabled()) {
+ pageSequenceCounter = 0;
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ parser = factory.newDocumentBuilder();
+ }
} catch (SAXException e) {
throw new IFException("SAX error in startDocument()", e);
+ } catch (ParserConfigurationException pce) {
+ throw new IFException("Error creating new DocumentBuilder", pce);
}
}
@@ -210,9 +267,33 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
if (id != null) {
atts.addAttribute(XML_NAMESPACE, "id", "xml:id", XMLUtil.CDATA, id);
}
+
handler.startElement(EL_PAGE_SEQUENCE, atts);
+ if (this.getUserAgent().accessibilityEnabled()) {
+ if (doc == null) {
+ doc = parser.parse(
+ new ByteArrayInputStream(this.getUserAgent().getReducedFOTree()));
+ }
+ handler.startElement(EL_STRUCTURE_TREE); // add structure tree
+ String xpathExpr
+ = "/fo:root/fo:page-sequence[" + Integer.toString(++pageSequenceCounter) + "]/*";
+ XPath xpath = XPathFactory.newInstance().newXPath();
+ NamespaceContext namespaceContext
+ = new NamespaceContextImpl("fo", "http://www.w3.org/1999/XSL/Format");
+ xpath.setNamespaceContext(namespaceContext);
+ NodeList nodes = (NodeList)xpath.evaluate(xpathExpr, doc, XPathConstants.NODESET);
+ 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);
+ } catch (XPathExpressionException e) {
+ throw new IFException("Error while evaluating XPath expression", e);
+ } catch (IOException ioe) {
+ throw new IFException("I/O error while parsing structure tree", ioe);
}
}
@@ -285,6 +366,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
public void startPageTrailer() throws IFException {
try {
handler.startElement(EL_PAGE_TRAILER);
+ commitNavigation();
} catch (SAXException e) {
throw new IFException("SAX error in startPageTrailer()", e);
}
@@ -293,7 +375,6 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
/** {@inheritDoc} */
public void endPageTrailer() throws IFException {
try {
- commitNavigation();
handler.endElement(EL_PAGE_TRAILER);
} catch (SAXException e) {
throw new IFException("SAX error in endPageTrailer()", e);
@@ -382,7 +463,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
}
/** {@inheritDoc} */
- public void drawImage(String uri, Rectangle rect) throws IFException {
+ public void drawImage(String uri, Rectangle rect, String ptr) throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, XLINK_HREF, uri);
@@ -391,6 +472,9 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
addAttribute(atts, "width", Integer.toString(rect.width));
addAttribute(atts, "height", Integer.toString(rect.height));
addForeignAttributes(atts);
+ if (ptr != null) {
+ addAttribute(atts, "ptr", ptr); // used for accessibility
+ }
handler.element(EL_IMAGE, atts);
} catch (SAXException e) {
throw new IFException("SAX error in startGroup()", e);
@@ -409,7 +493,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
}
/** {@inheritDoc} */
- public void drawImage(Document doc, Rectangle rect) throws IFException {
+ public void drawImage(Document doc, Rectangle rect, String ptr) throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, "x", Integer.toString(rect.x));
@@ -417,6 +501,9 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
addAttribute(atts, "width", Integer.toString(rect.width));
addAttribute(atts, "height", Integer.toString(rect.height));
addForeignAttributes(atts);
+ if (ptr != null) {
+ addAttribute(atts, "ptr", ptr); // used for accessibility
+ }
handler.startElement(EL_IMAGE, atts);
new DOM2SAX(handler).writeDocument(doc, true);
handler.endElement(EL_IMAGE);
@@ -515,7 +602,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
/** {@inheritDoc} */
public void drawText(int x, int y, int letterSpacing, int wordSpacing,
- int[] dx, String text) throws IFException {
+ int[] dx, String text, String ptr) throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
XMLUtil.addAttribute(atts, XMLConstants.XML_SPACE, "preserve");
@@ -530,6 +617,9 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
if (dx != null) {
addAttribute(atts, "dx", IFUtil.toString(dx));
}
+ if (ptr != null) {
+ addAttribute(atts, "ptr", ptr); // used for accessibility
+ }
handler.startElement(EL_TEXT, atts);
char[] chars = text.toCharArray();
handler.characters(chars, 0, chars.length);
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 f396fd09e..8a4237af1 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 @@ import org.apache.xmlgraphics.util.XMLizable;
public abstract class AbstractAction implements XMLizable {
private String id;
+ private String ptr; // used for accessibility
/**
* Sets an ID to make the action referencable.
@@ -43,7 +44,23 @@ public abstract class AbstractAction implements XMLizable {
public String getID() {
return this.id;
}
-
+
+ /**
+ * Used for accessibility
+ * @param s representing the ptr
+ */
+ public void setPtr(String s) {
+ this.ptr = s;
+ }
+
+ /**
+ * Used for accessibility
+ * @return the ptr
+ */
+ public String getPtr() {
+ return this.ptr;
+ }
+
/**
* 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/java2d/Java2DPainter.java b/src/java/org/apache/fop/render/java2d/Java2DPainter.java
index 55c5b8015..da3108973 100644
--- a/src/java/org/apache/fop/render/java2d/Java2DPainter.java
+++ b/src/java/org/apache/fop/render/java2d/Java2DPainter.java
@@ -156,7 +156,7 @@ public class Java2DPainter extends AbstractIFPainter {
}
/** {@inheritDoc} */
- public void drawImage(String uri, Rectangle rect) throws IFException {
+ public void drawImage(String uri, Rectangle rect, String ptr) throws IFException {
drawImageUsingURI(uri, rect);
}
@@ -168,7 +168,7 @@ public class Java2DPainter extends AbstractIFPainter {
}
/** {@inheritDoc} */
- public void drawImage(Document doc, Rectangle rect) throws IFException {
+ public void drawImage(Document doc, Rectangle rect, String ptr) throws IFException {
drawImageUsingDocument(doc, rect);
}
@@ -208,7 +208,7 @@ public class Java2DPainter extends AbstractIFPainter {
}
/** {@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, String ptr)
throws IFException {
g2dState.updateColor(state.getTextColor());
FontTriplet triplet = new FontTriplet(
diff --git a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
index 933398125..74c709278 100644
--- a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
+++ b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
@@ -875,7 +875,7 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem
ImageFlavor.XML_DOM};
/** {@inheritDoc} */
- protected void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) {
+ protected void drawImage(String uri, Rectangle2D pos, Map foreignAttributes, String ptr) {
int x = currentIPPosition + (int)Math.round(pos.getX());
int y = currentBPPosition + (int)Math.round(pos.getY());
diff --git a/src/java/org/apache/fop/render/pcl/PCLPainter.java b/src/java/org/apache/fop/render/pcl/PCLPainter.java
index da4f6a656..57810fb38 100644
--- a/src/java/org/apache/fop/render/pcl/PCLPainter.java
+++ b/src/java/org/apache/fop/render/pcl/PCLPainter.java
@@ -154,7 +154,7 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
}
/** {@inheritDoc} */
- public void drawImage(String uri, Rectangle rect) throws IFException {
+ public void drawImage(String uri, Rectangle rect, String ptr) throws IFException {
drawImageUsingURI(uri, rect);
}
@@ -176,7 +176,7 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
}
/** {@inheritDoc} */
- public void drawImage(Document doc, Rectangle rect) throws IFException {
+ public void drawImage(Document doc, Rectangle rect, String ptr) throws IFException {
drawImageUsingDocument(doc, rect);
}
@@ -312,8 +312,9 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
}
/** {@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, String ptr)
throws IFException {
+ //Note: ptr is ignored as it is only needed for accessibility
try {
FontTriplet triplet = new FontTriplet(
state.getFontFamily(), state.getFontStyle(), state.getFontWeight());
@@ -474,7 +475,7 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
Java2DPainter painter = new Java2DPainter(g2d,
getContext(), parent.getFontInfo(), state);
try {
- painter.drawText(x, y, letterSpacing, wordSpacing, dx, text);
+ painter.drawText(x, y, letterSpacing, wordSpacing, dx, text, "");
} catch (IFException e) {
//This should never happen with the Java2DPainter
throw new RuntimeException("Unexpected error while painting text", e);
diff --git a/src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java b/src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java
index 841dd7e01..2f4379795 100644
--- a/src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java
+++ b/src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java
@@ -49,4 +49,6 @@ public interface PDFConfigurationConstants {
* PDF/X profile is active).
*/
String KEY_DISABLE_SRGB_COLORSPACE = "disable-srgb-colorspace";
+ /** PDF Accessibility */
+ String ACCESSIBLITY = "accessibility";
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
index 45fb7ec51..750f9a3cf 100644
--- a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
+++ b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
@@ -50,6 +50,7 @@ public class PDFContentGenerator {
/** the current stream to add PDF commands to */
private PDFStream currentStream;
+ private boolean accessEnabled; // used for accessibility
/** drawing state */
protected PDFPaintingState currentState = null;
@@ -63,9 +64,10 @@ public class PDFContentGenerator {
* @param document the PDF document
* @param out the output stream the PDF document is generated to
* @param resourceContext the resource context
+ * @param accessibilityEnabled indicating if accessibility is enabled or not
*/
public PDFContentGenerator(PDFDocument document, OutputStream out,
- PDFResourceContext resourceContext) {
+ PDFResourceContext resourceContext, boolean accessibilityEnabled) {
this.document = document;
this.outputStream = out;
this.resourceContext = resourceContext;
@@ -78,6 +80,7 @@ public class PDFContentGenerator {
};
this.currentState = new PDFPaintingState();
+ this.accessEnabled = accessibilityEnabled;
}
/**
@@ -153,6 +156,23 @@ public class PDFContentGenerator {
currentStream.add("q\n");
}
+ /** {@inheritDoc} */
+ protected void saveGraphicsState(String structElemType, int sequenceNum) {
+ endTextObject();
+ currentState.save();
+ startAccessSequence(structElemType, sequenceNum);
+ currentStream.add("q\n");
+ }
+
+ /**
+ * Used for accessibility
+ * @param structElemType Structure Element Type
+ * @param sequenceNum Sequence number
+ */
+ protected void startAccessSequence(String structElemType, int sequenceNum) {
+ currentStream.add(structElemType + " <</MCID " + String.valueOf(sequenceNum) + ">>\nBDC\n");
+ }
+
/**
* Restored the graphics state valid before the previous {@code #saveGraphicsState()}.
* @param popState true if the state should also be popped, false if only the PDF command
@@ -171,6 +191,35 @@ public class PDFContentGenerator {
restoreGraphicsState(true);
}
+ /** used for accessibility */
+ protected void restoreGraphicsStateAccess() {
+ endTextObject();
+ currentStream.add("Q\n");
+ currentStream.add("EMC\n");
+ currentState.restore();
+ }
+
+ /**
+ * used for accessibility, separates 2 text elements
+ * @param mcid of new text element
+ * @param structElemType of parent of new text element
+ */
+ protected void separateTextElements(int mcid, String structElemType) {
+ textutil.endTextObject(true);
+ textutil.beginTextObjectAccess(mcid, structElemType);
+ }
+
+ /**
+ * used for accessibility
+ * separates a text element from fo:leader text element
+ */
+ public void separateTextElementFromLeader() {
+ if (!textutil.inArtifactMode()) {
+ textutil.endTextObject(true);
+ textutil.beginArtifactTextObject();
+ }
+ }
+
/** Indicates the beginning of a text object. */
protected void beginTextObject() {
if (!textutil.isInTextObject()) {
@@ -178,10 +227,30 @@ public class PDFContentGenerator {
}
}
+ /**
+ * Accessibility beginTextObject
+ * @param mcid of text element
+ * @param structElemType of parent
+ */
+ protected void beginTextObjectAccess(int mcid, String structElemType) {
+ if (!textutil.isInTextObject()) {
+ textutil.beginTextObjectAccess(mcid, structElemType);
+ }
+ }
+
+ /**
+ * Accessibility begin of LeaderTextObject
+ */
+ public void beginLeaderTextObject() {
+ if (!textutil.isInTextObject()) {
+ textutil.beginArtifactTextObject();
+ }
+ }
+
/** Indicates the end of a text object. */
protected void endTextObject() {
if (textutil.isInTextObject()) {
- textutil.endTextObject();
+ textutil.endTextObject(accessEnabled);
}
}
@@ -326,5 +395,26 @@ public class PDFContentGenerator {
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
+ * @param structElemType of this image
+ * @param mcid of this image
+ */
+ 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 b98b15d5e..f7eb3e7b3 100644
--- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
@@ -21,9 +21,28 @@ package org.apache.fop.render.pdf;
import java.awt.Dimension;
import java.awt.geom.AffineTransform;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.xml.sax.SAXException;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -32,11 +51,19 @@ import org.apache.xmlgraphics.xmp.Metadata;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.fo.extensions.xmp.XMPMetadata;
import org.apache.fop.pdf.PDFAnnotList;
+import org.apache.fop.pdf.PDFArray;
+import org.apache.fop.pdf.PDFDictionary;
import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFName;
+import org.apache.fop.pdf.PDFNumsArray;
+import org.apache.fop.pdf.PDFObject;
import org.apache.fop.pdf.PDFPage;
+import org.apache.fop.pdf.PDFParentTree;
import org.apache.fop.pdf.PDFReference;
import org.apache.fop.pdf.PDFResourceContext;
import org.apache.fop.pdf.PDFResources;
+import org.apache.fop.pdf.PDFStructElem;
+import org.apache.fop.pdf.PDFStructTreeRoot;
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
@@ -52,6 +79,70 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
/** logging instance */
private static Log log = LogFactory.getLog(PDFDocumentHandler.class);
+ /** the following variables are used for accessibility */
+ private int pageSequenceCounter;
+ private DocumentBuilder parser = null;
+ private Document doc = null;
+ private Map structElemType = new HashMap();
+ private boolean accessEnabled = false;
+ private int parentTreeKey = -1;
+ private int pageLinkCount = 0;
+ private int mcidKey = -1;
+ private PDFParentTree parentTree = null;
+ private Map structTreeMap = new HashMap();
+ private List parentTreeList = new java.util.ArrayList();
+
+ private static class NamespaceContextImpl implements NamespaceContext {
+
+ private String uri;
+ private String prefix;
+
+ public NamespaceContextImpl() {
+ }
+
+ public NamespaceContextImpl(String prefix, String uri) {
+ this.uri = uri;
+ this.prefix = prefix;
+ }
+
+ public String getNamespaceURI(String prefix) {
+ return uri;
+ }
+
+ public void setNamespaceURI(String uri) {
+ this.uri = uri;
+ }
+
+ public String getPrefix(String uri) {
+ return prefix;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public Iterator getPrefixes(String uri) {
+ return null;
+ }
+
+ }
+
+ private static final class ParentTreeEntry {
+ private final int position;
+ private final PDFObject object;
+ private ParentTreeEntry(int p, PDFObject o) {
+ position = p;
+ object = o;
+ }
+ private int getPosition() {
+ return position;
+ }
+ private PDFObject getPDFObject() {
+ return object;
+ }
+ }
+
+
/** the PDF Document being created */
protected PDFDocument pdfDoc;
@@ -79,7 +170,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
/** Used for bookmarks/outlines. */
protected Map pageReferences = new java.util.HashMap();
- private PDFDocumentNavigationHandler documentNavigationHandler
+ private final PDFDocumentNavigationHandler documentNavigationHandler
= new PDFDocumentNavigationHandler(this);
/**
@@ -90,7 +181,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
/** {@inheritDoc} */
public boolean supportsPagesOutOfOrder() {
- return true;
+ return !accessEnabled;
}
/** {@inheritDoc} */
@@ -123,8 +214,21 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
super.startDocument();
try {
this.pdfDoc = pdfUtil.setupPDFDocument(this.outputStream);
+ this.accessEnabled = getUserAgent().accessibilityEnabled();
+ if (accessEnabled) {
+ //TODO: make document language variable, see note on wiki page PDF Accessibility
+ //TODO: and follow-up emails on fop-dev
+ this.pdfDoc.getRoot().setLanguage("en");
+ parentTree = new PDFParentTree();
+ pageSequenceCounter = 0;
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ parser = factory.newDocumentBuilder();
+ }
} catch (IOException e) {
throw new IFException("I/O error in startDocument()", e);
+ } catch (ParserConfigurationException pce) {
+ throw new IFException("Error creating new DocumentBuilder", pce);
}
}
@@ -137,10 +241,34 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
public void endDocument() throws IFException {
try {
pdfDoc.getResources().addFonts(pdfDoc, fontInfo);
- pdfDoc.outputTrailer(this.outputStream);
-
+ if (getUserAgent().accessibilityEnabled()) {
+ PDFNumsArray nums = parentTree.getNums();
+ for (int i = 0; i <= this.parentTreeKey; i++) {
+ PDFArray tArray = new PDFArray();
+ for (int j = 0; j < parentTreeList.size(); j++) {
+ if (((ParentTreeEntry)parentTreeList.get(j)).getPosition() == i) {
+ tArray.add(((ParentTreeEntry)parentTreeList.get(j)).getPDFObject());
+ }
+ }
+ if (tArray.length() == 1) {
+ nums.put(i, tArray.get(0));
+ } else if (tArray.length() > 1) {
+ nums.put(i, tArray);
+ }
+ }
+ parentTree.setNums(nums);
+ getStructTreeRoot().addParentTree(parentTree);
+ pdfDoc.outputTrailer(this.outputStream);
+ parser = null;
+ doc = null;
+ structElemType = null;
+ parentTree = null;
+ structTreeMap = null;
+ parentTreeList = null;
+ } else {
+ pdfDoc.outputTrailer(this.outputStream);
+ }
this.pdfDoc = null;
-
pdfResources = null;
this.generator = null;
currentContext = null;
@@ -151,9 +279,60 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
super.endDocument();
}
+ private PDFStructTreeRoot getStructTreeRoot() {
+ return this.pdfDoc.getRoot().getStructTreeRoot();
+ }
+
+
/** {@inheritDoc} */
public void startPageSequence(String id) throws IFException {
//TODO page sequence title, country and language
+
+ if (getUserAgent().accessibilityEnabled()) {
+ try {
+ if (doc == null) {
+ doc = parser.parse(
+ new ByteArrayInputStream(this.getUserAgent().getReducedFOTree()));
+ }
+ PDFStructElem parent = (PDFStructElem)getStructTreeRoot().getFirstChild();
+ PDFStructElem structElemPart = new PDFStructElem("page-sequence", parent);
+ this.pdfDoc.assignObjectNumber(structElemPart);
+ this.pdfDoc.addTrailerObject(structElemPart);
+ parent.addKid(structElemPart);
+
+ String xpathExpr = "/fo:root/fo:page-sequence["
+ + Integer.toString(++pageSequenceCounter) + "]/*";
+ XPath xpath = XPathFactory.newInstance().newXPath();
+ NamespaceContext namespaceContext = new NamespaceContextImpl("fo",
+ "http://www.w3.org/1999/XSL/Format");
+ xpath.setNamespaceContext(namespaceContext);
+
+ NodeList nodes = (NodeList) xpath.evaluate(xpathExpr, doc,
+ XPathConstants.NODESET);
+
+ for (int i = 0, n = nodes.getLength(); i < n; i++) {
+ Node node = nodes.item(i);
+ if (node.getNodeName().equals("fo:flow")
+ || node.getNodeName().equals("fo:static-content")) {
+ PDFStructElem structElemSect = new PDFStructElem(
+ node.getLocalName(), structElemPart);
+ this.pdfDoc.assignObjectNumber(structElemSect);
+ this.pdfDoc.addTrailerObject(structElemSect);
+ structElemPart.addKid(structElemSect);
+ NodeList iNodes = node.getChildNodes();
+ for (int j = 0, m = iNodes.getLength(); j < m; j++) {
+ processContent(iNodes.item(j), structElemSect, 1);
+ }
+ }
+ }
+ } catch (SAXException e) {
+ throw new IFException("SAX error in startPageSequence()", e);
+ } catch (XPathExpressionException e) {
+ throw new IFException("Error while evaluating XPath expression", e);
+ } catch (IOException ioe) {
+ throw new IFException("I/O error while parsing structure tree", ioe);
+ }
+ }
}
/** {@inheritDoc} */
@@ -164,22 +343,25 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
/** {@inheritDoc} */
public void startPage(int index, String name, String pageMasterName, Dimension size)
throws IFException {
+ // used for accessibility
+ this.parentTreeKey = this.parentTreeKey + this.pageLinkCount + 1;
+ this.mcidKey = 0;
+ this.pageLinkCount = 0;
+ //
this.pdfResources = this.pdfDoc.getResources();
this.currentPage = this.pdfDoc.getFactory().makePage(
this.pdfResources,
(int)Math.round(size.getWidth() / 1000),
- (int)Math.round(size.getHeight() / 1000),
- index);
- //pageReferences.put(new Integer(index)/*page.getKey()*/, currentPage.referencePDF());
- //pvReferences.put(page.getKey(), page);
-
+ (int)Math.round(size.getHeight() / 1000), index,
+ parentTreeKey); // used for accessibility
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, this.accessEnabled);
// 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,
size.height / 1000f);
@@ -234,8 +416,8 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
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();
@@ -251,4 +433,191 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
}
}
+ /**
+ * Used for accessibility
+ * @param position in parentTree
+ * @param o reference of PDFObject to be added to parentTree
+ */
+ void addToParentTree(int position, PDFObject o) {
+ PDFNumsArray nums = parentTree.getNums();
+ nums.put(position, o);
+ parentTree.setNums(nums);
+ }
+
+
+ /**
+ * Used for accessibility
+ * @param position in parentTree
+ * @param o object to be added to parentTree
+ */
+ void addToTempList(int position, PDFObject o) {
+ ParentTreeEntry myEntry = new ParentTreeEntry(position, o);
+ this.parentTreeList.add(myEntry);
+ }
+
+
+ /**
+ * Return the PDFObject
+ * @param ptr this is the key
+ * @return PDFObject referenced with ptr
+ */
+ PDFObject getTrailerObject(String ptr) {
+ return (PDFObject) this.structTreeMap.get(ptr);
+ }
+
+ /**
+ * Return the parent PDFObject referenced by ptr
+ * @param ptr this is the key
+ * @return PDFObject parent of PDFObject referenced with ptr
+ */
+ PDFObject getParentTrailerObject(String ptr) {
+ PDFStructElem tempStructElem = (PDFStructElem) this.structTreeMap.get(ptr);
+ return tempStructElem.getParentStructElem();
+ }
+
+ /**
+ * Adds a link object as child to StructElem
+ * @param ptr of PDFStructElem
+ * @param o PDFLink object
+ */
+ void addLinkToStructElem(String ptr, PDFObject o) {
+ PDFDictionary dict = new PDFDictionary();
+ dict.put("Type", new PDFName("OBJR"));
+ dict.put("Pg", this.currentPage);
+ dict.put("Obj", o);
+ PDFStructElem tempStructElem = (PDFStructElem) structTreeMap.get(ptr);
+ tempStructElem.addKid(dict);
+ }
+
+ /**
+ * Adds a child to StructElem, called from PDFPainter.drawImage
+ * @param ptr of PDFStructElem
+ * @param mcid sequence number within page
+ */
+ void addChildToStructElemImage(String ptr, int mcid) {
+ PDFStructElem tempStructElem = (PDFStructElem) structTreeMap.get(ptr);
+ tempStructElem.addMCIDKid(mcid);
+ tempStructElem.addPage(this.currentPage);
+ if (!tempStructElem.getLevel1()) {
+ addMeToParent(tempStructElem);
+ }
+ }
+
+ /**
+ * Adds a child to StructElem, called from PDFPainter.drawText
+ * @param ptr of PDFSturctElem
+ * @param mcid sequence number within page
+ */
+ void addChildToStructElemText(String ptr, int mcid) {
+ PDFDictionary dict = new PDFDictionary();
+ dict.put("Type", new PDFName("MCR"));
+ dict.put("Pg", this.currentPage);
+ dict.put("MCID", mcid);
+ PDFStructElem tempStructElem = (PDFStructElem) structTreeMap.get(ptr);
+ tempStructElem.addKid(dict);
+ if (!tempStructElem.getLevel1()) {
+ addMeToParent(tempStructElem);
+ }
+ }
+
+ /**
+ * Add child PDFStructElem to parent child elements
+ * Repeat until level 1 or child already exists
+ * @param childStructElem to be added
+ */
+ protected void addMeToParent(PDFStructElem childStructElem) {
+ PDFStructElem parentStructElem = (PDFStructElem) childStructElem.getParentStructElem();
+ // test if child already exists or not
+ if (parentStructElem.addUniqueKid(childStructElem)) {
+ if (!parentStructElem.getLevel1()) {
+ addMeToParent(parentStructElem);
+ }
+ }
+ }
+
+ /**
+ * increment MCID value
+ */
+ void incMCID() {
+ this.mcidKey++;
+ }
+
+ /**
+ * MCID is a sequential number per page
+ * @return MCID value
+ */
+ int getMCID() {
+ return this.mcidKey;
+ }
+
+ /**
+ * Used for accessibility
+ * @param ptr pointer into map of all structElems
+ * @return type of found structElem
+ */
+ String getStructElemType(String ptr) {
+ return (String) structElemType.get(ptr);
+ }
+
+ /**
+ * Used for accessibility
+ * @param me node being processed
+ * @param parent parent node in DOM of me
+ * @param depth depth level in DOM, static-content & flow are 0
+ */
+ private void processContent(Node me, PDFStructElem parent, int depth) {
+ String ptr;
+ Node attr = me.getAttributes().getNamedItem("foi:ptr");
+ if (attr != null) {
+ ptr = attr.getNodeValue();
+ } else {
+ log.error("Accessibility: missing foi:ptr");
+ ptr = "";
+ }
+ String s = me.getLocalName();
+ PDFStructElem structElem = new PDFStructElem(s, parent);
+ this.pdfDoc.assignObjectNumber(structElem);
+ this.pdfDoc.addTrailerObject(structElem);
+ if (depth == 1) {
+ parent.addKid(structElem);
+ structElem.setLevel1();
+ }
+ if (s.equals("external-graphic") || s.equals("instream-foreign-object")) {
+ Node altTextNode = me.getAttributes().getNamedItem("fox:alt-text");
+ if (altTextNode != null) {
+ structElem.put("Alt", altTextNode.getNodeValue());
+ } else {
+ log.warn("fo:" + s
+ + " requires an alternative text attribute fox:alt-text for accessibility");
+ structElem.put("Alt", "No alternate text specified");
+ }
+ }
+ // the following map is used e.g. in PDFPainter.drawText
+ structElemType.put(ptr, structElem.get("S").toString());
+ // this map will be used for fast access of the StructElem by ptr
+ structTreeMap.put(ptr, structElem);
+ NodeList nodes = me.getChildNodes();
+ depth++;
+ for (int i = 0, n = nodes.getLength(); i < n; i++) {
+ processContent(nodes.item(i), structElem, depth);
+ }
+ }
+
+ /**
+ * used for accessibility
+ * @return mcid to be used for next link to be processed
+ */
+ int getPageLinkCountPlusPageParentKey() {
+ this.pageLinkCount++;
+ return (this.parentTreeKey + this.pageLinkCount);
+ }
+
+ /**
+ * used for accessibility
+ * @return current parentTreeKey
+ */
+ int getCurrentParentTreeKey() {
+ return this.parentTreeKey;
+ }
+
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
index bc22bb6d2..2f2de2e62 100644
--- a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java
@@ -42,15 +42,18 @@ import org.apache.fop.render.intermediate.extensions.NamedDestination;
import org.apache.fop.render.intermediate.extensions.URIAction;
import org.apache.fop.render.pdf.PDFDocumentHandler.PageReference;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
/**
* Implementation of the {@link IFDocumentNavigationHandler} interface for PDF output.
*/
public class PDFDocumentNavigationHandler implements IFDocumentNavigationHandler {
+ private static Log log = LogFactory.getLog(PDFDocumentHandler.class);
+ private final PDFDocumentHandler documentHandler;
- private 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 +114,14 @@ public class PDFDocumentNavigationHandler implements IFDocumentNavigationHandler
PDFLink pdfLink = getPDFDoc().getFactory().makeLink(
targetRect2D, pdfAction);
if (pdfLink != null) {
+ //accessibility: ptr has a value
+ String ptr = link.getAction().getPtr();
+ if (ptr.length() > 0) {
+ this.documentHandler.addLinkToStructElem(ptr, pdfLink);
+ int id = this.documentHandler.getPageLinkCountPlusPageParentKey();
+ pdfLink.setStructParent(id);
+ this.documentHandler.addToParentTree(id, pdfLink );
+ }
documentHandler.currentPage.addAnnotation(pdfLink);
}
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java
index d47d5a439..074a19bec 100644
--- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java
@@ -82,7 +82,17 @@ public class PDFImageHandlerRawJPEG implements PDFImageHandler, ImageHandler {
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().accessibilityEnabled()) {
+ String structElemType = pdfContext.getStructElemType();
+ if (structElemType.length() > 0) {
+ int sequenceNum = pdfContext.getSequenceNum();
+ generator.placeImage(x, y, w, h, xobj, structElemType, sequenceNum);
+ } else {
+ generator.placeImage(x, y, w, h, xobj);
+ }
+ } 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 3e57c7216..728fa0601 100644
--- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java
@@ -83,7 +83,13 @@ public class PDFImageHandlerRenderedImage implements PDFImageHandler, ImageHandl
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().accessibilityEnabled()) {
+ String structElemType = pdfContext.getStructElemType();
+ int sequenceNum = pdfContext.getSequenceNum();
+ generator.placeImage(x, y, w, h, xobj, structElemType, sequenceNum);
+ } 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 d1b7aa986..40fabbc5b 100644
--- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java
@@ -101,8 +101,8 @@ public class PDFImageHandlerSVG implements ImageHandler {
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 +121,11 @@ public class PDFImageHandlerSVG implements ImageHandler {
*/
generator.comment("SVG setup");
generator.saveGraphicsState();
+ if (context.getUserAgent().accessibilityEnabled()) {
+ String structElemType = pdfContext.getStructElemType();
+ int sequenceNum = pdfContext.getSequenceNum();
+ generator.startAccessSequence(structElemType, sequenceNum);
+ }
generator.setColor(Color.black, false);
generator.setColor(Color.black, true);
@@ -168,7 +173,11 @@ public class PDFImageHandlerSVG implements ImageHandler {
eventProducer.svgRenderingError(this, e, image.getInfo().getOriginalURI());
}
generator.getState().restore();
- generator.restoreGraphicsState();
+ if (context.getUserAgent().accessibilityEnabled()) {
+ generator.restoreGraphicsStateAccess();
+ } else {
+ generator.restoreGraphicsState();
+ }
generator.comment("SVG end");
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java
index fa00fd7b4..b87569ed3 100644
--- a/src/java/org/apache/fop/render/pdf/PDFPainter.java
+++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java
@@ -59,12 +59,18 @@ 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 = false;
+
+ private int mcid; // used for accessibility
+
+ private String structElemType; // used for accessibility
/**
* Default constructor.
@@ -76,6 +82,7 @@ public class PDFPainter extends AbstractIFPainter {
this.generator = documentHandler.generator;
this.borderPainter = new PDFBorderPainter(this.generator);
this.state = IFState.create();
+ accessEnabled = this.getUserAgent().accessibilityEnabled();
}
/** {@inheritDoc} */
@@ -122,15 +129,50 @@ public class PDFPainter extends AbstractIFPainter {
}
/** {@inheritDoc} */
- public void drawImage(String uri, Rectangle rect) throws IFException {
+ public void drawImage(String uri, Rectangle rect, String ptr)
+ throws IFException {
PDFXObject xobject = getPDFDoc().getXObject(uri);
if (xobject != null) {
- placeImage(rect, xobject);
+ if (accessEnabled && ptr.length() > 0) {
+ mcid = this.documentHandler.getMCID();
+ mcid++; // fix for Acro Checker
+ this.documentHandler.incMCID(); // simulating a parent text element
+ structElemType = this.documentHandler.getStructElemType(ptr);
+ this.documentHandler.addToTempList(
+ this.documentHandler.getCurrentParentTreeKey(),
+ this.documentHandler.getParentTrailerObject(ptr));
+ this.documentHandler.addToTempList(
+ this.documentHandler.getCurrentParentTreeKey(),
+ this.documentHandler.getTrailerObject(ptr));
+ placeImageAccess(rect, xobject);
+ this.documentHandler.addChildToStructElemImage(ptr, mcid);
+ this.documentHandler.incMCID();
+ } else {
+ placeImage(rect, xobject);
+ }
return;
}
-
- drawImageUsingURI(uri, rect);
-
+ if (accessEnabled && ptr.length() > 0) {
+ mcid = this.documentHandler.getMCID();
+ mcid++; // fix for Acro Checker
+ this.documentHandler.incMCID(); // simulating a parent text element
+ structElemType = this.documentHandler.getStructElemType(ptr);
+ this.documentHandler.addToTempList(
+ this.documentHandler.getCurrentParentTreeKey(),
+ this.documentHandler.getParentTrailerObject(ptr));
+ this.documentHandler.addToTempList(
+ this.documentHandler.getCurrentParentTreeKey(),
+ this.documentHandler.getTrailerObject(ptr));
+ //PDFRenderingContext pdfContext = new PDFRenderingContext(
+ // getUserAgent(), generator, this.documentHandler.currentPage, getFontInfo());
+ //pdfContext.setMCID(mcid);
+ //pdfContext.setStructElemType(structElemType);
+ drawImageUsingURI(uri, rect);
+ this.documentHandler.addChildToStructElemImage(ptr, mcid);
+ this.documentHandler.incMCID();
+ } else {
+ drawImageUsingURI(uri, rect);
+ }
flushPDFDoc();
}
@@ -138,6 +180,8 @@ public class PDFPainter extends AbstractIFPainter {
protected RenderingContext createRenderingContext() {
PDFRenderingContext pdfContext = new PDFRenderingContext(
getUserAgent(), generator, this.documentHandler.currentPage, getFontInfo());
+ pdfContext.setMCID(mcid);
+ pdfContext.setStructElemType(structElemType);
return pdfContext;
}
@@ -158,11 +202,43 @@ public class PDFPainter extends AbstractIFPainter {
+ " 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(structElemType, 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 {
- drawImageUsingDocument(doc, rect);
-
+ public void drawImage(Document doc, Rectangle rect, String ptr) throws IFException {
+ if (accessEnabled && ptr.length() > 0) {
+ mcid = this.documentHandler.getMCID();
+ mcid++; // fix for Acro Checker
+ this.documentHandler.incMCID(); // simulating a parent text element
+ structElemType = this.documentHandler.getStructElemType(ptr);
+ this.documentHandler.addToTempList(
+ this.documentHandler.getCurrentParentTreeKey(),
+ this.documentHandler.getParentTrailerObject(ptr));
+ this.documentHandler.addToTempList(
+ this.documentHandler.getCurrentParentTreeKey(),
+ this.documentHandler.getTrailerObject(ptr));
+ drawImageUsingDocument(doc, rect);
+ this.documentHandler.addChildToStructElemImage(ptr, mcid);
+ this.documentHandler.incMCID();
+ } else {
+ drawImageUsingDocument(doc, rect);
+ }
flushPDFDoc();
}
@@ -253,10 +329,38 @@ public class PDFPainter extends AbstractIFPainter {
}
/** {@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, String ptr)
throws IFException {
- generator.updateColor(state.getTextColor(), true, null);
- generator.beginTextObject();
+ if (accessEnabled ) {
+ int mcId;
+ String structElType = "";
+ if (ptr != null && ptr.length() > 0) {
+ mcId = this.documentHandler.getMCID();
+ this.documentHandler.addToTempList(
+ this.documentHandler.getCurrentParentTreeKey(),
+ this.documentHandler.getTrailerObject(ptr));
+ structElType = this.documentHandler.getStructElemType(ptr);
+ if (generator.getTextUtil().isInTextObject()) {
+ generator.separateTextElements(mcId, structElType);
+ }
+ generator.updateColor(state.getTextColor(), true, null);
+ generator.beginTextObjectAccess(mcId, structElType);
+ this.documentHandler.addChildToStructElemText(ptr, mcId);
+ this.documentHandler.incMCID();
+ } else {
+ // <fo:leader leader-pattern="use-content">
+ if (generator.getTextUtil().isInTextObject()) {
+ generator.separateTextElementFromLeader();
+ }
+ generator.updateColor(state.getTextColor(), true, null);
+ generator.beginLeaderTextObject();
+ }
+ } 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 +381,7 @@ public class PDFPainter extends AbstractIFPainter {
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 c40c94fc4..c6b4d4977 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java
@@ -461,7 +461,8 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf
double h = bounds.getHeight();
pageHeight = (int) h;
- this.generator = new PDFContentGenerator(this.pdfDoc, this.ostream, this.currentPage);
+ this.generator = new PDFContentGenerator(this.pdfDoc, this.ostream, this.currentPage,
+ false);
this.borderPainter = new PDFBorderPainter(this.generator);
// Transform the PDF's default coordinate system (0,0 at lower left) to the PDFRenderer's
@@ -1073,7 +1074,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConf
}
/** {@inheritDoc} */
- protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes) {
+ protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes, String ptr) {
endTextObject();
putImage(url, pos, foreignAttributes);
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingContext.java b/src/java/org/apache/fop/render/pdf/PDFRenderingContext.java
index 98b0c8203..f82019b4a 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderingContext.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderingContext.java
@@ -34,6 +34,11 @@ public class PDFRenderingContext extends AbstractRenderingContext {
private PDFContentGenerator generator;
private FontInfo fontInfo;
private PDFPage page;
+ /** Temp. val. for accessibility, used in PDFImageHandlerRenderedImage */
+ private String structElemType = "";
+
+ /** Temp. val. for accessibility, used in PDFImageHandlerRenderedImage */
+ private int mcid = -1;
/**
* Main constructor.
@@ -79,4 +84,35 @@ public class PDFRenderingContext extends AbstractRenderingContext {
return this.fontInfo;
}
+ /**
+ * Used for accessibility, used in PDFPainter.drawImage
+ * @param value to be stored
+ */
+ public void setMCID(int value) {
+ mcid = value;
+ }
+
+ /**
+ * Used for accessibility
+ * @return mcid
+ */
+ public int getSequenceNum() {
+ return mcid;
+ }
+
+ /**
+ * Used for accessibility
+ * @param s the type of the structure element
+ */
+ public void setStructElemType(String s) {
+ structElemType = s;
+ }
+
+ /**
+ * Used for accessibility
+ * @return the type of the structure element
+ */
+ public String getStructElemType() {
+ return structElemType;
+ }
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
index 2e3c83126..8d9536bff 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
@@ -52,6 +52,8 @@ import org.apache.fop.pdf.PDFMetadata;
import org.apache.fop.pdf.PDFNumsArray;
import org.apache.fop.pdf.PDFOutputIntent;
import org.apache.fop.pdf.PDFPageLabels;
+import org.apache.fop.pdf.PDFStructElem;
+import org.apache.fop.pdf.PDFStructTreeRoot;
import org.apache.fop.pdf.PDFXMode;
import org.apache.fop.util.ColorProfileUtil;
@@ -75,6 +77,9 @@ class PDFRenderingUtil implements PDFConfigurationConstants {
/** the PDF/X mode (Default: disabled) */
protected PDFXMode pdfXMode = PDFXMode.DISABLED;
+ /** the accessibility mode (Default: false=disabled) */
+ protected boolean accessibility = false;
+
/** the (optional) encryption parameters */
protected PDFEncryptionParams encryptionParams;
@@ -169,6 +174,12 @@ class PDFRenderingUtil implements PDFConfigurationConstants {
if (s != null) {
this.outputProfileURI = s;
}
+ // used for accessibility
+ setting = userAgent.getRendererOptions().get(ACCESSIBLITY);
+ if (setting != null) {
+ this.accessibility = booleanValueOf(setting);
+ }
+
setting = userAgent.getRendererOptions().get(KEY_DISABLE_SRGB_COLORSPACE);
if (setting != null) {
this.disableSRGBColorSpace = booleanValueOf(setting);
@@ -384,6 +395,16 @@ class PDFRenderingUtil implements PDFConfigurationConstants {
log.debug("PDF/A is active. Conformance Level: " + pdfAMode);
addPDFA1OutputIntent();
}
+ if (this.accessibility) {
+ this.pdfDoc.getRoot().makeTagged();
+ log.info("Accessibility is enabled");
+ PDFStructTreeRoot structTreeRoot = this.pdfDoc.getFactory().makeStructTreeRoot();
+ this.pdfDoc.getRoot().setStructTreeRoot(structTreeRoot);
+ PDFStructElem structElemDocument = new PDFStructElem("root", structTreeRoot);
+ this.pdfDoc.assignObjectNumber(structElemDocument);
+ this.pdfDoc.addTrailerObject(structElemDocument);
+ structTreeRoot.addKid(structElemDocument);
+ }
return this.pdfDoc;
}
diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java
index cb88f4670..807bf50b4 100644
--- a/src/java/org/apache/fop/render/ps/PSPainter.java
+++ b/src/java/org/apache/fop/render/ps/PSPainter.java
@@ -176,7 +176,7 @@ public class PSPainter extends AbstractIFPainter {
}
/** {@inheritDoc} */
- public void drawImage(String uri, Rectangle rect) throws IFException {
+ public void drawImage(String uri, Rectangle rect, String ptr) throws IFException {
try {
endTextObject();
} catch (IOException ioe) {
@@ -186,7 +186,7 @@ public class PSPainter extends AbstractIFPainter {
}
/** {@inheritDoc} */
- public void drawImage(Document doc, Rectangle rect) throws IFException {
+ public void drawImage(Document doc, Rectangle rect, String ptr) throws IFException {
try {
endTextObject();
} catch (IOException ioe) {
@@ -338,7 +338,7 @@ public class PSPainter extends AbstractIFPainter {
/** {@inheritDoc} */
public void drawText(int x, int y, int letterSpacing, int wordSpacing,
- int[] dx, String text) throws IFException {
+ int[] dx, String text, String ptr) throws IFException {
try {
//Note: dy is currently ignored
PSGenerator generator = getGenerator();
diff --git a/src/java/org/apache/fop/render/ps/PSRenderer.java b/src/java/org/apache/fop/render/ps/PSRenderer.java
index 19fcd8af8..5b918156b 100644
--- a/src/java/org/apache/fop/render/ps/PSRenderer.java
+++ b/src/java/org/apache/fop/render/ps/PSRenderer.java
@@ -345,7 +345,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
}
/** {@inheritDoc} */
- protected void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) {
+ protected void drawImage(String uri, Rectangle2D pos, Map foreignAttributes, String ptr) {
endTextObject();
int x = currentIPPosition + (int)Math.round(pos.getX());
int y = currentBPPosition + (int)Math.round(pos.getY());
@@ -1233,7 +1233,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
* {@inheritDoc}
*/
public void renderImage(Image image, Rectangle2D pos) {
- drawImage(image.getURL(), pos, image.getForeignAttributes());
+ drawImage(image.getURL(), pos, image.getForeignAttributes(), "");
}
/**
diff --git a/src/java/org/apache/fop/render/txt/TXTRenderer.java b/src/java/org/apache/fop/render/txt/TXTRenderer.java
index 575f1232f..2170a67d2 100644
--- a/src/java/org/apache/fop/render/txt/TXTRenderer.java
+++ b/src/java/org/apache/fop/render/txt/TXTRenderer.java
@@ -443,7 +443,7 @@ public class TXTRenderer extends AbstractPathOrientedRenderer {
}
/** {@inheritDoc} */
- protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes) {
+ protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes, String ptr) {
//No images are painted here
}
diff --git a/src/java/org/apache/fop/util/DOM2SAX.java b/src/java/org/apache/fop/util/DOM2SAX.java
index 839cf107f..b9021ed3a 100644
--- a/src/java/org/apache/fop/util/DOM2SAX.java
+++ b/src/java/org/apache/fop/util/DOM2SAX.java
@@ -77,6 +77,15 @@ public class DOM2SAX {
contentHandler.endDocument();
}
}
+
+ /**
+ * 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
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
index 0ee13c021..cb0547391 100644
--- a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
+++ b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
@@ -193,7 +193,7 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
}
/** {@inheritDoc} */
- public void drawImage(String uri, Rectangle rect) throws IFException {
+ public void drawImage(String uri, Rectangle rect, String ptr) throws IFException {
try {
establish(MODE_NORMAL);
@@ -243,7 +243,7 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
}
/** {@inheritDoc} */
- public void drawImage(Document doc, Rectangle rect) throws IFException {
+ public void drawImage(Document doc, Rectangle rect, String ptr) throws IFException {
try {
establish(MODE_NORMAL);
@@ -325,8 +325,10 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
}
/** {@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, String ptr) throws IFException {
+ //Note: ptr is ignored as it is only needed for accessibility
try {
establish(MODE_TEXT);
AttributesImpl atts = new AttributesImpl();