diff options
author | Jeremias Maerki <jeremias@apache.org> | 2009-04-17 12:10:07 +0000 |
---|---|---|
committer | Jeremias Maerki <jeremias@apache.org> | 2009-04-17 12:10:07 +0000 |
commit | 8eefdf7d68cc640ca4c3d4ac331e5af1a9049ee0 (patch) | |
tree | d7ff2463e2e2ce6a322e9df1796a8b205f2c5440 /src/java/org | |
parent | eeb0cc4fb8f3816e7507ce22de0fb4b9dd433f1c (diff) | |
download | xmlgraphics-fop-8eefdf7d68cc640ca4c3d4ac331e5af1a9049ee0.tar.gz xmlgraphics-fop-8eefdf7d68cc640ca4c3d4ac331e5af1a9049ee0.zip |
Added support for adding natural language information on page-sequence and document level to a tagged PDF.
The document-level language is assumed to be the language of the first page-sequence.
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_Accessibility@765965 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org')
8 files changed, 163 insertions, 16 deletions
diff --git a/src/java/org/apache/fop/area/AreaTreeParser.java b/src/java/org/apache/fop/area/AreaTreeParser.java index bb7e47dd3..d2af24a01 100644 --- a/src/java/org/apache/fop/area/AreaTreeParser.java +++ b/src/java/org/apache/fop/area/AreaTreeParser.java @@ -85,6 +85,7 @@ import org.apache.fop.util.ContentHandlerFactory; import org.apache.fop.util.ContentHandlerFactoryRegistry; import org.apache.fop.util.ConversionUtils; import org.apache.fop.util.DefaultErrorListener; +import org.apache.fop.util.XMLConstants; import org.apache.fop.util.XMLUtil; /** @@ -1134,7 +1135,7 @@ public class AreaTreeParser { for (int i = 0, c = atts.getLength(); i < c; i++) { String ns = atts.getURI(i); if (ns.length() > 0) { - if ("http://www.w3.org/2000/xmlns/".equals(ns)) { + if (XMLConstants.XMLNS_NAMESPACE_URI.equals(ns)) { continue; } QName qname = new QName(ns, atts.getQName(i)); diff --git a/src/java/org/apache/fop/pdf/PDFStructElem.java b/src/java/org/apache/fop/pdf/PDFStructElem.java index e36f5fd42..2f8898a19 100644 --- a/src/java/org/apache/fop/pdf/PDFStructElem.java +++ b/src/java/org/apache/fop/pdf/PDFStructElem.java @@ -19,6 +19,10 @@ package org.apache.fop.pdf; +import java.util.Locale; + +import org.apache.fop.util.XMLUtil; + /** * Class representing a PDF Structure Element. */ @@ -144,4 +148,29 @@ public class PDFStructElem extends PDFDictionary { public PDFName getStructureType() { return (PDFName)get("S"); } + + /** + * Sets the language of this structure element. + * @param language the language (as defined in the section about + * "Natural Language Specification") + */ + public void setLanguage(String language) { + put("Lang", language); + } + + /** + * Sets the language of this structure element. + * @param language the language + */ + public void setLanguage(Locale language) { + setLanguage(XMLUtil.toRFC3066(language)); + } + + /** + * Returns the language of this structure element. + * @return the language (or null if no language was specified) + */ + public String getLanguage() { + return (String)get("Lang"); + } } diff --git a/src/java/org/apache/fop/render/intermediate/IFContext.java b/src/java/org/apache/fop/render/intermediate/IFContext.java index b534cfa56..f052846d3 100644 --- a/src/java/org/apache/fop/render/intermediate/IFContext.java +++ b/src/java/org/apache/fop/render/intermediate/IFContext.java @@ -20,6 +20,7 @@ package org.apache.fop.render.intermediate; import java.util.Collections; +import java.util.Locale; import java.util.Map; import org.apache.xmlgraphics.util.QName; @@ -43,6 +44,8 @@ public class IFContext { /** foreign attributes: Map<QName, Object> */ private Map foreignAttributes = Collections.EMPTY_MAP; + private Locale language; + private String structurePointer; /** @@ -111,6 +114,22 @@ public class IFContext { } /** + * Sets the currently applicable language. + * @param lang the language + */ + public void setLanguage(Locale lang) { + this.language = lang; + } + + /** + * Returns the currently applicable language. + * @return the language (or null if the language is undefined) + */ + public Locale getLanguage() { + return this.language; + } + + /** * Sets the structure pointer for the following painted marks. This method is used when * accessibility features are enabled. * @param ptr the structure pointer diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java index f1a921377..e374f82fa 100644 --- a/src/java/org/apache/fop/render/intermediate/IFParser.java +++ b/src/java/org/apache/fop/render/intermediate/IFParser.java @@ -25,6 +25,7 @@ import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.util.Map; +import java.util.Set; import javax.xml.transform.Source; import javax.xml.transform.Transformer; @@ -73,6 +74,15 @@ public class IFParser implements IFConstants { private static SAXTransformerFactory tFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); + private static Set handledNamespaces = new java.util.HashSet(); + + static { + handledNamespaces.add(XMLNS_NAMESPACE_URI); + handledNamespaces.add(XML_NAMESPACE); + handledNamespaces.add(NAMESPACE); + handledNamespaces.add(XLINK_NAMESPACE); + } + /** * Parses an intermediate file and paints it. * @param src the Source instance pointing to the intermediate file @@ -357,6 +367,11 @@ public class IFParser implements IFConstants { public void startElement(Attributes attributes) throws IFException { String id = attributes.getValue("id"); + String xmllang = attributes.getValue(XML_NAMESPACE, "lang"); + if (xmllang != null) { + documentHandler.getContext().setLanguage( + XMLUtil.convertRFC3066ToLocale(xmllang)); + } Map foreignAttributes = getForeignAttributes(lastAttributes); establishForeignAttributes(foreignAttributes); documentHandler.startPageSequence(id); @@ -365,6 +380,7 @@ public class IFParser implements IFConstants { public void endElement() throws IFException { documentHandler.endPageSequence(); + documentHandler.getContext().setLanguage(null); } } @@ -643,11 +659,7 @@ public class IFParser implements IFConstants { for (int i = 0, c = atts.getLength(); i < c; i++) { String ns = atts.getURI(i); if (ns.length() > 0) { - if ("http://www.w3.org/2000/xmlns/".equals(ns)) { - continue; - } else if (NAMESPACE.equals(ns)) { - continue; - } else if (XLINK_NAMESPACE.equals(ns)) { + if (handledNamespaces.contains(ns)) { continue; } if (foreignAttributes == null) { diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java index 905277f1b..7c0bf8abc 100644 --- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java +++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java @@ -30,6 +30,7 @@ import java.io.OutputStream; import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Stack; @@ -493,6 +494,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { try { if (this.inPageSequence) { documentHandler.endPageSequence(); + documentHandler.getContext().setLanguage(null); } else { if (this.documentMetadata == null) { this.documentMetadata = createDefaultDocumentMetadata(); @@ -502,6 +504,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { this.inPageSequence = true; } establishForeignAttributes(pageSequence.getForeignAttributes()); + documentHandler.getContext().setLanguage(toLocale(pageSequence)); documentHandler.startPageSequence(null); resetForeignAttributes(); processExtensionAttachments(pageSequence); @@ -510,6 +513,17 @@ public class IFRenderer extends AbstractPathOrientedRenderer { } } + private Locale toLocale(PageSequence pageSequence) { + if (pageSequence.getLanguage() != null) { + if (pageSequence.getCountry() != null) { + return new Locale(pageSequence.getLanguage(), pageSequence.getCountry()); + } else { + return new Locale(pageSequence.getLanguage()); + } + } + return null; + } + private Metadata createDefaultDocumentMetadata() { Metadata xmp = new Metadata(); DublinCoreAdapter dc = DublinCoreSchema.getAdapter(xmp); diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java index e00c88b4a..2d009d58d 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java @@ -29,7 +29,9 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Stack; import javax.xml.namespace.NamespaceContext; import javax.xml.parsers.DocumentBuilder; @@ -267,6 +269,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler if (id != null) { atts.addAttribute(XML_NAMESPACE, "id", "xml:id", XMLUtil.CDATA, id); } + applyLanguage(atts); addForeignAttributes(atts); handler.startElement(EL_PAGE_SEQUENCE, atts); if (this.getUserAgent().isAccessibilityEnabled()) { @@ -301,6 +304,7 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler public void endPageSequence() throws IFException { try { handler.endElement(EL_PAGE_SEQUENCE); + popLanguage(); } catch (SAXException e) { throw new IFException("SAX error in endPageSequence()", e); } @@ -831,4 +835,25 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler } } + private Stack languageStack = new Stack(); + + private void applyLanguage(AttributesImpl atts) { + Locale lang = getContext().getLanguage(); + if (lang != null) { + if (languageStack.isEmpty() || !languageStack.peek().equals(lang)) { + atts.addAttribute(XML_NAMESPACE, "lang", "xml:lang", XMLUtil.CDATA, + XMLUtil.toRFC3066(lang)); + } + languageStack.push(lang); + } else { + assert languageStack.isEmpty(); + } + } + + private void popLanguage() { + if (!languageStack.isEmpty()) { + languageStack.pop(); + } + } + } diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java index 7f5a9be5f..aba4cceb3 100644 --- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java @@ -70,6 +70,7 @@ import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; import org.apache.fop.render.intermediate.IFDocumentNavigationHandler; import org.apache.fop.render.intermediate.IFException; import org.apache.fop.render.intermediate.IFPainter; +import org.apache.fop.util.XMLUtil; /** * {@code IFDocumentHandler} implementation that produces PDF. @@ -82,7 +83,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { /** the following variables are used for accessibility */ private int pageSequenceCounter; private DocumentBuilder parser = null; - private Document doc = null; + private Document reducedFOTree = null; private Map structElemType = new HashMap(); private boolean accessEnabled = false; private int parentTreeKey = -1; @@ -226,10 +227,6 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { this.pdfDoc.addTrailerObject(structElemDocument); structTreeRoot.addKid(structElemDocument); - //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(); @@ -271,7 +268,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { getStructTreeRoot().addParentTree(parentTree); pdfDoc.outputTrailer(this.outputStream); parser = null; - doc = null; + reducedFOTree = null; structElemType = null; parentTree = null; structTreeMap = null; @@ -298,17 +295,31 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { /** {@inheritDoc} */ public void startPageSequence(String id) throws IFException { - //TODO page sequence title, country and language + //TODO page sequence title + + if (this.pdfDoc.getRoot().getLanguage() == null + && getContext().getLanguage() != null) { + //No document-level language set, so we use the first page-sequence's language + this.pdfDoc.getRoot().setLanguage(XMLUtil.toRFC3066(getContext().getLanguage())); + } if (getUserAgent().isAccessibilityEnabled()) { try { - if (doc == null) { - doc = parser.parse( + if (this.pdfDoc.getRoot().getLanguage() == null) { + //No language has been set on the first page-sequence, so fall back to "en". + this.pdfDoc.getRoot().setLanguage("en"); + } + + if (reducedFOTree == null) { + reducedFOTree = parser.parse( new ByteArrayInputStream(this.getUserAgent().getReducedFOTree())); } PDFStructElem parent = (PDFStructElem)getStructTreeRoot().getFirstChild(); PDFStructElem structElemPart = new PDFStructElem(parent, FOToPDFRoleMap.mapFormattingObject("page-sequence", parent)); + if (getContext().getLanguage() != null) { + structElemPart.setLanguage(getContext().getLanguage()); + } this.pdfDoc.assignObjectNumber(structElemPart); this.pdfDoc.addTrailerObject(structElemPart); parent.addKid(structElemPart); @@ -320,7 +331,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { "http://www.w3.org/1999/XSL/Format"); xpath.setNamespaceContext(namespaceContext); - NodeList nodes = (NodeList) xpath.evaluate(xpathExpr, doc, + NodeList nodes = (NodeList) xpath.evaluate(xpathExpr, reducedFOTree, XPathConstants.NODESET); for (int i = 0, n = nodes.getLength(); i < n; i++) { diff --git a/src/java/org/apache/fop/util/XMLUtil.java b/src/java/org/apache/fop/util/XMLUtil.java index e42bef90e..644b7c22d 100644 --- a/src/java/org/apache/fop/util/XMLUtil.java +++ b/src/java/org/apache/fop/util/XMLUtil.java @@ -21,6 +21,7 @@ package org.apache.fop.util; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; +import java.util.Locale; import org.xml.sax.Attributes; import org.xml.sax.SAXException; @@ -170,4 +171,39 @@ public class XMLUtil implements XMLConstants { atts.addAttribute("", localName, localName, XMLUtil.CDATA, value); } + /** + * Converts a {@link Locale} instance to an RFC 3066 compliant language identifier. + * @param language the language + * @return the formatted language identifier + */ + public static String toRFC3066(Locale language) { + if (language == null || language.getLanguage().length() == 0) { + return null; + } + StringBuffer sb = new StringBuffer(); + sb.append(language.getLanguage()); + if (language.getCountry().length() > 0) { + sb.append('-'); + sb.append(language.getCountry()); + } + return sb.toString(); + } + + /** + * Converts an RFC 3066 compliant language identifier to a {@link Locale} instance. + * @param lang the language string + * @return the converted locale instance + */ + public static Locale convertRFC3066ToLocale(String lang) { + if (lang == null || lang.length() == 0) { + return null; + } + String[] parts = lang.split("-"); + if (parts.length == 1) { + return new Locale(parts[0]); + } else { + return new Locale(parts[0], parts[1]); + } + } + } |