diff options
author | Jeremias Maerki <jeremias@apache.org> | 2009-02-19 18:04:18 +0000 |
---|---|---|
committer | Jeremias Maerki <jeremias@apache.org> | 2009-02-19 18:04:18 +0000 |
commit | 223eb5df1f7597ac5269eb8dce45b6a7450144b8 (patch) | |
tree | b6475383dd7fca2a53402b19d22b682276d8d6d1 /src/java/org/apache/fop/pdf | |
parent | 51c210eea6bf627053854359388c1ad14203c0aa (diff) | |
download | xmlgraphics-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.java | 9 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFFactory.java | 42 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFLink.java | 12 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFPage.java | 17 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFParentTree.java | 50 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFRoot.java | 30 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFStructElem.java | 196 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFStructTreeRoot.java | 67 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFTextUtil.java | 52 |
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(); } |