aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/fop/pdf
diff options
context:
space:
mode:
authorJeremias Maerki <jeremias@apache.org>2009-02-19 18:04:18 +0000
committerJeremias Maerki <jeremias@apache.org>2009-02-19 18:04:18 +0000
commit223eb5df1f7597ac5269eb8dce45b6a7450144b8 (patch)
treeb6475383dd7fca2a53402b19d22b682276d8d6d1 /src/java/org/apache/fop/pdf
parent51c210eea6bf627053854359388c1ad14203c0aa (diff)
downloadxmlgraphics-fop-223eb5df1f7597ac5269eb8dce45b6a7450144b8.tar.gz
xmlgraphics-fop-223eb5df1f7597ac5269eb8dce45b6a7450144b8.zip
Bugzilla #46705:
Accessibility and Tagged PDF Support Submitted by: Jost Klopfstein <jost.klopfstein.at.gmail.com> Changes to patch by jeremias: - Some style fixes - Various simplifications - Removal of dead code - Addressed some issues raised in Bugzilla (work in progress) - Fixed a couple of bugs on leader handling detected while testing (an NPE remains in leader_leader-pattern_use-content.xml) git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_Accessibility@745949 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/fop/pdf')
-rw-r--r--src/java/org/apache/fop/pdf/PDFArray.java9
-rw-r--r--src/java/org/apache/fop/pdf/PDFFactory.java42
-rw-r--r--src/java/org/apache/fop/pdf/PDFLink.java12
-rw-r--r--src/java/org/apache/fop/pdf/PDFPage.java17
-rw-r--r--src/java/org/apache/fop/pdf/PDFParentTree.java50
-rw-r--r--src/java/org/apache/fop/pdf/PDFRoot.java30
-rw-r--r--src/java/org/apache/fop/pdf/PDFStructElem.java196
-rw-r--r--src/java/org/apache/fop/pdf/PDFStructTreeRoot.java67
-rw-r--r--src/java/org/apache/fop/pdf/PDFTextUtil.java52
9 files changed, 470 insertions, 5 deletions
diff --git a/src/java/org/apache/fop/pdf/PDFArray.java b/src/java/org/apache/fop/pdf/PDFArray.java
index 7c5f8ba9b..a7dfc388e 100644
--- a/src/java/org/apache/fop/pdf/PDFArray.java
+++ b/src/java/org/apache/fop/pdf/PDFArray.java
@@ -107,6 +107,15 @@ public class PDFArray extends PDFObject {
}
/**
+ * Indicates whether the given object exists in the array.
+ * @param obj the object to look for
+ * @return true if obj is contained
+ */
+ public boolean contains(Object obj) {
+ return this.values.contains(obj);
+ }
+
+ /**
* Returns the length of the array
* @return the length of the array
*/
diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java
index 0a4516ce6..087ae4277 100644
--- a/src/java/org/apache/fop/pdf/PDFFactory.java
+++ b/src/java/org/apache/fop/pdf/PDFFactory.java
@@ -177,18 +177,26 @@ public class PDFFactory {
* @param pageWidth width of the page in points
* @param pageHeight height of the page in points
* @param pageIndex index of the page (zero-based)
+ * @param currentPageParentKey the integer key in the structural parent tree
*
* @return the created /Page object
*/
public PDFPage makePage(PDFResources resources,
- int pageWidth, int pageHeight, int pageIndex) {
-
+ int pageWidth, int pageHeight, int pageIndex,
+ int currentPageParentKey) {
/*
* create a PDFPage with the next object number, the given
* resources, contents and dimensions
*/
- PDFPage page = new PDFPage(resources,
+ PDFPage page = new PDFPage(resources, // old numPages
pageWidth, pageHeight, pageIndex);
+ if (currentPageParentKey > -1) {
+ //Accessibility is enabled
+ page.setStructParents(currentPageParentKey);
+ //This is a PDF 1.5 feature. It is set as a work-around for a bug in Adobe Acrobat
+ //which reports this missing even if the PDF file is PDF 1.4.
+ page.setTabs(new PDFName("S"));
+ }
getDocument().assignObjectNumber(page);
getDocument().getPages().addPage(page);
@@ -203,6 +211,23 @@ public class PDFFactory {
* @param resources resources object to use
* @param pageWidth width of the page in points
* @param pageHeight height of the page in points
+ * @param pageIndex index of the page (zero-based)
+ *
+ * @return the created /Page object
+ */
+ public PDFPage makePage(PDFResources resources,
+ int pageWidth, int pageHeight, int pageIndex) {
+ return makePage(resources, pageWidth, pageHeight, pageIndex, -1);
+ }
+
+ /**
+ * Make a /Page object. The page is assigned an object number immediately
+ * so references can already be made. The page must be added to the
+ * PDFDocument later using addObject().
+ *
+ * @param resources resources object to use
+ * @param pageWidth width of the page in points
+ * @param pageHeight height of the page in points
*
* @return the created /Page object
*/
@@ -868,6 +893,17 @@ public class PDFFactory {
}
/**
+ * Creates and returns a StructTreeRoot object. Used for accessibility.
+ * @return structure Tree Root element
+ */
+ public PDFStructTreeRoot makeStructTreeRoot() {
+ PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot();
+ getDocument().assignObjectNumber(structTreeRoot);
+ getDocument().addTrailerObject(structTreeRoot);
+ return structTreeRoot;
+ }
+
+ /**
* Make a the head object of the name dictionary (the /Dests object).
*
* @param destinationList a list of PDFDestination instances
diff --git a/src/java/org/apache/fop/pdf/PDFLink.java b/src/java/org/apache/fop/pdf/PDFLink.java
index a7c4c6548..620e5d51d 100644
--- a/src/java/org/apache/fop/pdf/PDFLink.java
+++ b/src/java/org/apache/fop/pdf/PDFLink.java
@@ -42,6 +42,7 @@ public class PDFLink extends PDFObject {
private float bry;
private String color;
private PDFAction action;
+ private Integer structParent;
/**
* create objects associated with a link annotation (GoToR)
@@ -68,6 +69,15 @@ public class PDFLink extends PDFObject {
this.action = action;
}
+
+ /**
+ * Used for accessibility
+ * @param mcid of this structParent
+ */
+ public void setStructParent(int mcid) {
+ this.structParent = new Integer(mcid);
+ }
+
/**
* {@inheritDoc}
*/
@@ -87,6 +97,8 @@ public class PDFLink extends PDFObject {
+ (brx) + " " + (bry) + " ]\n" + "/C [ "
+ this.color + " ]\n" + "/Border [ 0 0 0 ]\n" + "/A "
+ this.action.getAction() + "\n" + "/H /I\n"
+ + (this.structParent != null
+ ? "/StructParent " + this.structParent.toString() + "\n" : "")
+ fFlag + "\n>>\nendobj\n";
return s;
}
diff --git a/src/java/org/apache/fop/pdf/PDFPage.java b/src/java/org/apache/fop/pdf/PDFPage.java
index d1472e154..6cc8c3e57 100644
--- a/src/java/org/apache/fop/pdf/PDFPage.java
+++ b/src/java/org/apache/fop/pdf/PDFPage.java
@@ -146,4 +146,21 @@ public class PDFPage extends PDFResourceContext {
return this.pageIndex;
}
+ /**
+ * Sets the "StructParents" value.
+ * @param structParents the integer key of this object's entry in the structural parent tree.
+ */
+ public void setStructParents(int structParents) {
+ put("StructParents", structParents);
+ }
+
+ /**
+ * Specifies the tab order for annotations on a page.
+ * @param value one of the allowed values (see PDF 1.5)
+ * @since PDF 1.5
+ */
+ public void setTabs(PDFName value) {
+ put("Tabs", value);
+ }
+
}
diff --git a/src/java/org/apache/fop/pdf/PDFParentTree.java b/src/java/org/apache/fop/pdf/PDFParentTree.java
new file mode 100644
index 000000000..7528aa299
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFParentTree.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+/**
+ * Class representing a PDF /ParentTree
+ */
+public class PDFParentTree extends PDFNumberTreeNode {
+
+ /**
+ * Create the /ParentTree NumberTreeNode
+ */
+ public PDFParentTree() {
+ super();
+ }
+
+ /**
+ * Get the parentTree.
+ * @return parentTree as PDFNumsArray
+ */
+ public PDFNumsArray getNums() {
+ PDFNumsArray nums = super.getNums();
+ if (nums == null) {
+ nums = new PDFNumsArray(this);
+ setNums(nums);
+ }
+ return nums;
+ }
+}
+
+
+
+
diff --git a/src/java/org/apache/fop/pdf/PDFRoot.java b/src/java/org/apache/fop/pdf/PDFRoot.java
index 1ea316390..c8d94585c 100644
--- a/src/java/org/apache/fop/pdf/PDFRoot.java
+++ b/src/java/org/apache/fop/pdf/PDFRoot.java
@@ -63,7 +63,7 @@ public class PDFRoot extends PDFDictionary {
*/
public PDFRoot(int objnum, PDFPages pages) {
super();
- setObjectNumber(objnum);
+ setObjectNumber(objnum);
put("Type", new PDFName("Catalog"));
setRootPages(pages);
}
@@ -252,4 +252,32 @@ public class PDFRoot extends PDFDictionary {
put("Lang", lang);
}
+ /**
+ * Sets the StructTreeRoot object. Used for accessibility.
+ * @param structTreeRoot of this document
+ */
+ public void setStructTreeRoot(PDFStructTreeRoot structTreeRoot) {
+ if (structTreeRoot == null) {
+ throw new NullPointerException("structTreeRoot must not be null");
+ }
+ put("StructTreeRoot", structTreeRoot);
+ }
+
+ /**
+ * Returns the StructTreeRoot object.
+ * @return the structure tree root (or null if accessibility is not enabled)
+ */
+ public PDFStructTreeRoot getStructTreeRoot() {
+ return (PDFStructTreeRoot)get("StructTreeRoot");
+ }
+
+ /**
+ * Marks this document as conforming to the Tagged PDF conventions.
+ */
+ public void makeTagged() {
+ PDFDictionary dict = new PDFDictionary();
+ dict.put("Marked", Boolean.TRUE);
+ put("MarkInfo", dict); //new PDFMarkInfo()
+ }
+
}
diff --git a/src/java/org/apache/fop/pdf/PDFStructElem.java b/src/java/org/apache/fop/pdf/PDFStructElem.java
new file mode 100644
index 000000000..408b1f2c7
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFStructElem.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+/**
+ * Class representing a PDF Structure Element.
+ */
+public class PDFStructElem extends PDFDictionary {
+
+ private PDFObject parentObject = null;
+ private String source = "";
+ private boolean level1 = false;
+
+ /**
+ * Create the /StructTreeRoot dictionary
+ * @param fo passed in fo object
+ * @param parent Parent of this PDFStructElem
+ */
+ public PDFStructElem(String fo, PDFObject parent) {
+ super();
+ if (parent instanceof PDFStructElem) {
+ parentObject = (PDFStructElem) parent;
+ }
+ put("Type", new PDFName("StructElem"));
+ source = fo;
+ //TODO Move this into the render/pdf package. The PDF library shall not contain FO knowledge
+ if ("block".equals(fo)) {
+ put("S", new PDFName("P"));
+ } else if ("inline".equals(fo) || "wrapper".equals(fo) || "character".equals(fo)) {
+ put("S", new PDFName("Span"));
+ } else if ("table-cell".equals(fo)) {
+ PDFStructElem grandParent = (PDFStructElem)
+ ((PDFStructElem)parent).getParentStructElem();
+ String s = grandParent.getSource();
+ if ("table-header".equals(s)) {
+ put("S", new PDFName("TH"));
+ } else {
+ put("S", new PDFName("TD"));
+ }
+ } else if ("table-row".equals(fo)) {
+ put("S", new PDFName("TR"));
+ } else if ("root".equals(fo)) {
+ put("S", new PDFName("Document"));
+ } else if ("page-sequence".equals(fo)) {
+ put("S", new PDFName("Part"));
+ } else if ("flow".equals(fo) || "static-content".equals(fo)) {
+ put("S", new PDFName("Sect"));
+ } else if ("page-number".equals(fo) || "page-number-citation".equals(fo)
+ || "page-number-citation-last".equals(fo)) {
+ put("S", new PDFName("Quote"));
+ } else if ("external-graphic".equals(fo) || "instream-foreign-object".equals(fo)) {
+ put("S", new PDFName("Figure"));
+ } else if ("table".equals(fo)) {
+ put("S", new PDFName("Table"));
+ } else if ("table-body".equals(fo)) {
+ put("S", new PDFName("TBody"));
+ } else if ("table-header".equals(fo)) {
+ put("S", new PDFName("THead"));
+ } else if ("table-footer".equals(fo)) {
+ put("S", new PDFName("TFoot"));
+ } else if ("list-block".equals(fo)) {
+ put("S", new PDFName("L"));
+ } else if ("list-item".equals(fo)) {
+ put("S", new PDFName("LI"));
+ } else if ("list-item-label".equals(fo)) {
+ put("S", new PDFName("Lbl"));
+ } else if ("list-item-body".equals(fo)) {
+ put("S", new PDFName("LBody"));
+ } else if ("block-container".equals(fo)) {
+ put("S", new PDFName("Div"));
+ } else if ("basic-link".equals(fo)) {
+ put("S", new PDFName("Link"));
+ } else if ("footnote".equals(fo)) {
+ put("S", new PDFName("Note"));
+ } else if ("footnote-body".equals(fo)) {
+ put("S", new PDFName("Sect"));
+ } else if ("marker".equals(fo)) {
+ put("S", new PDFName("Private"));
+ } else {
+ log.error("Accessibility: PDFStructElem constructor is missing: " + fo);
+ }
+ setParent(parent);
+ if (!"external-graphic".equals(fo) && !"instream-foreign-object".equals(fo)) {
+ put("K", new PDFArray());
+ }
+ }
+
+ /**
+ * This method is called for PDFStructElements which are direct children of
+ * fo:static-content or fo:flow-section
+ */
+ public void setLevel1() {
+ this.level1 = true;
+ }
+
+ /**
+ *
+ * @return true if the PDFStructElement is a direct child of
+ * fo:static-content or fo:flow-section
+ */
+ public boolean getLevel1() {
+ return this.level1;
+ }
+
+ /**
+ * Get the parent
+ * @return PDFStructElem of parent
+ */
+ public PDFObject getParentStructElem() {
+ return (PDFStructElem)this.parentObject;
+ }
+
+ /**
+ * Set the parent for this StructElem
+ * @param parent to be added
+ */
+ public void setParent(PDFObject parent) {
+ if (parent != null) {
+ put("P", new PDFReference(parent));
+ }
+ }
+
+ /**
+ * Get the source of this StructElem
+ * @return the source
+ */
+ public String getSource() {
+ return source;
+ }
+
+ /**
+ * The kids of this StructElem
+ * @return the kids
+ */
+ public PDFArray getKids() {
+ return (PDFArray)get("K");
+ }
+
+ /**
+ * Add a kid to this strucElem
+ * @param kid to be added
+ */
+ public void addKid(PDFObject kid) {
+ getKids().add(kid);
+ }
+
+ /**
+ * Add a kid, but only if it does not already exist
+ * @param kid to be added
+ * @return true if kid did not already exist
+ */
+ public boolean addUniqueKid(PDFObject kid) {
+ PDFArray mArray = getKids();
+ if (!mArray.contains(kid)) {
+ getKids().add(kid);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Add kid referenced through mcid integer
+ * used fo:external-graphic
+ * @param mcid of this kid
+ */
+ public void addMCIDKid(int mcid) {
+ put("K", mcid);
+ }
+
+ /**
+ * Add a page reference to this structElem
+ * @param pageObject to be added
+ */
+ public void addPage(Object pageObject) {
+ put("Pg", (PDFObject) pageObject);
+ }
+
+}
diff --git a/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java b/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java
new file mode 100644
index 000000000..e530b1582
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+/**
+ * Class representing a PDF /StructTreeRoot dictionary.
+ */
+public class PDFStructTreeRoot extends PDFDictionary {
+
+ /**
+ * Create the /StructTreeRoot dictionary.
+ */
+ public PDFStructTreeRoot() {
+ super();
+ put("Type", new PDFName("StructTreeRoot"));
+ put("K", new PDFArray());
+ }
+
+ /**
+ * Add parentTree entry.
+ * @param parentTree to be added
+ */
+ public void addParentTree(PDFParentTree parentTree) {
+ put("ParentTree", parentTree);
+ }
+
+ /**
+ * Get the kids.
+ * @return the kids
+ */
+ public PDFArray getKids() {
+ return (PDFArray)get("K");
+ }
+
+ /**
+ * Returns the first child of the kids array (normally the structure tree root element)
+ * @return the first child
+ */
+ public PDFObject getFirstChild() {
+ return (PDFObject)getKids().get(0);
+ }
+
+ /**
+ * Adds a kid.
+ * @param kid to be added
+ */
+ public void addKid(PDFObject kid) {
+ getKids().add(kid);
+ }
+} \ No newline at end of file
diff --git a/src/java/org/apache/fop/pdf/PDFTextUtil.java b/src/java/org/apache/fop/pdf/PDFTextUtil.java
index 6640f9b80..bb8816995 100644
--- a/src/java/org/apache/fop/pdf/PDFTextUtil.java
+++ b/src/java/org/apache/fop/pdf/PDFTextUtil.java
@@ -48,6 +48,7 @@ public abstract class PDFTextUtil {
public static final int TR_CLIP = 7;
private boolean inTextObject = false;
+ private boolean artifactMode = false;
private String startText;
private String endText;
private boolean useMultiByte;
@@ -116,6 +117,15 @@ public abstract class PDFTextUtil {
}
/**
+ * Indicates whether we are in a text object and if that text object represents
+ * an artifact.
+ * @return true if in artifact-mode text object
+ */
+ public boolean inArtifactMode() {
+ return this.artifactMode;
+ }
+
+ /**
* Called when a new text object should be started. Be sure to call setFont() before
* issuing any text painting commands.
*/
@@ -128,11 +138,51 @@ public abstract class PDFTextUtil {
}
/**
+ * Begin of a regular text object, used for accessibility
+ * @param mcid of text object
+ * @param structElemType of parent
+ */
+ public void beginTextObjectAccess(int mcid, String structElemType) {
+ if (inTextObject) {
+ throw new IllegalStateException("Already in text object");
+ }
+ write(structElemType + " <</MCID "
+ + String.valueOf(mcid) + ">>\nBDC\nBT\n");
+ this.inTextObject = true;
+ }
+
+ /**
+ * Begin of a text object marked as artifact (fo:leader in XSL-FO) text object.
+ * Used for accessibility.
+ */
+ public void beginArtifactTextObject() {
+ if (inTextObject) {
+ throw new IllegalStateException("Already in text object");
+ }
+ write("/Artifact\nBMC\nBT\n");
+ this.inTextObject = true;
+ this.artifactMode = true;
+ }
+
+ /**
* Called when a text object should be ended.
*/
public void endTextObject() {
+ endTextObject(false);
+ }
+
+ /**
+ * Called when a text object should be ended.
+ * @param accessEnabled indicating if accessibility is turned on or not
+ */
+ public void endTextObject(boolean accessEnabled) {
checkInTextObject();
- write("ET\n");
+ if (accessEnabled) {
+ write("ET\nEMC\n");
+ } else {
+ write("ET\n");
+ }
+ this.artifactMode = false;
this.inTextObject = false;
initValues();
}