Index: src/java/META-INF/services/org.apache.fop.fo.ElementMapping =================================================================== --- src/java/META-INF/services/org.apache.fop.fo.ElementMapping (revision 736273) +++ src/java/META-INF/services/org.apache.fop.fo.ElementMapping (working copy) @@ -2,6 +2,7 @@ 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 Index: src/java/org/apache/fop/accessibility/TransformerNode.java =================================================================== --- src/java/org/apache/fop/accessibility/TransformerNode.java (revision 0) +++ src/java/org/apache/fop/accessibility/TransformerNode.java (revision 0) @@ -0,0 +1,341 @@ +/* + * 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.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.apache.fop.apps.FOPException; + 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; + + /** + * Used for accessibility to run required xslt transforms + */ + public class TransformerNode extends DefaultHandler { + + private TransformerHandler mTranHandler; + + /** + * 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(new StringReader(xsltText)); + StreamSource ss = new StreamSource(xsltFile); + mTranHandler = saxTFactory.newTransformerHandler(ss); + SAXResult saxResult = new SAXResult(); + saxResult.setHandler(downstreamHandler); + mTranHandler.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); + mTranHandler = saxTFactory.newTransformerHandler(ss); + mTranHandler.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); + // StreamSource ss = new StreamSource(xsltFile); + mTranHandler = saxTFactory.newTransformerHandler(xsltFile); + } 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) { + mTranHandler.setResult(result); + } + + + + /******************** start of ContentHandler ***************************/ + /** + * @param locator - the locator + */ + public void setDocumentLocator(Locator locator) { + if (mTranHandler != null) { + mTranHandler.setDocumentLocator(locator); + } + } + + /** + * handle startDocument event + * @throws SAXException - if parser fails + */ + public void startDocument() + throws SAXException { + if (mTranHandler != null) { + mTranHandler.startDocument(); + } + } + + /** + * handle endDocument event + * @throws SAXException - if parser fails + */ + public void endDocument() + throws SAXException { + if (mTranHandler != null) { + mTranHandler.endDocument(); + } + } + + /** Processing instruction. + * @param target - of processing instruction + * @param data - of processing instruction + * @throws SAXException - if parser fails + */ + public void processingInstruction(String target, String data) + throws SAXException { + if (mTranHandler != null) { + mTranHandler.processingInstruction(target, data); + } + } + + /** + * Start element. + * This one is usually overridden for special processing as well as endElement + * @param uri - the uri + * @param local - the local + * @param raw - the raw version + * @param attrs - the attributes of the element + * @throws SAXException - if parser fails + */ + public void startElement(String uri, String local, String raw, + Attributes attrs) throws SAXException { + AttributesImpl ai = new AttributesImpl(attrs); + if (mTranHandler != null) { + mTranHandler.startElement(uri, local, raw, ai); + } + } + + /** + * Characters. + * @param ch the characters + * @param start - start index + * @param length - the length of ch + * + * @throws SAXException - if parser fails + */ + public void characters(char[] ch, int start, int length) + throws SAXException { + if (mTranHandler != null) { + mTranHandler.characters(ch, start, length); + } + } + + /** + * Ignorable whitespace. + * @param ch the characters + * @param start - start index + * @param length - the length of ch + * @throws SAXException - if parser fails + */ + public void ignorableWhitespace(char[] ch, int start, int length) + throws SAXException { + if (mTranHandler != null) { + mTranHandler.ignorableWhitespace(ch, start, length); + } + } + + /** + * End element. + * @param uri - the uri + * @param local - the local + * @param raw - the raw version + * @throws SAXException - if parser fails + */ + + public void endElement(String uri, String local, String raw) + throws SAXException { + if (mTranHandler != null) { + mTranHandler.endElement(uri, local, raw); + } + } + + /** + * @param string - to be skipped + * @throws SAXException - if parser fails + */ + public void skippedEntity(String string) + throws SAXException { + if (mTranHandler != null) { + mTranHandler.skippedEntity(string); + } + } + + /** + * @param string - param1 + * @param string1 - param2 + * @throws SAXException - if parser fails + */ + public void startPrefixMapping(String string, String string1) + throws SAXException { + if (mTranHandler != null) { + mTranHandler.startPrefixMapping(string, string1); + } + } + + /** + * @param string - param1 + * @throws SAXException - if parser fails + */ + public void endPrefixMapping(String string) + throws SAXException { + if (mTranHandler != null) { + mTranHandler.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 (mTranHandler != null) { + mTranHandler.startDTD(name, pid, lid); + } + } + + /** + * End of DTD + * @throws SAXException - if parser fails + */ + public void endDTD() + throws SAXException { + if (mTranHandler != null) { + mTranHandler.endDTD(); + } + } + + /** + * startEnitity. + * @param string - param 1 + * @throws SAXException - if parser fails + */ + public void startEntity(String string) + throws SAXException { + if (mTranHandler != null) { + mTranHandler.startEntity(string); + } + } + + /** + * end Entity + * @param string - param 1 + * @throws SAXException - if paser fails + */ + public void endEntity(String string) + throws SAXException { + if (mTranHandler != null) { + mTranHandler.endEntity(string); + } + } + + /** + * Start of CDATA section + * @throws SAXException - parser fails + */ + public void startCDATA() + throws SAXException { + if (mTranHandler != null) { + mTranHandler.startCDATA(); + } + } + + /** + * endCDATA section + * @throws SAXException - if paser fails + */ + public void endCDATA() + throws SAXException { + if (mTranHandler != null) { + mTranHandler.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 (mTranHandler != null) { + mTranHandler.comment(charArray, int1, int2); + } + } + + /******************** End of Lexical Handler ***********************/ + } Index: src/java/org/apache/fop/accessibility/TransformerNodeEndProcessing.java =================================================================== --- src/java/org/apache/fop/accessibility/TransformerNodeEndProcessing.java (revision 0) +++ src/java/org/apache/fop/accessibility/TransformerNodeEndProcessing.java (revision 0) @@ -0,0 +1,103 @@ +/* + * 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.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * An extension of TransformerNode used to run 2nd transform after completion of first + * + */ +public class TransformerNodeEndProcessing extends TransformerNode { + private final ByteArrayOutputStream mBaosBuf = new ByteArrayOutputStream(); + private DefaultHandler mFopHandler = null; + private final FOUserAgent userAgent; + + /** + * 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); + mFopHandler = fopHandler; + this.userAgent = userAgent; + Result res1 = new StreamResult(mBaosBuf); + super.initResult(res1); + } + + /** + * @throws SAXException + * if the parser fails + */ + public void endDocument() throws SAXException { + super.endDocument(); + // do the second tranform to struct + Source xsltFile = new + StreamSource(getClass().getResource("reduceFOTree.xsl").toExternalForm()); + TransformerFactory factory = TransformerFactory.newInstance(); + try { + Transformer transformer = factory.newTransformer(xsltFile); + Source src = new StreamSource(new ByteArrayInputStream(mBaosBuf + .toByteArray())); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Result res = new StreamResult(out); + userAgent.putResult(out); + transformer.transform(src, res); + SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); + saxParserFactory.setNamespaceAware(true); + saxParserFactory.setValidating(false); + SAXParser saxParser = saxParserFactory.newSAXParser(); + InputStream in = new ByteArrayInputStream(mBaosBuf.toByteArray()); + saxParser.parse(in, mFopHandler); + } catch (Exception e) { + // TODO Auto-generated catch block + throw new SAXException(e); + } + + } + +} Index: src/java/org/apache/fop/accessibility/addPtr.xsl =================================================================== --- src/java/org/apache/fop/accessibility/addPtr.xsl (revision 0) +++ src/java/org/apache/fop/accessibility/addPtr.xsl (revision 0) @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file Index: src/java/org/apache/fop/accessibility/reduceFOTree.xsl =================================================================== --- src/java/org/apache/fop/accessibility/reduceFOTree.xsl (revision 0) +++ src/java/org/apache/fop/accessibility/reduceFOTree.xsl (revision 0) @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: src/java/org/apache/fop/apps/FOUserAgent.java =================================================================== --- src/java/org/apache/fop/apps/FOUserAgent.java (revision 742684) +++ src/java/org/apache/fop/apps/FOUserAgent.java (working copy) @@ -20,6 +20,7 @@ package org.apache.fop.apps; // Java +import java.io.ByteArrayOutputStream; import java.io.File; import java.util.Date; import java.util.Map; @@ -76,7 +77,7 @@ private static Log log = LogFactory.getLog("FOP"); - private FopFactory factory; + private final FopFactory factory; /** * The base URL for all URL resolutions, especially for @@ -91,13 +92,14 @@ private URIResolver uriResolver = null; private float targetResolution = FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION; - private Map rendererOptions = new java.util.HashMap(); + private final Map rendererOptions = new java.util.HashMap(); private File outputFile = null; private IFDocumentHandler documentHandlerOverride = null; private Renderer rendererOverride = null; private FOEventHandler foEventHandlerOverride = null; private boolean locatorEnabled = true; // true by default (for error messages). - private EventBroadcaster eventBroadcaster = new FOPEventBroadcaster(); + private final EventBroadcaster eventBroadcaster = new FOPEventBroadcaster(); + private ByteArrayOutputStream reducedFOTree; // accessibility: reduced FO /** Producer: Metadata element for the system/software that produces * the document. (Some renderers can store this in the document.) @@ -123,7 +125,7 @@ /** Set of keywords applicable to this document. */ protected String keywords = null; - private ImageSessionContext imageSessionContext = new AbstractImageSessionContext() { + private final ImageSessionContext imageSessionContext = new AbstractImageSessionContext() { public ImageContext getParentContext() { return getFactory(); @@ -153,6 +155,9 @@ 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 +202,7 @@ return rendererOverride; } + /** * Sets an explicit FOEventHandler instance which overrides the one * defined by the render type setting. @@ -615,5 +621,33 @@ } + /** + * check if accessibility is enabled + * @return boolean + */ + public boolean accessibilityEnabled() { + Object obj = this.getRendererOptions().get("accessibility"); + if (obj == null) { + obj = Boolean.FALSE; + } + return ((Boolean) obj).booleanValue(); + //return ((Boolean)this.getRendererOptions().get("accessibility")).booleanValue(); + } + + /** + * Used for accessibility, store result from 2nd transform for later use + * @param res the result from 2nd transform + */ + public void putResult(ByteArrayOutputStream res) { + this.reducedFOTree = res; + } + + /** + * Used for accessibility + * @return result from 2nd transform as byte array + */ + public byte[] getResultAsByteArray() { + return this.reducedFOTree.toByteArray(); + } } Index: src/java/org/apache/fop/apps/Fop.java =================================================================== --- src/java/org/apache/fop/apps/Fop.java (revision 742684) +++ src/java/org/apache/fop/apps/Fop.java (working copy) @@ -19,33 +19,36 @@ package org.apache.fop.apps; -// Java import java.io.OutputStream; +import javax.xml.transform.Source; +import javax.xml.transform.stream.StreamSource; + import org.xml.sax.helpers.DefaultHandler; - import org.apache.fop.fo.FOTreeBuilder; +import org.apache.fop.accessibility.TransformerNodeEndProcessing; /** * Primary class that activates the FOP process for embedded usage. *

- * JAXP is the standard method of embedding FOP in Java programs. - * Please check our - * embedding page - * for samples (these are also available within the distribution in + * JAXP is the standard method of embedding FOP in Java programs. Please check + * our embedding + * page for samples (these are also available within the distribution in * FOP_DIR\examples\embedding) *

* Methods within FOUserAgent are available to customize portions of the - * process. For example, a specific Renderer object can be specified, - * also ElementMappings which determine elements in the FO that can be - * processed) can be added. + * process. For example, a specific Renderer object can be specified, also + * ElementMappings which determine elements in the FO that can be processed) can + * be added. *

- * At the moment, it is recommended not to reuse an instance of this - * class for more than one rendering run. + * At the moment, it is recommended not to reuse an instance of this class for + * more than one rendering run. */ public class Fop { - // desired output format: MIME type such as "application/pdf", "application/postscript" etc. + // desired output format: MIME type such as "application/pdf", + // "application/postscript" etc. private String outputFormat = null; // output stream to send results to @@ -57,15 +60,24 @@ // FOTreeBuilder object to maintain reference for access to results private FOTreeBuilder foTreeBuilder = null; + + /** - * Constructor for use with already-created FOUserAgents. It uses MIME types to select the - * output format (ex. "application/pdf" for PDF). - * @param outputFormat the MIME type of the output format to use (ex. "application/pdf"). - * @param ua FOUserAgent object - * @param stream the output stream - * @throws FOPException if setting up the DefaultHandler fails + * Constructor for use with already-created FOUserAgents. It uses MIME types + * to select the output format (ex. "application/pdf" for PDF). + * + * @param outputFormat + * the MIME type of the output format to use (ex. + * "application/pdf"). + * @param ua + * FOUserAgent object + * @param stream + * the output stream + * @throws FOPException + * if setting up the DefaultHandler fails */ - Fop(String outputFormat, FOUserAgent ua, OutputStream stream) throws FOPException { + Fop(String outputFormat, FOUserAgent ua, OutputStream stream) + throws FOPException { this.outputFormat = outputFormat; foUserAgent = ua; @@ -79,7 +91,9 @@ } /** - * Get the FOUserAgent instance associated with the rendering run represented by this instance. + * Get the FOUserAgent instance associated with the rendering run + * represented by this instance. + * * @return the user agent */ public FOUserAgent getUserAgent() { @@ -87,40 +101,59 @@ } /** - * Creates a DefaultHandler object used to generate the document. - * Note this object implements the ContentHandler interface. - * For processing with a Transformer object, this DefaultHandler object - * can be used in the SAXResult constructor. - * Alternatively, for processing with a SAXParser, this object can be - * used as the DefaultHandler argument to its parse() methods. + * Creates a DefaultHandler object used to generate the document. Note this + * object implements the ContentHandler interface. For processing with a + * Transformer object, this DefaultHandler object can be used in the + * SAXResult constructor. Alternatively, for processing with a SAXParser, + * this object can be used as the DefaultHandler argument to its parse() + * methods. * - * @throws FOPException if setting up the DefaultHandler fails + * @throws FOPException + * if setting up the DefaultHandler fails */ private void createDefaultHandler() throws FOPException { - this.foTreeBuilder = new FOTreeBuilder(outputFormat, foUserAgent, stream); + this.foTreeBuilder = new FOTreeBuilder(outputFormat, foUserAgent, + stream); } + + + /** - * Returns the DefaultHandler object that will receive the SAX stream containing the - * FO document to be rendered. + * Returns the DefaultHandler object that will receive the SAX stream + * containing the FO document to be rendered. + * * @return the SAX DefaultHandler for handling the SAX events. - * @throws FOPException if setting up the DefaultHandler fails + * @throws FOPException + * if setting up the DefaultHandler fails */ public DefaultHandler getDefaultHandler() throws FOPException { + DefaultHandler fopHandler = null; + if (foTreeBuilder == null) { createDefaultHandler(); } - return this.foTreeBuilder; + fopHandler = this.foTreeBuilder; + if (this.foUserAgent.accessibilityEnabled()) { + Source xsltFile = new + StreamSource(getClass().getResource("../accessibility/addPtr.xsl").toExternalForm()); + DefaultHandler transformNode = new + TransformerNodeEndProcessing(xsltFile, fopHandler, foUserAgent); + return transformNode; + } else { + return fopHandler; + } } /** - * Returns the results of the rendering process. Information includes - * the total number of pages generated and the number of pages per + * Returns the results of the rendering process. Information includes the + * total number of pages generated and the number of pages per * page-sequence. Call this method only after the rendering process is * finished. Note that the results are only available for output formats * which make use of FOP's layout engine (PDF, PS, etc.). + * * @return the results of the rendering process, or null for flow-oriented - * output formats like RTF and MIF. + * output formats like RTF and MIF. */ public FormattingResults getResults() { if (foTreeBuilder == null) { Index: src/java/org/apache/fop/apps/FopFactory.java =================================================================== --- src/java/org/apache/fop/apps/FopFactory.java (revision 742684) +++ src/java/org/apache/fop/apps/FopFactory.java (working copy) @@ -99,6 +99,11 @@ * 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 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. *

Index: src/java/org/apache/fop/apps/FopFactoryConfigurator.java =================================================================== --- src/java/org/apache/fop/apps/FopFactoryConfigurator.java (revision 742684) +++ src/java/org/apache/fop/apps/FopFactoryConfigurator.java (working copy) @@ -90,6 +90,21 @@ log.debug("Initializing FopFactory Configuration"); } + if (cfg.getChild("accessibility", false) != null) { + try { + String s = cfg.getChild("accessibility").getValue(); + if ("true".equals(s)) { + this.factory.setAccessibility(true); + } else if ("false".equals(s)) { + this.factory.setAccessibility(false); + } else { + throw new FOPException("Invalid value for accessibility"); + } + } catch (ConfigurationException e) { + throw new FOPException(e); + } + } + // strict configuration if (cfg.getChild("strict-configuration", false) != null) { try { Index: src/java/org/apache/fop/area/AreaTreeParser.java =================================================================== --- src/java/org/apache/fop/area/AreaTreeParser.java (revision 742684) +++ src/java/org/apache/fop/area/AreaTreeParser.java (working copy) @@ -838,7 +838,8 @@ public void startElement(Attributes attributes) { String url = attributes.getValue("url"); - Image image = new Image(url); + String ptr = attributes.getValue("ptr"); + Image image = new Image(url, ptr); transferForeignObjects(attributes, image); setAreaAttributes(attributes, image); setTraits(attributes, image, SUBSET_COMMON); Index: src/java/org/apache/fop/area/Trait.java =================================================================== --- src/java/org/apache/fop/area/Trait.java (revision 742684) +++ src/java/org/apache/fop/area/Trait.java (working copy) @@ -194,12 +194,16 @@ 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]; + private static class TraitInfo { private String name; private Class clazz; // Class of trait data @@ -225,6 +229,7 @@ static { // Create a hashmap mapping trait code to name for external representation //put(ID_LINK, new TraitInfo("id-link", String.class)); + put(PTR, new TraitInfo("ptr", String.class)); put(INTERNAL_LINK, new TraitInfo("internal-link", InternalLink.class)); put(EXTERNAL_LINK, new TraitInfo("external-link", ExternalLink.class)); put(FONT, new TraitInfo("font", FontTriplet.class)); @@ -277,7 +282,6 @@ new TraitInfo("is-reference-area", Boolean.class)); put(IS_VIEWPORT_AREA, new TraitInfo("is-viewport-area", Boolean.class)); - } /** Index: src/java/org/apache/fop/area/inline/Image.java =================================================================== --- src/java/org/apache/fop/area/inline/Image.java (revision 742684) +++ src/java/org/apache/fop/area/inline/Image.java (working copy) @@ -28,14 +28,17 @@ */ public class Image extends Area { private String url; - + private String ptr; // used for accessibility + /** * Create a new image with the given url. * * @param url the url of the image + * @param ptr used for accessibility */ - public Image(String url) { + public Image(String url, String ptr) { this.url = url; + this.ptr = ptr; } /** @@ -47,5 +50,14 @@ public String getURL() { return this.url; } + + /** + * Used for accessibility + * @return ptr of this image + */ + public String getPtr() { + return ptr; + } + } Index: src/java/org/apache/fop/cli/CommandLineOptions.java =================================================================== --- src/java/org/apache/fop/cli/CommandLineOptions.java (revision 742684) +++ src/java/org/apache/fop/cli/CommandLineOptions.java (working copy) @@ -327,6 +327,8 @@ i = i + parseAreaTreeOption(args, i); } else if (args[i].equals("-if")) { i = i + parseIntermediateFormatOption(args, i); + } else if (args[i].equals("-a")) { + this.renderingOptions.put("accessibility", Boolean.TRUE); } else if (args[i].equals("-v")) { printVersion(); return false; @@ -445,7 +447,8 @@ } private int parsePDFOutputOption(String[] args, int i, String pdfAMode) throws FOPException { - setOutputMode(MimeConstants.MIME_PDF); +// setOutputMode(MimeConstants.MIME_PDF); + setOutputMode(MimeConstants.MIME_PDF + ";mode=painter"); if ((i + 1 == args.length) || (isOption(args[i + 1]))) { throw new FOPException("you must specify the PDF output file"); @@ -1129,6 +1132,7 @@ + " -nocopy PDF file will be encrypted without copy content permission\n" + " -noedit PDF file will be encrypted without edit content permission\n" + " -noannotations PDF file will be encrypted without edit annotation permission\n" + + " -a enables PDF accessibility (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" Index: src/java/org/apache/fop/events/EventFormatter.xml =================================================================== --- src/java/org/apache/fop/events/EventFormatter.xml (revision 742684) +++ src/java/org/apache/fop/events/EventFormatter.xml (working copy) @@ -1,4 +1,5 @@ - +--> + + [ (See position {loc})| (See {#gatherContextInfo})| (No context info available)] An fo:marker is permitted only as the descendant of an fo:flow. An fo:retrieve-marker is permitted only as the descendant of an fo:static-content. Index: src/java/org/apache/fop/fo/Constants.java =================================================================== --- src/java/org/apache/fop/fo/Constants.java (revision 742684) +++ src/java/org/apache/fop/fo/Constants.java (working copy) @@ -771,8 +771,14 @@ * 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: alt text for fo:external-graphic, + * used for accessibility */ + int PR_X_ALT = 275; + /** Number of property constants defined */ - int PROPERTY_COUNT = 273; + int PROPERTY_COUNT = 275; // compound property constants Index: src/java/org/apache/fop/fo/FONode.java =================================================================== --- src/java/org/apache/fop/fo/FONode.java (revision 742684) +++ src/java/org/apache/fop/fo/FONode.java (working copy) @@ -36,7 +36,9 @@ 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.extensions.InternalElementMapping; import org.apache.fop.fo.pagination.Root; import org.apache.fop.util.CharUtilities; import org.apache.fop.util.ContentHandlerFactory; @@ -49,6 +51,7 @@ /** the XSL-FO namespace URI */ protected static final String FO_URI = FOElementMapping.URI; + /** FOP's proprietary extension namespace URI */ protected static final String FOX_URI = ExtensionElementMapping.URI; @@ -419,6 +422,8 @@ return "fo:" + localName; } else if (namespaceURI.equals(ExtensionElementMapping.URI)) { return "fox:" + localName; + } else if (namespaceURI.equals(InternalElementMapping.URI)) { + return "foi:" + localName; // used FOP internally for accessibility } else if (namespaceURI.equals(SVGElementMapping.URI)) { return "svg:" + localName; } else { Index: src/java/org/apache/fop/fo/FOPropertyMapping.java =================================================================== --- src/java/org/apache/fop/fo/FOPropertyMapping.java (revision 742684) +++ src/java/org/apache/fop/fo/FOPropertyMapping.java (working copy) @@ -1074,6 +1074,7 @@ m.addShorthand(s_generics[PR_XML_LANG]); addPropertyMaker("language", m); + // script m = new StringProperty.Maker(PR_SCRIPT); m.setInherited(true); @@ -2514,6 +2515,18 @@ m.setInherited(false); 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); + m.setInherited(false); + m.setDefault(""); + addPropertyMaker("fox:alt-text", m); // provisional-label-separation m = new LengthProperty.Maker(PR_PROVISIONAL_LABEL_SEPARATION); Index: src/java/org/apache/fop/fo/FOTreeBuilder.java =================================================================== --- src/java/org/apache/fop/fo/FOTreeBuilder.java (revision 742684) +++ src/java/org/apache/fop/fo/FOTreeBuilder.java (working copy) @@ -39,6 +39,7 @@ import org.apache.fop.area.AreaTreeHandler; import org.apache.fop.fo.ElementMapping.Maker; import org.apache.fop.fo.extensions.ExtensionElementMapping; +//import org.apache.fop.fo.extensions.InternalElementMapping; import org.apache.fop.fo.pagination.Root; import org.apache.fop.util.ContentHandlerFactory; import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener; @@ -97,7 +98,6 @@ FOUserAgent foUserAgent, OutputStream stream) throws FOPException { - this.userAgent = foUserAgent; this.elementMappingRegistry = userAgent.getFactory().getElementMappingRegistry(); //This creates either an AreaTreeHandler and ultimately a Renderer, or @@ -263,7 +263,8 @@ } } else { // check that incoming node is valid for currentFObj if (currentFObj.getNamespaceURI().equals(FOElementMapping.URI) - || currentFObj.getNamespaceURI().equals(ExtensionElementMapping.URI)) { + || currentFObj.getNamespaceURI().equals(ExtensionElementMapping.URI) + /*|| currentFObj.getNamespaceURI().equals(InternalElementMapping.URI)*/ ) { currentFObj.validateChildNode(locator, namespaceURI, localName); } } Index: src/java/org/apache/fop/fo/FObj.java =================================================================== --- src/java/org/apache/fop/fo/FObj.java (revision 742684) +++ src/java/org/apache/fop/fo/FObj.java (working copy) @@ -33,6 +33,7 @@ import org.apache.xmlgraphics.util.QName; import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.FONode.FONodeIterator; import org.apache.fop.fo.extensions.ExtensionAttachment; import org.apache.fop.fo.flow.Marker; import org.apache.fop.fo.properties.PropertyMaker; Index: src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java =================================================================== --- src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java (revision 742684) +++ src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java (working copy) @@ -46,6 +46,8 @@ propertyAttributes.add("orphan-content-limit"); propertyAttributes.add("internal-destination"); propertyAttributes.add("disable-column-balancing"); + //These are FOP's extension properties for accessibility + propertyAttributes.add("alt"); } /** Index: src/java/org/apache/fop/fo/extensions/ExternalDocument.java =================================================================== --- src/java/org/apache/fop/fo/extensions/ExternalDocument.java (revision 742684) +++ src/java/org/apache/fop/fo/extensions/ExternalDocument.java (working copy) @@ -31,7 +31,7 @@ import org.apache.fop.fo.properties.LengthRangeProperty; /** - * Class for the fox:external-document extenstion element. + * Class for the fox:external-document extension element. */ public class ExternalDocument extends AbstractPageSequence implements GraphicsProperties { Index: src/java/org/apache/fop/fo/extensions/InternalElementMapping.java =================================================================== --- src/java/org/apache/fop/fo/extensions/InternalElementMapping.java (revision 0) +++ src/java/org/apache/fop/fo/extensions/InternalElementMapping.java (revision 0) @@ -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: InternalElementMapping.java 679781 2008-07-25 12:44:20Z jeremias $ */ + +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()); + } + +} Index: src/java/org/apache/fop/fo/flow/AbstractGraphics.java =================================================================== --- src/java/org/apache/fop/fo/flow/AbstractGraphics.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/AbstractGraphics.java (working copy) @@ -60,6 +60,7 @@ private int scaling; private int textAlign; private Length width; + private String ptr; // used for accessibility // Unused but valid items, commented out for performance: // private CommonAccessibility commonAccessibility; // private CommonAural commonAural; @@ -94,6 +95,7 @@ dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum(); height = pList.get(PR_HEIGHT).getLength(); id = pList.get(PR_ID).getString(); + ptr = pList.get(PR_X_PTR).getString(); // used for accessibility inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange(); keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep(); keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep(); @@ -104,6 +106,12 @@ width = pList.get(PR_WIDTH).getLength(); } + /** @return the "foi:ptr" property. */ + public String getPtr() { + return ptr; + } + + /** * @return the "id" property. */ Index: src/java/org/apache/fop/fo/flow/AbstractListItemPart.java =================================================================== --- src/java/org/apache/fop/fo/flow/AbstractListItemPart.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/AbstractListItemPart.java (working copy) @@ -90,5 +90,7 @@ public KeepProperty getKeepTogether() { return keepTogether; } + + } Index: src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java =================================================================== --- src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/AbstractPageNumberCitation.java (working copy) @@ -51,6 +51,7 @@ private int alignmentBaseline; private Length baselineShift; private int dominantBaseline; + private String ptr; // used for accessibility // private ToBeImplementedProperty letterSpacing; private SpaceProperty lineHeight; private String refId; @@ -96,6 +97,7 @@ dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum(); // letterSpacing = pList.get(PR_LETTER_SPACING); lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); + ptr = pList.get(PR_X_PTR).getString(); // used for accessibility refId = pList.get(PR_REF_ID).getString(); textDecoration = pList.getTextDecorationProps(); // textShadow = pList.get(PR_TEXT_SHADOW); @@ -138,6 +140,11 @@ return textDecoration; } + /** @return the "foi:ptr" property. */ + public String getPtr() { + return ptr; + } + /** @return the "alignment-adjust" property */ public Length getAlignmentAdjust() { return alignmentAdjust; Index: src/java/org/apache/fop/fo/flow/BasicLink.java =================================================================== --- src/java/org/apache/fop/fo/flow/BasicLink.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/BasicLink.java (working copy) @@ -65,6 +65,7 @@ /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); + // destinationPlacementOffset = pList.get(PR_DESTINATION_PLACEMENT_OFFSET); externalDestination = pList.get(PR_EXTERNAL_DESTINATION).getString(); // indicateDestination = pList.get(PR_INDICATE_DESTINATION); @@ -83,6 +84,11 @@ } } + /** @return the "fox:ptr" property. */ + public String getPtr() { + return super.getPtr(); + } + /** {@inheritDoc} */ protected void startOfNode() throws FOPException { super.startOfNode(); Index: src/java/org/apache/fop/fo/flow/Block.java =================================================================== --- src/java/org/apache/fop/fo/flow/Block.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/Block.java (working copy) @@ -71,6 +71,7 @@ private int lineHeightShiftAdjustment; private int lineStackingStrategy; private Numeric orphans; + private String ptr; //used for accessibility private int whiteSpaceTreatment; private int span; private int textAlign; @@ -80,6 +81,7 @@ private Numeric widows; private int wrapOption; private int disableColumnBalancing; + private String xmlLang; // Unused but valid items, commented out for performance: // private CommonAccessibility commonAccessibility; // private CommonAural commonAural; @@ -122,6 +124,7 @@ lineHeightShiftAdjustment = pList.get(PR_LINE_HEIGHT_SHIFT_ADJUSTMENT).getEnum(); lineStackingStrategy = pList.get(PR_LINE_STACKING_STRATEGY).getEnum(); orphans = pList.get(PR_ORPHANS).getNumeric(); + ptr = pList.get(PR_X_PTR).getString(); //used for accessibility whiteSpaceTreatment = pList.get(PR_WHITE_SPACE_TREATMENT).getEnum(); span = pList.get(PR_SPAN).getEnum(); textAlign = pList.get(PR_TEXT_ALIGN).getEnum(); @@ -131,6 +134,7 @@ widows = pList.get(PR_WIDOWS).getNumeric(); wrapOption = pList.get(PR_WRAP_OPTION).getEnum(); disableColumnBalancing = pList.get(PR_X_DISABLE_COLUMN_BALANCING).getEnum(); + xmlLang = pList.get(PR_XML_LANG).getString(); } /** {@inheritDoc} */ @@ -171,6 +175,17 @@ return breakAfter; } + /** @return the "foi:ptr" property. */ + public String getPtr() { + return ptr; + } + + + /** @return the "xml:lang" property. */ + public String getXmlLang() { + return xmlLang; + } + /** @return the "break-before" property. */ public int getBreakBefore() { return breakBefore; Index: src/java/org/apache/fop/fo/flow/BlockContainer.java =================================================================== --- src/java/org/apache/fop/fo/flow/BlockContainer.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/BlockContainer.java (working copy) @@ -101,7 +101,7 @@ super.startOfNode(); getFOEventHandler().startBlockContainer(this); } - + /** * {@inheritDoc} *
XSL Content Model: marker* (%block;)+ Index: src/java/org/apache/fop/fo/flow/Character.java =================================================================== --- src/java/org/apache/fop/fo/flow/Character.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/Character.java (working copy) @@ -62,6 +62,7 @@ private CommonTextDecoration textDecoration; // private ToBeImplementedProperty textShadow; private Property wordSpacing; + private String ptr; // used for accessibility // Unused but valid items, commented out for performance: // private CommonAural commonAural; // private CommonMarginInline commonMarginInline; @@ -108,8 +109,14 @@ 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 } - + + /** @return the "fox:ptr" property. */ + public String getPtr() { + return ptr; + } + /** {@inheritDoc} */ protected void startOfNode() throws FOPException { super.startOfNode(); Index: src/java/org/apache/fop/fo/flow/Footnote.java =================================================================== --- src/java/org/apache/fop/fo/flow/Footnote.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/Footnote.java (working copy) @@ -53,7 +53,7 @@ public void bind(PropertyList pList) throws FOPException { // No active properties -> do nothing. } - + /** {@inheritDoc} */ protected void startOfNode() throws FOPException { getFOEventHandler().startFootnote(this); Index: src/java/org/apache/fop/fo/flow/FootnoteBody.java =================================================================== --- src/java/org/apache/fop/fo/flow/FootnoteBody.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/FootnoteBody.java (working copy) @@ -33,10 +33,11 @@ * fo:footnote-body object. */ public class FootnoteBody extends FObj { - // The value of properties relevant for fo:footnote-body (commented out for perforance). + // The value of properties relevant for fo:footnote-body (commented out for performance). // private CommonAccessibility commonAccessibility; // End of property values + /** * Base constructor * @@ -49,7 +50,9 @@ /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { } + + /** {@inheritDoc} */ protected void startOfNode() throws FOPException { getFOEventHandler().startFootnoteBody(this); Index: src/java/org/apache/fop/fo/flow/Inline.java =================================================================== --- src/java/org/apache/fop/fo/flow/Inline.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/Inline.java (working copy) @@ -37,6 +37,7 @@ 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 @@ 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} */ @@ -149,6 +151,11 @@ return "inline"; } + /** @return the "foi:ptr" property. */ + public String getPtr() { + return ptr; + } + /** * {@inheritDoc} * @return {@link org.apache.fop.fo.Constants#FO_INLINE} Index: src/java/org/apache/fop/fo/flow/ListBlock.java =================================================================== --- src/java/org/apache/fop/fo/flow/ListBlock.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/ListBlock.java (working copy) @@ -132,7 +132,7 @@ public CommonBorderPaddingBackground getCommonBorderPaddingBackground() { return commonBorderPaddingBackground; } - + /** @return the "break-after" property */ public int getBreakAfter() { return breakAfter; Index: src/java/org/apache/fop/fo/flow/ListItem.java =================================================================== --- src/java/org/apache/fop/fo/flow/ListItem.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/ListItem.java (working copy) @@ -138,7 +138,7 @@ public CommonMarginBlock getCommonMarginBlock() { return commonMarginBlock; } - + /** @return the {@link CommonBorderPaddingBackground} */ public CommonBorderPaddingBackground getCommonBorderPaddingBackground() { return commonBorderPaddingBackground; Index: src/java/org/apache/fop/fo/flow/Marker.java =================================================================== --- src/java/org/apache/fop/fo/flow/Marker.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/Marker.java (working copy) @@ -19,7 +19,6 @@ package org.apache.fop.fo.flow; -import java.util.Collections; import java.util.Map; import org.xml.sax.Attributes; @@ -65,12 +64,11 @@ } markerClassName = pList.get(PR_MARKER_CLASS_NAME).getString(); - if (markerClassName == null || markerClassName.equals("")) { missingPropertyError("marker-class-name"); } } - + /** * Retrieve the property list of the given {@link FONode} * descendant Index: src/java/org/apache/fop/fo/flow/PageNumber.java =================================================================== --- src/java/org/apache/fop/fo/flow/PageNumber.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/PageNumber.java (working copy) @@ -47,6 +47,7 @@ private int alignmentBaseline; private Length baselineShift; private int dominantBaseline; + private String ptr; // used for accessibility // private ToBeImplementedProperty letterSpacing; private SpaceProperty lineHeight; /** Holds the text decoration values. May be null */ @@ -92,6 +93,7 @@ // letterSpacing = pList.get(PR_LETTER_SPACING); lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); textDecoration = pList.getTextDecorationProps(); + ptr = pList.get(PR_X_PTR).getString(); // used for accessibility // textShadow = pList.get(PR_TEXT_SHADOW); // implicit properties @@ -109,6 +111,11 @@ getFOEventHandler().endPageNumber(this); } + /** @return the "foi:ptr" property. */ + public String getPtr() { + return ptr; + } + /** * {@inheritDoc} *
XSL Content Model: empty Index: src/java/org/apache/fop/fo/flow/Wrapper.java =================================================================== --- src/java/org/apache/fop/fo/flow/Wrapper.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/Wrapper.java (working copy) @@ -26,6 +26,7 @@ import org.apache.fop.fo.FONode; import org.apache.fop.fo.FOText; import org.apache.fop.fo.FObjMixed; +import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; /** @@ -51,6 +52,11 @@ super(parent); } + /** {@inheritDoc} */ + public void bind(PropertyList pList) throws FOPException { + super.bind(pList); + } + /** * {@inheritDoc} *
XSL Content Model: marker* (#PCDATA|%inline;|%block;)* Index: src/java/org/apache/fop/fo/flow/table/TableFObj.java =================================================================== --- src/java/org/apache/fop/fo/flow/table/TableFObj.java (revision 742684) +++ src/java/org/apache/fop/fo/flow/table/TableFObj.java (working copy) @@ -46,6 +46,7 @@ private Numeric borderBeforePrecedence; private Numeric borderEndPrecedence; private Numeric borderStartPrecedence; + private String ptr; ConditionalBorder borderBefore; ConditionalBorder borderAfter; @@ -71,6 +72,7 @@ borderBeforePrecedence = pList.get(PR_BORDER_BEFORE_PRECEDENCE).getNumeric(); borderEndPrecedence = pList.get(PR_BORDER_END_PRECEDENCE).getNumeric(); borderStartPrecedence = pList.get(PR_BORDER_START_PRECEDENCE).getNumeric(); + ptr = pList.get(PR_X_PTR).getString(); if (getNameId() != FO_TABLE //Separate check for fo:table in Table.java && getNameId() != FO_TABLE_CELL && getCommonBorderPaddingBackground().hasPadding( @@ -235,6 +237,11 @@ } } + /** @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. Index: src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java (revision 742684) +++ src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java (working copy) @@ -385,6 +385,7 @@ addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); + TraitSetter.addPtr(curBlockArea, getBlockFO().getPtr()); // used for accessibility TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(), effSpaceBefore, effSpaceAfter); flush(); Index: src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java (revision 742684) +++ src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java (working copy) @@ -179,7 +179,7 @@ blockArea.setIPD(imageSize.width); LineArea lineArea = new LineArea(); - Image imageArea = new Image(uri); + Image imageArea = new Image(uri,""); // not supporting accessibility for this extension TraitSetter.setProducerID(imageArea, fobj.getId()); transferForeignAttributes(imageArea); Index: src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java (revision 742684) +++ src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java (working copy) @@ -95,7 +95,7 @@ pageSequenceAreaObject.setLanguage(getPageSequence().getLanguage()); pageSequenceAreaObject.setCountry(getPageSequence().getCountry()); areaTreeModel.startPageSequence(pageSequenceAreaObject); - if (log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("Starting layout"); } Index: src/java/org/apache/fop/layoutmgr/TraitSetter.java =================================================================== --- src/java/org/apache/fop/layoutmgr/TraitSetter.java (revision 742684) +++ src/java/org/apache/fop/layoutmgr/TraitSetter.java (working copy) @@ -584,6 +584,17 @@ } /** + * Adds the ptr trait to the area. + * @param area the area to set the traits on + * @param ptr string + */ + public static void addPtr(Area area, String ptr) { + if (ptr != null && ptr.length() > 0) { + area.addTrait(Trait.PTR, ptr); + } + } + + /** * Sets the producer's ID as a trait on the area. This can be used to track back the * generating FO node. * @param area the area to set the traits on Index: src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java (revision 742684) +++ src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java (working copy) @@ -39,6 +39,7 @@ */ public abstract class AbstractGraphicsLayoutManager extends LeafNodeLayoutManager { + /** * Constructor. * @@ -85,6 +86,7 @@ transferForeignAttributes(viewportArea); Viewport vp = new Viewport(viewportArea); + TraitSetter.addPtr(vp, fobj.getPtr()); // used for accessibility TraitSetter.setProducerID(vp, fobj.getId()); vp.setIPD(imageLayout.getViewportSize().width); vp.setBPD(imageLayout.getViewportSize().height); @@ -102,6 +104,9 @@ return vp; } + + + /** {@inheritDoc} */ public List getNextKnuthElements(LayoutContext context, int alignment) { Index: src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java (revision 742684) +++ src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java (working copy) @@ -138,6 +138,7 @@ text.setBaselineOffset(font.getAscender()); TraitSetter.addFontTraits(text, font); text.addTrait(Trait.COLOR, fobj.getColor()); + TraitSetter.addPtr(text, fobj.getPtr()); // used for accessibility TraitSetter.addTextDecoration(text, fobj.getTextDecoration()); } Index: src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java (revision 742684) +++ src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java (working copy) @@ -56,6 +56,7 @@ 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(); Index: src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java (revision 742684) +++ src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java (working copy) @@ -86,6 +86,7 @@ } TraitSetter.setProducerID(text, node.getId()); TraitSetter.addTextDecoration(text, node.getTextDecoration()); + TraitSetter.addPtr(text, node.getPtr()); // used for accessibility return text; } Index: src/java/org/apache/fop/layoutmgr/inline/ExternalGraphicLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/inline/ExternalGraphicLayoutManager.java (revision 742684) +++ src/java/org/apache/fop/layoutmgr/inline/ExternalGraphicLayoutManager.java (working copy) @@ -41,8 +41,8 @@ } /** {@inheritDoc} */ - protected Area getChildArea() { - return new Image(((ExternalGraphic) fobj).getSrc()); + protected Area getChildArea() { // used for accessibility + return new Image(((ExternalGraphic) fobj).getSrc(), ((ExternalGraphic) fobj).getPtr()); } } Index: src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java (revision 742684) +++ src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java (working copy) @@ -85,9 +85,8 @@ 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; } Index: src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java (revision 742684) +++ src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java (working copy) @@ -27,8 +27,10 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.area.Trait; import org.apache.fop.area.inline.TextArea; +import org.apache.fop.area.Block; // ?? 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,27 @@ } 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); Index: src/java/org/apache/fop/pdf/PDFArray.java =================================================================== --- src/java/org/apache/fop/pdf/PDFArray.java (revision 742684) +++ src/java/org/apache/fop/pdf/PDFArray.java (working copy) @@ -107,6 +107,15 @@ } /** + * Used for accessibility + * @param obj searched + * @return boolean indicating if obj is contained or not + */ + public boolean contains(Object obj) { + return this.values.contains(obj); + } + + /** * Returns the length of the array * @return the length of the array */ Index: src/java/org/apache/fop/pdf/PDFDocument.java =================================================================== --- src/java/org/apache/fop/pdf/PDFDocument.java (revision 742684) +++ src/java/org/apache/fop/pdf/PDFDocument.java (working copy) @@ -75,14 +75,16 @@ /** Integer constant to represent PDF 1.4 */ public static final int PDF_VERSION_1_4 = 4; + /** the encoding to use when converting strings to PDF commands */ + public static final String ENCODING = "ISO-8859-1"; /** the counter for object numbering */ protected int objectcount = 0; /** the logger instance */ - private Log log = LogFactory.getLog("org.apache.fop.pdf"); + private final Log log = LogFactory.getLog("org.apache.fop.pdf"); /** the current character position */ private int position = 0; @@ -91,40 +93,40 @@ private int xref; /** the character position of each object */ - private List location = new ArrayList(); + private final List location = new ArrayList(); /** List of objects to write in the trailer */ - private List trailerObjects = new ArrayList(); + private final List trailerObjects = new ArrayList(); /** the objects themselves */ - private List objects = new LinkedList(); + private final List objects = new LinkedList(); /** Indicates what PDF version is active */ - private int pdfVersion = PDF_VERSION_1_4; + private final int pdfVersion = PDF_VERSION_1_4; /** Indicates which PDF profiles are active (PDF/A, PDF/X etc.) */ - private PDFProfile pdfProfile = new PDFProfile(this); + private final PDFProfile pdfProfile = new PDFProfile(this); /** the /Root object */ - private PDFRoot root; + private final PDFRoot root; /** The root outline object */ private PDFOutline outlineRoot = null; /** The /Pages object (mark-fop@inomial.com) */ - private PDFPages pages; + private final PDFPages pages; /** the /Info object */ - private PDFInfo info; + private final PDFInfo info; /** the /Resources object */ - private PDFResources resources; + private final PDFResources resources; /** the document's encryption, if it exists */ private PDFEncryption encryption; /** the colorspace (0=RGB, 1=CMYK) */ - private PDFDeviceColorSpace colorspace = + private final PDFDeviceColorSpace colorspace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB); /** the counter for Pattern name numbering (e.g. 'Pattern1') */ @@ -138,43 +140,43 @@ /** the {@link PDFXObject}s map */ /* TODO: Should be modified (works only for image subtype) */ - private Map xObjectsMap = new HashMap(); + private final Map xObjectsMap = new HashMap(); /** The {@link PDFFont} map */ - private Map fontMap = new HashMap(); + private final Map fontMap = new HashMap(); /** The {@link PDFFilter} map */ private Map filterMap = new HashMap(); /** List of {@link PDFGState}s. */ - private List gstates = new ArrayList(); + private final List gstates = new ArrayList(); /** List of {@link PDFFunction}s. */ - private List functions = new ArrayList(); + private final List functions = new ArrayList(); /** List of {@link PDFShading}s. */ - private List shadings = new ArrayList(); + private final List shadings = new ArrayList(); /** List of {@link PDFPattern}s. */ - private List patterns = new ArrayList(); + private final List patterns = new ArrayList(); /** List of {@link PDFLink}s. */ - private List links = new ArrayList(); + private final List links = new ArrayList(); /** List of {@link PDFDestination}s. */ private List destinations; /** List of {@link PDFFileSpec}s. */ - private List filespecs = new ArrayList(); + private final List filespecs = new ArrayList(); /** List of {@link PDFGoToRemote}s. */ - private List gotoremotes = new ArrayList(); + private final List gotoremotes = new ArrayList(); /** List of {@link PDFGoTo}s. */ - private List gotos = new ArrayList(); + private final List gotos = new ArrayList(); /** List of {@link PDFLaunch}es. */ - private List launches = new ArrayList(); + private final List launches = new ArrayList(); /** * The PDFDests object for the name dictionary. @@ -182,9 +184,9 @@ */ private PDFDests dests; - private PDFFactory factory; + private final PDFFactory factory; - private boolean encodingOnTheFly = true; + private final boolean encodingOnTheFly = true; /** * Creates an empty PDF document. @@ -206,15 +208,17 @@ this.pages = getFactory().makePages(); // Create the Root object - this.root = getFactory().makeRoot(this.pages); + this.root = getFactory().makeRoot(pages); // Create the Resources object this.resources = getFactory().makeResources(); // Make the /Info record this.info = getFactory().makeInfo(prod); + } + /** * @return the integer representing the active PDF version * (one of PDFDocument.PDF_VERSION_*) @@ -470,7 +474,6 @@ */ public void addTrailerObject(PDFObject obj) { this.trailerObjects.add(obj); - if (obj instanceof PDFGoTo) { this.gotos.add(obj); } @@ -1068,4 +1071,5 @@ return pdfBytes.length; } + } Index: src/java/org/apache/fop/pdf/PDFFactory.java =================================================================== --- src/java/org/apache/fop/pdf/PDFFactory.java (revision 742684) +++ src/java/org/apache/fop/pdf/PDFFactory.java (working copy) @@ -65,9 +65,9 @@ */ public class PDFFactory { - private PDFDocument document; + private final PDFDocument document; - private Log log = LogFactory.getLog(PDFFactory.class); + private final Log log = LogFactory.getLog(PDFFactory.class); /** * Creates a new PDFFactory. @@ -94,6 +94,7 @@ * * @param pages the pages pdf object that the root points to * @return the new pdf root object for this document + * */ public PDFRoot makeRoot(PDFPages pages) { //Make a /Pages object. This object is written in the trailer. @@ -177,19 +178,19 @@ * @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 used for accessibility * * @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, - pageWidth, pageHeight, pageIndex); - + PDFPage page = new PDFPage(resources, // old numPages + pageWidth, pageHeight, pageIndex, currentPageParentKey); getDocument().assignObjectNumber(page); getDocument().getPages().addPage(page); return page; @@ -203,12 +204,14 @@ * @param resources resources object to use * @param pageWidth width of the page in points * @param pageHeight height of the page in points + * @param currentPageParentKey used for accessibility * * @return the created /Page object */ public PDFPage makePage(PDFResources resources, - int pageWidth, int pageHeight) { - return makePage(resources, pageWidth, pageHeight, -1); + int pageWidth, int pageHeight, + int currentPageParentKey) { + return makePage(resources, pageWidth, pageHeight, -1, currentPageParentKey); } /* ========================= functions ================================= */ @@ -868,6 +871,17 @@ } /** + * 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 Index: src/java/org/apache/fop/pdf/PDFLink.java =================================================================== --- src/java/org/apache/fop/pdf/PDFLink.java (revision 742684) +++ src/java/org/apache/fop/pdf/PDFLink.java (working copy) @@ -42,6 +42,7 @@ private float bry; private String color; private PDFAction action; + private String structParent = ""; /** * create objects associated with a link annotation (GoToR) @@ -68,7 +69,16 @@ this.action = action; } + /** + * Used for accessibility + * @param mcid of this structParent + */ + public void setStructParent(int mcid) { + this.structParent = "/StructParent " + String.valueOf(mcid) + "\n"; + } + + /** * {@inheritDoc} */ public String toPDFString() { @@ -87,6 +97,7 @@ + (brx) + " " + (bry) + " ]\n" + "/C [ " + this.color + " ]\n" + "/Border [ 0 0 0 ]\n" + "/A " + this.action.getAction() + "\n" + "/H /I\n" + + this.structParent + fFlag + "\n>>\nendobj\n"; return s; } Index: src/java/org/apache/fop/pdf/PDFPage.java =================================================================== --- src/java/org/apache/fop/pdf/PDFPage.java (revision 742684) +++ src/java/org/apache/fop/pdf/PDFPage.java (working copy) @@ -42,9 +42,10 @@ * @param pageWidth the page's width in points * @param pageHeight the page's height in points * @param pageIndex the page's zero-based index (or -1 if the page number is auto-determined) + * @param structParents used for accessibility */ public PDFPage(PDFResources resources, PDFStream contents, - int pageWidth, int pageHeight, int pageIndex) { + int pageWidth, int pageHeight, int pageIndex, int structParents) { /* generic creation of object */ super(resources); @@ -54,6 +55,10 @@ setContents(contents); setSimplePageSize(pageWidth, pageHeight); this.pageIndex = pageIndex; + if (structParents > -1) { + put("StructParents", structParents); + put("Tabs", new PDFName("S")); + } } /** @@ -63,10 +68,11 @@ * @param pageWidth the page's width in points * @param pageHeight the page's height in points * @param pageIndex the page's zero-based index (or -1 if the page number is auto-determined) + * @param structParents used for accessibility */ public PDFPage(PDFResources resources, - int pageWidth, int pageHeight, int pageIndex) { - this(resources, null, pageWidth, pageHeight, pageIndex); + int pageWidth, int pageHeight, int pageIndex, int structParents) { + this(resources, null, pageWidth, pageHeight, pageIndex, structParents); } private void setSimplePageSize(int width, int height) { Index: src/java/org/apache/fop/pdf/PDFParentTree.java =================================================================== --- src/java/org/apache/fop/pdf/PDFParentTree.java (revision 0) +++ src/java/org/apache/fop/pdf/PDFParentTree.java (revision 0) @@ -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; + } +} + + + + Index: src/java/org/apache/fop/pdf/PDFRoot.java =================================================================== --- src/java/org/apache/fop/pdf/PDFRoot.java (revision 742684) +++ src/java/org/apache/fop/pdf/PDFRoot.java (working copy) @@ -19,6 +19,7 @@ package org.apache.fop.pdf; + /** * Class representing a Root (/Catalog) object. */ @@ -51,6 +52,8 @@ new PDFName("FullScreen"), }; + + /** * create a Root (/Catalog) object. NOTE: The PDFRoot * object must be created before the PDF document is @@ -63,7 +66,7 @@ */ public PDFRoot(int objnum, PDFPages pages) { super(); - setObjectNumber(objnum); + setObjectNumber(objnum); put("Type", new PDFName("Catalog")); setRootPages(pages); } @@ -252,4 +255,25 @@ put("Lang", lang); } -} + /** + * 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); + } + + /** + * Used for accessibility + */ + public void makeTagged() { + PDFDictionary dict = new PDFDictionary(); + dict.put("Marked", Boolean.TRUE); + put("MarkInfo", dict); //new PDFMarkInfo() + } + +} \ No newline at end of file Index: src/java/org/apache/fop/pdf/PDFStructElem.java =================================================================== --- src/java/org/apache/fop/pdf/PDFStructElem.java (revision 0) +++ src/java/org/apache/fop/pdf/PDFStructElem.java (revision 0) @@ -0,0 +1,199 @@ +/* + * 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; + 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); + } + +} Index: src/java/org/apache/fop/pdf/PDFStructTreeRoot.java =================================================================== --- src/java/org/apache/fop/pdf/PDFStructTreeRoot.java (revision 0) +++ src/java/org/apache/fop/pdf/PDFStructTreeRoot.java (revision 0) @@ -0,0 +1,59 @@ +/* + * 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"); + } + + /** + * add a kid + * @param kid to be added + */ + public void addKid(PDFObject kid) { + getKids().add(kid); + } +} \ No newline at end of file Index: src/java/org/apache/fop/pdf/PDFTextUtil.java =================================================================== --- src/java/org/apache/fop/pdf/PDFTextUtil.java (revision 742684) +++ src/java/org/apache/fop/pdf/PDFTextUtil.java (working copy) @@ -128,16 +128,68 @@ } /** + * 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 + " <>\nBDC\nBT\n"); + this.inTextObject = true; + } + + + /** + * begin of a fo:leader text object + * used for accessibility + */ + public void beginLeaderTextObject() { + if (inTextObject) { + throw new IllegalStateException("Already in text object"); + } + write("/Artifact\nBMC\nBT\n"); + this.inTextObject = true; + } + + + + /** * Called when a text object should be ended. + * @param accessEnabled indicating if accessibility is turned on or not */ - public void endTextObject() { + public void endTextObject(boolean accessEnabled) { checkInTextObject(); - write("ET\n"); + if (accessEnabled) { + write("ET\nEMC\n"); + } else { + write("ET\n"); + } this.inTextObject = false; initValues(); } /** + * used for accessibility, separate 2 text elements + * @param mcid of new text element + * @param structElemType of parent + */ + public void makeTextSeparator(int mcid, String structElemType) { + write("ET\nEMC\n" + structElemType + " <>\nBDC\nBT\n"); + } + + /** + * used for accessibility, separate regular text from fo:leader text + */ + public void separateTextFromLeader() { + write("ET\nEMC\n/Artifact\nBMC\nBT\n"); + } + + + /** * Resets the state fields. */ protected void initValues() { @@ -300,4 +352,7 @@ return bufTJ != null && bufTJ.length() > 0; } + + + } Index: src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java =================================================================== --- src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java (revision 742684) +++ src/java/org/apache/fop/render/AbstractPathOrientedRenderer.java (working copy) @@ -834,8 +834,10 @@ * @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 @@ * @param pos the position of the image */ protected final void drawImage(String url, Rectangle2D pos) { - drawImage(url, pos, null); + drawImage(url, pos, null, ""); } /** Index: src/java/org/apache/fop/render/AbstractRenderer.java =================================================================== --- src/java/org/apache/fop/render/AbstractRenderer.java (revision 742684) +++ src/java/org/apache/fop/render/AbstractRenderer.java (working copy) @@ -737,11 +737,13 @@ 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 @@ * * @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 @@ * * @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. } Index: src/java/org/apache/fop/render/afp/AFPPainter.java =================================================================== --- src/java/org/apache/fop/render/afp/AFPPainter.java (revision 742684) +++ src/java/org/apache/fop/render/afp/AFPPainter.java (working copy) @@ -183,7 +183,7 @@ } /** {@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 @@ } /** {@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 @@ /** {@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); Index: src/java/org/apache/fop/render/afp/AFPRenderer.java =================================================================== --- src/java/org/apache/fop/render/afp/AFPRenderer.java (revision 742684) +++ src/java/org/apache/fop/render/afp/AFPRenderer.java (working copy) @@ -375,7 +375,7 @@ 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 @@ /** {@inheritDoc} */ public void renderImage(Image image, Rectangle2D pos) { - drawImage(image.getURL(), pos, image.getForeignAttributes()); + drawImage(image.getURL(), pos, image.getForeignAttributes(),""); } /** {@inheritDoc} */ Index: src/java/org/apache/fop/render/intermediate/IFConstants.java =================================================================== --- src/java/org/apache/fop/render/intermediate/IFConstants.java (revision 742684) +++ src/java/org/apache/fop/render/intermediate/IFConstants.java (working copy) @@ -50,4 +50,6 @@ String EL_BORDER_RECT = "border-rect"; String EL_FONT = "font"; String EL_TEXT = "text"; + /** used for accessibility */ + String EL_STRUCTURE_TREE = "structure-tree"; } Index: src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java =================================================================== --- src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java (revision 742684) +++ src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java (working copy) @@ -20,6 +20,7 @@ package org.apache.fop.render.intermediate; import java.awt.Dimension; +import java.io.IOException; import javax.xml.transform.Result; @@ -140,6 +141,7 @@ * Indicates the start of a document. This method may only be called once before any other * event method. * @throws IFException if an error occurs while handling this event + * @throws */ void startDocument() throws IFException; Index: src/java/org/apache/fop/render/intermediate/IFPainter.java =================================================================== --- src/java/org/apache/fop/render/intermediate/IFPainter.java (revision 742684) +++ src/java/org/apache/fop/render/intermediate/IFPainter.java (working copy) @@ -153,10 +153,11 @@ * @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 @@ * 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 Index: src/java/org/apache/fop/render/intermediate/IFParser.java =================================================================== --- src/java/org/apache/fop/render/intermediate/IFParser.java (revision 742684) +++ src/java/org/apache/fop/render/intermediate/IFParser.java (working copy) @@ -86,7 +86,7 @@ try { Transformer transformer = tFactory.newTransformer(); transformer.setErrorListener(new DefaultErrorListener(log)); - + SAXResult res = new SAXResult(getContentHandler(documentHandler, userAgent)); transformer.transform(src, res); @@ -481,7 +481,9 @@ 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()); + int[] dy = XMLUtil.getAttributeAsIntArray(lastAttributes, "dy"); + String ptr = lastAttributes.getValue("ptr"); // used for accessibility + painter.drawText(x, y, letterSpacing, wordSpacing, dx, content.toString(), ptr); } public boolean ignoreCharacters() { @@ -576,9 +578,10 @@ 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 +589,7 @@ 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; Index: src/java/org/apache/fop/render/intermediate/IFRenderer.java =================================================================== --- src/java/org/apache/fop/render/intermediate/IFRenderer.java (revision 742684) +++ src/java/org/apache/fop/render/intermediate/IFRenderer.java (working copy) @@ -121,8 +121,8 @@ private boolean inPageSequence = false; - private Stack graphicContextStack = new Stack(); - private Stack viewportDimensionStack = new Stack(); + private final Stack graphicContextStack = new Stack(); + private final Stack viewportDimensionStack = new Stack(); private IFGraphicContext graphicContext = new IFGraphicContext(); //private Stack groupStack = new Stack(); @@ -133,12 +133,12 @@ * Must be used in conjunction with the page reference to fully specify the details * of a "go-to" action. */ - private Map idPositions = new java.util.HashMap(); + private final Map idPositions = new java.util.HashMap(); /** * The "go-to" actions in idGoTos that are not complete yet */ - private List unfinishedGoTos = new java.util.ArrayList(); + private final List unfinishedGoTos = new java.util.ArrayList(); // can't use a Set because PDFGoTo.equals returns true if the target is the same, // even if the object number differs @@ -146,11 +146,11 @@ protected Map pageIndices = new java.util.HashMap(); private BookmarkTree bookmarkTree; - private List deferredDestinations = new java.util.ArrayList(); - private List deferredLinks = new java.util.ArrayList(); - private ActionSet actionSet = new ActionSet(); + private final List deferredDestinations = new java.util.ArrayList(); + private final List deferredLinks = new java.util.ArrayList(); + private final ActionSet actionSet = new ActionSet(); - private TextUtil textUtil = new TextUtil(); + private final TextUtil textUtil = new TextUtil(); /** * Main constructor @@ -818,7 +818,7 @@ currentIPPosition = saveIP; currentBPPosition = saveBP; - currentBPPosition += (int)(bv.getAllocBPD()); + currentBPPosition += (bv.getAllocBPD()); } viewportDimensionStack.pop(); } @@ -886,6 +886,7 @@ // stuff we only need if a link must be created: Rectangle ipRect = null; AbstractAction action = null; + String ptr = (String) ip.getTrait(Trait.PTR); // used for accessibility // make sure the rect is determined *before* calling super! int ipp = currentIPPosition; int bpp = currentBPPosition + ip.getOffset(); @@ -898,6 +899,7 @@ boolean linkTraitFound = false; + // try INTERNAL_LINK first Trait.InternalLink intLink = (Trait.InternalLink) ip.getTrait(Trait.INTERNAL_LINK); if (intLink != null) { @@ -929,6 +931,7 @@ // 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 +966,7 @@ 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 +985,7 @@ 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 +1058,24 @@ 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(); @@ -1090,6 +1103,7 @@ this.starty = y; } + void setSpacing(int tls, int tws) { this.tls = tls; this.tws = tws; @@ -1105,9 +1119,9 @@ 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 +1132,12 @@ } /** {@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 +1146,7 @@ uri = URISpecification.getURL(uri); try { establishForeignAttributes(foreignAttributes); - painter.drawImage(uri, posInt); + painter.drawImage(uri, posInt, ptr); resetForeignAttributes(); } catch (IFException ife) { handleIFException(ife); @@ -1140,7 +1154,7 @@ } /** {@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 +1164,7 @@ Document doc = fo.getDocument(); try { establishForeignAttributes(fo.getForeignAttributes()); - painter.drawImage(doc, posInt); + painter.drawImage(doc, posInt, ptr); resetForeignAttributes(); } catch (IFException ife) { handleIFException(ife); Index: src/java/org/apache/fop/render/intermediate/IFSerializer.java =================================================================== --- src/java/org/apache/fop/render/intermediate/IFSerializer.java (revision 742684) +++ src/java/org/apache/fop/render/intermediate/IFSerializer.java (working copy) @@ -25,11 +25,30 @@ 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.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +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; @@ -53,6 +72,8 @@ import org.apache.fop.util.XMLConstants; import org.apache.fop.util.XMLUtil; + + /** * IFPainter implementation that serializes the intermediate format to XML. */ @@ -60,10 +81,46 @@ 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 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. */ @@ -150,9 +207,18 @@ 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 e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } /** {@inheritDoc} */ @@ -202,6 +268,9 @@ } } + + + /** {@inheritDoc} */ public void startPageSequence(String id) throws IFException { try { @@ -209,9 +278,36 @@ 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().getResultAsByteArray())); + } + 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) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw new IFException("IO Problem.", e); } } @@ -231,10 +327,9 @@ AttributesImpl atts = new AttributesImpl(); addAttribute(atts, "index", Integer.toString(index)); addAttribute(atts, "name", name); - addAttribute(atts, "page-master-name", pageMasterName); + addAttribute(atts, "page-master-name", name); addAttribute(atts, "width", Integer.toString(size.width)); addAttribute(atts, "height", Integer.toString(size.height)); - addForeignAttributes(atts); handler.startElement(EL_PAGE, atts); } catch (SAXException e) { throw new IFException("SAX error in startPage()", e); @@ -284,6 +379,7 @@ public void startPageTrailer() throws IFException { try { handler.startElement(EL_PAGE_TRAILER); + commitNavigation(); } catch (SAXException e) { throw new IFException("SAX error in startPageTrailer()", e); } @@ -292,7 +388,6 @@ /** {@inheritDoc} */ public void endPageTrailer() throws IFException { try { - commitNavigation(); handler.endElement(EL_PAGE_TRAILER); } catch (SAXException e) { throw new IFException("SAX error in endPageTrailer()", e); @@ -381,7 +476,7 @@ } /** {@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); @@ -389,33 +484,26 @@ addAttribute(atts, "y", Integer.toString(rect.y)); 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); } } - private void addForeignAttributes(AttributesImpl atts) { - Map foreignAttributes = getContext().getForeignAttributes(); - if (!foreignAttributes.isEmpty()) { - Iterator iter = foreignAttributes.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = (Map.Entry)iter.next(); - addAttribute(atts, (QName)entry.getKey(), entry.getValue().toString()); - } - } - } - /** {@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)); addAttribute(atts, "y", Integer.toString(rect.y)); 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); @@ -504,7 +592,7 @@ addAttribute(atts, "x2", Integer.toString(end.x)); addAttribute(atts, "y2", Integer.toString(end.y)); addAttribute(atts, "stroke-width", Integer.toString(width)); - addAttribute(atts, "color", ColorUtil.colorToString(color)); + addAttribute(atts, "color", Integer.toString(width)); addAttribute(atts, "style", style.getName()); handler.element(EL_LINE, atts); } catch (SAXException e) { @@ -514,10 +602,9 @@ /** {@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"); addAttribute(atts, "x", Integer.toString(x)); addAttribute(atts, "y", Integer.toString(y)); if (letterSpacing != 0) { @@ -529,6 +616,9 @@ 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); @@ -707,8 +797,9 @@ public void addResolvedAction(AbstractAction action) throws IFException { assert action.isComplete(); assert action.hasID(); - AbstractAction noted = (AbstractAction)incompleteActions.remove(action.getID()); + AbstractAction noted = (AbstractAction)incompleteActions.get(action.getID()); if (noted != null) { + incompleteActions.remove(action.getID()); completeActions.add(action); } else { //ignore as it was already complete when it was first used. Index: src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java =================================================================== --- src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java (revision 742684) +++ src/java/org/apache/fop/render/intermediate/extensions/AbstractAction.java (working copy) @@ -27,6 +27,7 @@ public abstract class AbstractAction implements XMLizable { private String id; + private String ptr; // used for accessibility /** * Sets an ID to make the action referencable. @@ -43,8 +44,24 @@ 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 */ Index: src/java/org/apache/fop/render/java2d/Java2DPainter.java =================================================================== --- src/java/org/apache/fop/render/java2d/Java2DPainter.java (revision 742684) +++ src/java/org/apache/fop/render/java2d/Java2DPainter.java (working copy) @@ -156,7 +156,7 @@ } /** {@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 @@ } /** {@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 @@ } /** {@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( Index: src/java/org/apache/fop/render/java2d/Java2DRenderer.java =================================================================== --- src/java/org/apache/fop/render/java2d/Java2DRenderer.java (revision 742684) +++ src/java/org/apache/fop/render/java2d/Java2DRenderer.java (working copy) @@ -875,7 +875,7 @@ 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()); Index: src/java/org/apache/fop/render/pcl/PCLEventProducer.xml =================================================================== --- src/java/org/apache/fop/render/pcl/PCLEventProducer.xml (revision 742684) +++ src/java/org/apache/fop/render/pcl/PCLEventProducer.xml (working copy) @@ -1,3 +1,4 @@ - + + Paper type ({pageWidth} x {pageHeight} mpt) could not be determined. Falling back to: {fallbackPaper} Index: src/java/org/apache/fop/render/pcl/PCLPainter.java =================================================================== --- src/java/org/apache/fop/render/pcl/PCLPainter.java (revision 742684) +++ src/java/org/apache/fop/render/pcl/PCLPainter.java (working copy) @@ -154,7 +154,7 @@ } /** {@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 @@ } /** {@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 @@ } /** {@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 @@ 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); Index: src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java =================================================================== --- src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java (revision 742684) +++ src/java/org/apache/fop/render/pdf/PDFConfigurationConstants.java (working copy) @@ -49,4 +49,6 @@ * PDF/X profile is active). */ String KEY_DISABLE_SRGB_COLORSPACE = "disable-srgb-colorspace"; + /** PDF Accessibility */ + String ACCESSIBLITY = "accessibility"; } Index: src/java/org/apache/fop/render/pdf/PDFContentGenerator.java =================================================================== --- src/java/org/apache/fop/render/pdf/PDFContentGenerator.java (revision 742684) +++ src/java/org/apache/fop/render/pdf/PDFContentGenerator.java (working copy) @@ -50,6 +50,7 @@ /** 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 @@ * @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 @@ }; this.currentState = new PDFPaintingState(); + this.accessEnabled = accessibilityEnabled; } /** @@ -153,7 +156,25 @@ currentStream.add("q\n"); } + /** {@inheritDoc} */ + protected void saveGraphicsState(String structElemType, int sequenceNum) { + endTextObject(); + currentState.save(); + currentStream.add(structElemType + " <>\nBDC\n"); + 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 + " <>\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 * should be issued @@ -171,6 +192,33 @@ 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.makeTextSeparator(mcid, structElemType); + } + + /** + * used for accessibility + * separates a text element from fo:leader text element + */ + public void separateTextElementFromLeader() { + textutil.separateTextFromLeader(); + } + + /** Indicates the beginning of a text object. */ protected void beginTextObject() { if (!textutil.isInTextObject()) { @@ -178,10 +226,31 @@ } } + + /** + * 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.beginLeaderTextObject(); + } + } + /** Indicates the end of a text object. */ protected void endTextObject() { if (textutil.isInTextObject()) { - textutil.endTextObject(); + textutil.endTextObject(accessEnabled); } } @@ -326,5 +395,30 @@ 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(); + } + + + + } Index: src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java =================================================================== --- src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java (revision 742684) +++ src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java (working copy) @@ -21,9 +21,22 @@ import java.awt.Dimension; import java.awt.geom.AffineTransform; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; 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.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -32,11 +45,18 @@ 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.render.intermediate.AbstractBinaryWritingIFDocumentHandler; import org.apache.fop.render.intermediate.IFContext; import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; @@ -44,6 +64,10 @@ import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFPainter; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; /** * {@code IFDocumentHandler} implementation that produces PDF. */ @@ -52,6 +76,70 @@ /** 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 ArrayList parentTreeList = new ArrayList(); + + private 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 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 +167,7 @@ /** Used for bookmarks/outlines. */ protected Map pageReferences = new java.util.HashMap(); - private PDFDocumentNavigationHandler documentNavigationHandler + private final PDFDocumentNavigationHandler documentNavigationHandler = new PDFDocumentNavigationHandler(this); /** @@ -90,7 +178,11 @@ /** {@inheritDoc} */ public boolean supportsPagesOutOfOrder() { - return true; + if (accessEnabled) { + return false; // required for accessibility + } else { + return true; + } } /** {@inheritDoc} */ @@ -129,8 +221,23 @@ throw new IllegalStateException("OutputStream hasn't been set through setResult()"); } 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 e) { + // TODO Auto-generated catch block + e.printStackTrace(); } } @@ -143,10 +250,34 @@ 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); + pdfUtil.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; @@ -157,9 +288,62 @@ super.endDocument(); } + /** {@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().getResultAsByteArray())); + } + PDFStructElem structElemPart = new PDFStructElem("page-sequence", + pdfUtil.getStructElemDocument()); + this.pdfDoc.assignObjectNumber(structElemPart); + this.pdfDoc.addTrailerObject(structElemPart); + pdfUtil.getStructElemDocument().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 (XPathExpressionException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw new IFException("XPath Problem.", e); + } catch (SAXException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw new IFException("SAX Problem.", e); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw new IFException("IO Problem.", e); + } + } } /** {@inheritDoc} */ @@ -170,22 +354,25 @@ /** {@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); @@ -240,8 +427,8 @@ static final class PageReference { - private PDFReference pageRef; - private Dimension pageDimension; + private final PDFReference pageRef; + private final Dimension pageDimension; private PageReference(PDFPage page, Dimension dim) { this.pageRef = page.makeReference(); @@ -257,4 +444,192 @@ } } + /** + * Used for accessibility + * @param position in parentTree + * @param o reference of PDFObject to be added to parentTree + */ + public 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 + */ + public 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 + */ + public 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 + */ + public 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 + */ + public 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 + */ + public 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 + */ + public 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 + */ + public void incMCID() { + this.mcidKey++; + } + + /** + * MCID is a sequential number per page + * @return MCID value + */ + public int getMCID() { + return this.mcidKey; + } + + /** + * Used for accessibility + * @param ptr pointer into map of all structElems + * @return type of found structElem + */ + public 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 + */ + public int getPageLinkCountPlusPageParentKey() { + this.pageLinkCount++; + return (this.parentTreeKey + this.pageLinkCount); + } + + /** + * used for accessibility + * @return current parentTreeKey + */ + public int getCurrentParentTreeKey() { + return this.parentTreeKey; + } + } Index: src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java =================================================================== --- src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java (revision 742684) +++ src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java (working copy) @@ -42,16 +42,19 @@ 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 final Map incompleteActions = new java.util.HashMap(); + private final Map completeActions = new java.util.HashMap(); - private Map incompleteActions = new java.util.HashMap(); - private Map completeActions = new java.util.HashMap(); - /** * Default constructor. * @param documentHandler the parent document handler @@ -111,6 +114,14 @@ 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); } } Index: src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java =================================================================== --- src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java (revision 742684) +++ src/java/org/apache/fop/render/pdf/PDFImageHandlerRawJPEG.java (working copy) @@ -82,7 +82,17 @@ 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} */ Index: src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java =================================================================== --- src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java (revision 742684) +++ src/java/org/apache/fop/render/pdf/PDFImageHandlerRenderedImage.java (working copy) @@ -83,7 +83,13 @@ 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} */ Index: src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java =================================================================== --- src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java (revision 742684) +++ src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java (working copy) @@ -101,8 +101,8 @@ float w = (float)ctx.getDocumentSize().getWidth() * 1000f; float h = (float)ctx.getDocumentSize().getHeight() * 1000f; - float sx = pos.width / (float)w; - float sy = pos.height / (float)h; + float sx = pos.width / w; + float sy = pos.height / h; //Scaling and translation for the bounding box of the image AffineTransform scaling = new AffineTransform( @@ -119,8 +119,14 @@ * Note: To have the svg overlay (under) a text area then use * an fo:block-container */ + + generator.saveGraphicsState(); 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 +174,11 @@ 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"); } Index: src/java/org/apache/fop/render/pdf/PDFPainter.java =================================================================== --- src/java/org/apache/fop/render/pdf/PDFPainter.java (revision 742684) +++ src/java/org/apache/fop/render/pdf/PDFPainter.java (working copy) @@ -59,13 +59,19 @@ /** 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. * @param documentHandler the parent document handler @@ -76,6 +82,7 @@ this.generator = documentHandler.generator; this.borderPainter = new PDFBorderPainter(this.generator); this.state = IFState.create(); + accessEnabled = this.getUserAgent().accessibilityEnabled(); } /** {@inheritDoc} */ @@ -122,15 +129,50 @@ } /** {@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 @@ protected RenderingContext createRenderingContext() { PDFRenderingContext pdfContext = new PDFRenderingContext( getUserAgent(), generator, this.documentHandler.currentPage, getFontInfo()); + pdfContext.setMCID(mcid); + pdfContext.setStructElemType(structElemType); return pdfContext; } @@ -158,11 +202,45 @@ + " 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 +331,46 @@ } /** {@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) { + if (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 { + // + if (generator.getTextUtil().isInTextObject()) { + generator.separateTextElementFromLeader(); + } + generator.updateColor(state.getTextColor(), true, null); + generator.beginLeaderTextObject(); + } + } else { + generator.updateColor(state.getTextColor(), true, null); + generator.beginTextObject(); + } + } 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 +391,7 @@ PDFTextUtil textutil = generator.getTextUtil(); textutil.updateTf(fontKey, fontSize, tf.isMultiByte()); - generator.updateCharacterSpacing((float)letterSpacing / 1000f); + generator.updateCharacterSpacing(letterSpacing / 1000f); textutil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, x / 1000f, y / 1000f)); int l = text.length(); Index: src/java/org/apache/fop/render/pdf/PDFRenderer.java =================================================================== --- src/java/org/apache/fop/render/pdf/PDFRenderer.java (revision 742684) +++ src/java/org/apache/fop/render/pdf/PDFRenderer.java (working copy) @@ -433,7 +433,7 @@ this.currentPage = this.pdfDoc.getFactory().makePage( this.pdfResources, (int) Math.round(w / 1000), (int) Math.round(h / 1000), - page.getPageIndex()); + page.getPageIndex(), -1); pageReferences.put(page.getKey(), currentPage.referencePDF()); //pvReferences.put(page.getKey(), page); @@ -461,7 +461,8 @@ 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, + this.getUserAgent().accessibilityEnabled()); 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 @@ } /** {@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); } Index: src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java =================================================================== --- src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java (revision 742684) +++ src/java/org/apache/fop/render/pdf/PDFRendererConfigurator.java (working copy) @@ -69,6 +69,7 @@ } } + private void configure(Configuration cfg, PDFRenderingUtil pdfUtil) throws FOPException { //PDF filters try { Index: src/java/org/apache/fop/render/pdf/PDFRenderingContext.java =================================================================== --- src/java/org/apache/fop/render/pdf/PDFRenderingContext.java (revision 742684) +++ src/java/org/apache/fop/render/pdf/PDFRenderingContext.java (working copy) @@ -31,10 +31,15 @@ */ public class PDFRenderingContext extends AbstractRenderingContext { - private PDFContentGenerator generator; - private FontInfo fontInfo; - private PDFPage page; + private final PDFContentGenerator generator; + private final FontInfo fontInfo; + private final 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. * @param userAgent the user agent @@ -79,4 +84,35 @@ 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; + } } Index: src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java =================================================================== --- src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java (revision 742684) +++ src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java (working copy) @@ -52,6 +52,8 @@ 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; @@ -64,7 +66,7 @@ /** logging instance */ private static Log log = LogFactory.getLog(PDFRenderingUtil.class); - private FOUserAgent userAgent; + private final FOUserAgent userAgent; /** the PDF Document being created */ protected PDFDocument pdfDoc; @@ -75,12 +77,21 @@ /** 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; /** Registry of PDF filters */ protected Map filterMap; + /** used for accessibility */ + protected PDFStructTreeRoot structTreeRoot = null; + + /** used for accessibility */ + protected PDFStructElem structElemDocument = null; + /** the ICC stream used as output profile by this document for PDF/A and PDF/X functionality. */ protected PDFICCStream outputProfile; /** the default sRGB color space. */ @@ -157,6 +168,8 @@ } this.encryptionParams.setAllowEditAnnotations(!booleanValueOf(setting)); } + + String s = (String)userAgent.getRendererOptions().get(PDF_A_MODE); if (s != null) { this.pdfAMode = PDFAMode.valueOf(s); @@ -169,6 +182,12 @@ 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,10 +403,36 @@ log.debug("PDF/A is active. Conformance Level: " + pdfAMode); addPDFA1OutputIntent(); } + if (this.accessibility) { + this.pdfDoc.getRoot().makeTagged(); + log.info("Accessibility is enabled"); + structTreeRoot = this.pdfDoc.getFactory().makeStructTreeRoot(); + this.pdfDoc.getRoot().setStructTreeRoot(structTreeRoot); + structElemDocument = new PDFStructElem("root", structTreeRoot); + this.pdfDoc.assignObjectNumber(structElemDocument); + this.pdfDoc.addTrailerObject(structElemDocument); + structTreeRoot.addKid(structElemDocument); + } return this.pdfDoc; } /** + * Used for accessibility + * @return the Structure Tree Root element + */ + public PDFStructTreeRoot getStructTreeRoot() { + return this.structTreeRoot; + } + + /** + * Used for accessibility + * @return the Structure Tree Document Element + */ + public PDFStructElem getStructElemDocument() { + return this.structElemDocument; + } + + /** * Generates a page label in the PDF document. * @param pageIndex the index of the page * @param pageNumber the formatted page number Index: src/java/org/apache/fop/render/ps/PSEventProducer.xml =================================================================== --- src/java/org/apache/fop/render/ps/PSEventProducer.xml (revision 742684) +++ src/java/org/apache/fop/render/ps/PSEventProducer.xml (working copy) @@ -1,3 +1,4 @@ - + + Failed to parse dictionary string. Reason: {e}, content = "{content}" Index: src/java/org/apache/fop/render/ps/PSPainter.java =================================================================== --- src/java/org/apache/fop/render/ps/PSPainter.java (revision 742684) +++ src/java/org/apache/fop/render/ps/PSPainter.java (working copy) @@ -176,7 +176,7 @@ } /** {@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 @@ } /** {@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 @@ /** {@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(); Index: src/java/org/apache/fop/render/ps/PSRenderer.java =================================================================== --- src/java/org/apache/fop/render/ps/PSRenderer.java (revision 742684) +++ src/java/org/apache/fop/render/ps/PSRenderer.java (working copy) @@ -345,7 +345,7 @@ } /** {@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 @@ * {@inheritDoc} */ public void renderImage(Image image, Rectangle2D pos) { - drawImage(image.getURL(), pos, image.getForeignAttributes()); + drawImage(image.getURL(), pos, image.getForeignAttributes(), ""); } /** Index: src/java/org/apache/fop/render/txt/TXTRenderer.java =================================================================== --- src/java/org/apache/fop/render/txt/TXTRenderer.java (revision 742684) +++ src/java/org/apache/fop/render/txt/TXTRenderer.java (working copy) @@ -443,7 +443,7 @@ } /** {@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 } Index: src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java =================================================================== --- src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java (revision 742684) +++ src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java (working copy) @@ -316,7 +316,7 @@ PDFResources pdfResources = this.pdfDoc.getResources(); PDFPage page = this.pdfDoc.getFactory().makePage(pdfResources, - width, height); + width, height, -1); resourceContext = page; pdfContext.setCurrentPage(page); pageRef = page.referencePDF(); Index: src/java/org/apache/fop/svg/PDFTextPainter.java =================================================================== --- src/java/org/apache/fop/svg/PDFTextPainter.java (revision 742684) +++ src/java/org/apache/fop/svg/PDFTextPainter.java (working copy) @@ -145,7 +145,7 @@ applyColorAndPaint(tpi, pdf); - textUtil.beginTextObject(); + textUtil.beginTextObject(); textUtil.setFonts(fonts); boolean stroke = (tpi.strokePaint != null) && (tpi.strokeStroke != null); @@ -239,7 +239,7 @@ prevVisibleCharWidth = textUtil.getCurrentFont().getCharWidth(chars.charAt(index)); } textUtil.writeTJ(); - textUtil.endTextObject(); + textUtil.endTextObject(false); textUtil.restoreGraphicsState(); if (DEBUG) { g2d.setStroke(new BasicStroke(0)); Index: src/java/org/apache/fop/util/DOM2SAX.java =================================================================== --- src/java/org/apache/fop/util/DOM2SAX.java (revision 742684) +++ src/java/org/apache/fop/util/DOM2SAX.java (working copy) @@ -77,6 +77,18 @@ 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 Index: src/sandbox/org/apache/fop/render/svg/SVGPainter.java =================================================================== --- src/sandbox/org/apache/fop/render/svg/SVGPainter.java (revision 742750) +++ src/sandbox/org/apache/fop/render/svg/SVGPainter.java (working copy) @@ -193,7 +193,7 @@ } /** {@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 @@ } /** {@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 @@ } /** {@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(); Index: test/java/org/apache/fop/intermediate/IFParserTestCase.java =================================================================== --- test/java/org/apache/fop/intermediate/IFParserTestCase.java (revision 736273) +++ test/java/org/apache/fop/intermediate/IFParserTestCase.java (working copy) @@ -57,7 +57,7 @@ /** {@inheritDoc} */ protected String getTargetMIME() { - return MimeConstants.MIME_PDF + ";mode=painter"; + return MimeConstants.MIME_PDF + "mode=painter"; } /** {@inheritDoc} */