aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org
diff options
context:
space:
mode:
authorJeremias Maerki <jeremias@apache.org>2009-04-17 12:10:07 +0000
committerJeremias Maerki <jeremias@apache.org>2009-04-17 12:10:07 +0000
commit8eefdf7d68cc640ca4c3d4ac331e5af1a9049ee0 (patch)
treed7ff2463e2e2ce6a322e9df1796a8b205f2c5440 /src/java/org
parenteeb0cc4fb8f3816e7507ce22de0fb4b9dd433f1c (diff)
downloadxmlgraphics-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')
-rw-r--r--src/java/org/apache/fop/area/AreaTreeParser.java3
-rw-r--r--src/java/org/apache/fop/pdf/PDFStructElem.java29
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFContext.java19
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFParser.java22
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFRenderer.java14
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFSerializer.java25
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java31
-rw-r--r--src/java/org/apache/fop/util/XMLUtil.java36
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]);
+ }
+ }
+
}