Fixed bug occurring when a block contains only one line of text: the generated PDF was illegal Fixed bug in the generation of the structural parent tree: values in a number tree must be indirect references (note: other number trees in the document may still be wrongly generated) git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_Accessibility@824407 13f79535-47bb-0310-9956-ffa450edef68tags/fop-1_0
@@ -354,6 +354,25 @@ public class PDFDocument { | |||
return this.root; | |||
} | |||
/** | |||
* Makes sure a Lang entry has been set on the document catalog, setting it | |||
* to a default value if necessary. When accessibility is enabled the | |||
* language must be specified for any text element in the document. | |||
*/ | |||
public void enforceLanguageOnRoot() { | |||
if (root.getLanguage() == null) { | |||
String fallbackLanguage; | |||
if (getProfile().getPDFAMode().isPDFA1LevelA()) { | |||
//According to Annex B of ISO-19005-1:2005(E), section B.2 | |||
fallbackLanguage = "x-unknown"; | |||
} else { | |||
//No language has been set on the first page-sequence, so fall back to "en". | |||
fallbackLanguage = "en"; | |||
} | |||
root.setLanguage(fallbackLanguage); | |||
} | |||
} | |||
/** | |||
* Get the {@link PDFInfo} object for this document. | |||
* |
@@ -185,20 +185,12 @@ public class PDFFactory { | |||
*/ | |||
public PDFPage makePage(PDFResources resources, int pageIndex, | |||
Rectangle2D mediaBox, Rectangle2D cropBox, | |||
Rectangle2D bleedBox, Rectangle2D trimBox, | |||
int currentPageParentKey) { | |||
Rectangle2D bleedBox, Rectangle2D trimBox) { | |||
/* | |||
* create a PDFPage with the next object number, the given | |||
* resources, contents and dimensions | |||
*/ | |||
PDFPage page = new PDFPage(resources, pageIndex, mediaBox, cropBox, bleedBox, trimBox); | |||
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); | |||
@@ -220,7 +212,7 @@ public class PDFFactory { | |||
public PDFPage makePage(PDFResources resources, | |||
int pageWidth, int pageHeight, int pageIndex) { | |||
Rectangle2D mediaBox = new Rectangle2D.Double(0, 0, pageWidth, pageHeight); | |||
return makePage(resources, pageIndex, mediaBox, mediaBox, mediaBox, mediaBox, -1); | |||
return makePage(resources, pageIndex, mediaBox, mediaBox, mediaBox, mediaBox); | |||
} | |||
/** | |||
@@ -897,15 +889,33 @@ public class PDFFactory { | |||
/** | |||
* Creates and returns a StructTreeRoot object. Used for accessibility. | |||
* @param parentTree the value of the ParenTree entry | |||
* @return structure Tree Root element | |||
*/ | |||
public PDFStructTreeRoot makeStructTreeRoot() { | |||
PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(); | |||
public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) { | |||
PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree); | |||
getDocument().assignObjectNumber(structTreeRoot); | |||
getDocument().addTrailerObject(structTreeRoot); | |||
getDocument().getRoot().setStructTreeRoot(structTreeRoot); | |||
return structTreeRoot; | |||
} | |||
/** | |||
* Creates and returns a StructElem object. | |||
* | |||
* @param structureType the structure type of the new element (value for the | |||
* S entry) | |||
* @param parent the parent of the new structure element in the structure | |||
* hierarchy | |||
* @return the newly created element | |||
*/ | |||
public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) { | |||
PDFStructElem structElem = new PDFStructElem(parent, structureType); | |||
getDocument().assignObjectNumber(structElem); | |||
getDocument().addTrailerObject(structElem); | |||
return structElem; | |||
} | |||
/** | |||
* Make a the head object of the name dictionary (the /Dests object). | |||
* |
@@ -71,11 +71,12 @@ public class PDFLink extends PDFObject { | |||
/** | |||
* Used for accessibility | |||
* @param mcid of this structParent | |||
* Sets the value of the StructParent entry for this link. | |||
* | |||
* @param structParent key in the structure parent tree | |||
*/ | |||
public void setStructParent(int mcid) { | |||
this.structParent = new Integer(mcid); | |||
public void setStructParent(int structParent) { | |||
this.structParent = new Integer(structParent); | |||
} | |||
/** |
@@ -52,13 +52,31 @@ public class PDFNumsArray extends PDFObject { | |||
return this.map.size(); | |||
} | |||
/** | |||
* Sets an entry. | |||
* @param key the key of the value to set | |||
* @param obj the new value | |||
*/ | |||
public void put(Integer key, Object obj) { | |||
this.map.put(key, obj); | |||
} | |||
/** | |||
* Sets an entry. | |||
* @param key the key of the value to set | |||
* @param obj the new value | |||
*/ | |||
public void put(int key, Object obj) { | |||
this.map.put(new Integer(key), obj); | |||
put(new Integer(key), obj); | |||
} | |||
/** | |||
* Gets an entry. | |||
* @param key the key of requested value | |||
* @return the requested value | |||
*/ | |||
public Object get(Integer key) { | |||
return this.map.get(key); | |||
} | |||
/** | |||
@@ -67,7 +85,7 @@ public class PDFNumsArray extends PDFObject { | |||
* @return the requested value | |||
*/ | |||
public Object get(int key) { | |||
return this.map.get(new Integer(key)); | |||
return get(new Integer(key)); | |||
} | |||
/** {@inheritDoc} */ |
@@ -160,6 +160,18 @@ public class PDFPage extends PDFResourceContext { | |||
*/ | |||
public void setStructParents(int structParents) { | |||
put("StructParents", structParents); | |||
//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. | |||
setTabs(new PDFName("S")); | |||
} | |||
/** | |||
* Returns the value of the StructParents entry. | |||
* | |||
* @return the StructParents value, <code>null</code> if the entry has not been set | |||
*/ | |||
public Integer getStructParents() { | |||
return (Integer) get("StructParents"); | |||
} | |||
/** |
@@ -28,53 +28,34 @@ import org.apache.fop.util.XMLUtil; | |||
*/ | |||
public class PDFStructElem extends PDFDictionary { | |||
private PDFObject parentObject = null; | |||
private boolean level1 = false; | |||
private PDFStructElem parentElement; | |||
/** | |||
* Create the /StructTreeRoot dictionary | |||
* @param parent Parent of this PDFStructElem | |||
* @param structureType the structure type for the element | |||
* Creates a new structure element. | |||
* | |||
* @param parent parent of this element | |||
* @param structureType the structure type of this element | |||
*/ | |||
public PDFStructElem(PDFObject parent, PDFName structureType) { | |||
super(); | |||
PDFStructElem(PDFObject parent, PDFName structureType) { | |||
if (parent instanceof PDFStructElem) { | |||
parentObject = (PDFStructElem) parent; | |||
parentElement = (PDFStructElem) parent; | |||
} | |||
put("Type", new PDFName("StructElem")); | |||
setStructureType(structureType); | |||
put("S", structureType); | |||
setParent(parent); | |||
} | |||
/** | |||
* This method is called for PDFStructElements which are direct children of | |||
* fo:static-content or fo:flow-section | |||
*/ | |||
public void setLevel1() { | |||
this.level1 = true; | |||
} | |||
/** | |||
* Returns the parent of this structure element. | |||
* | |||
* @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 | |||
* @return the parent, <code>null</code> if the parent is not a structure | |||
* element (i.e., is the structure tree root) | |||
*/ | |||
public PDFObject getParentStructElem() { | |||
return (PDFStructElem)this.parentObject; | |||
public PDFStructElem getParentStructElem() { | |||
return parentElement; | |||
} | |||
/** | |||
* Set the parent for this StructElem | |||
* @param parent to be added | |||
*/ | |||
/** {@inheritDoc} */ | |||
public void setParent(PDFObject parent) { | |||
if (parent != null) { | |||
put("P", new PDFReference(parent)); | |||
@@ -82,16 +63,20 @@ public class PDFStructElem extends PDFDictionary { | |||
} | |||
/** | |||
* The kids of this StructElem | |||
* @return the kids | |||
* Returns the kids of this structure element. | |||
* | |||
* @return the value of the K entry | |||
*/ | |||
public PDFArray getKids() { | |||
return (PDFArray)get("K"); | |||
private PDFArray getKids() { | |||
return (PDFArray) get("K"); | |||
} | |||
/** | |||
* Add a kid to this strucElem | |||
* @param kid to be added | |||
* Add a kid to this structure element. This element will then add itself to | |||
* its parent structure element if it has not already, and so will the | |||
* parent, and so on. | |||
* | |||
* @param kid element to be added | |||
*/ | |||
public void addKid(PDFObject kid) { | |||
PDFArray kids = getKids(); | |||
@@ -100,50 +85,46 @@ public class PDFStructElem extends PDFDictionary { | |||
put("K", kids); | |||
} | |||
kids.add(kid); | |||
joinHierarchy(); | |||
} | |||
/** | |||
* 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 == null || !mArray.contains(kid)) { | |||
addKid(kid); | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
private boolean containsKid(PDFObject kid) { | |||
PDFArray kids = getKids(); | |||
return kids != null && kids.contains(kid); | |||
} | |||
/** | |||
* Add kid referenced through mcid integer. Used for images. | |||
* @param mcid of this kid | |||
*/ | |||
public void addMCIDKid(int mcid) { | |||
put("K", mcid); | |||
private void joinHierarchy() { | |||
if (parentElement != null && !parentElement.containsKid(this)) { | |||
parentElement.addKid(this); | |||
} | |||
} | |||
/** | |||
* Add a page reference to this structElem | |||
* @param pageObject to be added | |||
* Sets the given mcid as the kid of this structure element. This element | |||
* will then add itself to its parent structure element if it has not | |||
* already, and so will the parent, and so on. | |||
* | |||
* @param mcid mcid of the marked-content sequence corresponding to this | |||
* structure element's kid | |||
*/ | |||
public void addPage(Object pageObject) { | |||
put("Pg", (PDFObject) pageObject); | |||
public void setMCIDKid(int mcid) { | |||
put("K", mcid); | |||
joinHierarchy(); | |||
} | |||
/** | |||
* Sets the structure type (the "S" entry). | |||
* @param type the structure type | |||
* Sets the page reference of this structure element. | |||
* | |||
* @param page value for the Pg entry | |||
*/ | |||
public void setStructureType(PDFName type) { | |||
put("S", type); | |||
public void setPage(PDFPage page) { | |||
put("Pg", page); | |||
} | |||
/** | |||
* Returns the structure type of this structure element. | |||
* @return the structure type | |||
* | |||
* @return the value of the S entry | |||
*/ | |||
public PDFName getStructureType() { | |||
return (PDFName)get("S"); | |||
@@ -154,13 +135,14 @@ public class PDFStructElem extends PDFDictionary { | |||
* @param language the language (as defined in the section about | |||
* "Natural Language Specification") | |||
*/ | |||
public void setLanguage(String language) { | |||
private void setLanguage(String language) { | |||
put("Lang", language); | |||
} | |||
/** | |||
* Sets the language of this structure element. | |||
* @param language the language | |||
* | |||
* @param language a value for the Lang entry | |||
*/ | |||
public void setLanguage(Locale language) { | |||
setLanguage(XMLUtil.toRFC3066(language)); | |||
@@ -168,7 +150,8 @@ public class PDFStructElem extends PDFDictionary { | |||
/** | |||
* Returns the language of this structure element. | |||
* @return the language (or null if no language was specified) | |||
* | |||
* @return the value of the Lang entry (<code>null</code> if no language was specified) | |||
*/ | |||
public String getLanguage() { | |||
return (String)get("Lang"); |
@@ -26,18 +26,11 @@ public class PDFStructTreeRoot extends PDFDictionary { | |||
/** | |||
* Create the /StructTreeRoot dictionary. | |||
* @param parentTree the value of the ParenTree entry | |||
*/ | |||
public PDFStructTreeRoot() { | |||
super(); | |||
PDFStructTreeRoot(PDFParentTree parentTree) { | |||
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); | |||
} | |||
@@ -49,14 +42,6 @@ public class PDFStructTreeRoot extends PDFDictionary { | |||
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 |
@@ -28,7 +28,7 @@ import org.apache.fop.pdf.PDFStructElem; | |||
/** | |||
* This class provides the standard mappings from Formatting Objects to PDF structure types. | |||
*/ | |||
public class FOToPDFRoleMap { | |||
public final class FOToPDFRoleMap { | |||
private static final Map STANDARD_MAPPINGS = new java.util.HashMap(); | |||
@@ -138,4 +138,5 @@ public class FOToPDFRoleMap { | |||
} | |||
private FOToPDFRoleMap() { } | |||
} |
@@ -26,34 +26,23 @@ import java.awt.geom.Point2D; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.geom.Rectangle2D.Double; | |||
import java.io.IOException; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.w3c.dom.NodeList; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.w3c.dom.Node; | |||
import org.w3c.dom.NodeList; | |||
import org.apache.xmlgraphics.xmp.Metadata; | |||
import org.apache.fop.accessibility.StructureTree; | |||
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.pdf.PDFStructTreeRoot; | |||
import org.apache.fop.render.extensions.prepress.PageBoundaries; | |||
import org.apache.fop.render.extensions.prepress.PageScale; | |||
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler; | |||
@@ -72,32 +61,11 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { | |||
/** logging instance */ | |||
private static Log log = LogFactory.getLog(PDFDocumentHandler.class); | |||
/** the following variables are used for accessibility */ | |||
private int pageSequenceCounter; | |||
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 List parentTreeList = new java.util.ArrayList(); | |||
private static final 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; | |||
} | |||
} | |||
private int pageSequenceNumber; | |||
private boolean accessEnabled; | |||
private PDFLogicalStructureHandler logicalStructureHandler; | |||
/** the PDF Document being created */ | |||
protected PDFDocument pdfDoc; | |||
@@ -165,6 +133,10 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { | |||
return this.pdfUtil; | |||
} | |||
PDFLogicalStructureHandler getLogicalStructureHandler() { | |||
return logicalStructureHandler; | |||
} | |||
/** {@inheritDoc} */ | |||
public void startDocument() throws IFException { | |||
super.startDocument(); | |||
@@ -172,18 +144,8 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { | |||
this.pdfDoc = pdfUtil.setupPDFDocument(this.outputStream); | |||
this.accessEnabled = getUserAgent().isAccessibilityEnabled(); | |||
if (accessEnabled) { | |||
this.pdfDoc.getRoot().makeTagged(); | |||
log.info("Accessibility is enabled"); | |||
PDFStructTreeRoot structTreeRoot = this.pdfDoc.getFactory().makeStructTreeRoot(); | |||
this.pdfDoc.getRoot().setStructTreeRoot(structTreeRoot); | |||
PDFStructElem structElemDocument = new PDFStructElem(structTreeRoot, | |||
FOToPDFRoleMap.mapFormattingObject("root", structTreeRoot)); | |||
this.pdfDoc.assignObjectNumber(structElemDocument); | |||
this.pdfDoc.addTrailerObject(structElemDocument); | |||
structTreeRoot.addKid(structElemDocument); | |||
parentTree = new PDFParentTree(); | |||
pageSequenceCounter = 0; | |||
pdfDoc.getRoot().makeTagged(); | |||
logicalStructureHandler = new PDFLogicalStructureHandler(pdfDoc); | |||
} | |||
} catch (IOException e) { | |||
throw new IFException("I/O error in startDocument()", e); | |||
@@ -199,31 +161,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { | |||
public void endDocument() throws IFException { | |||
try { | |||
pdfDoc.getResources().addFonts(pdfDoc, fontInfo); | |||
if (getUserAgent().isAccessibilityEnabled()) { | |||
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); | |||
getStructTreeRoot().addParentTree(parentTree); | |||
pdfDoc.outputTrailer(this.outputStream); | |||
structElemType = null; | |||
parentTree = null; | |||
structTreeMap = null; | |||
parentTreeList = null; | |||
} else { | |||
pdfDoc.outputTrailer(this.outputStream); | |||
} | |||
pdfDoc.outputTrailer(this.outputStream); | |||
this.pdfDoc = null; | |||
pdfResources = null; | |||
@@ -236,11 +174,6 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { | |||
super.endDocument(); | |||
} | |||
private PDFStructTreeRoot getStructTreeRoot() { | |||
return this.pdfDoc.getRoot().getStructTreeRoot(); | |||
} | |||
/** {@inheritDoc} */ | |||
public void startPageSequence(String id) throws IFException { | |||
//TODO page sequence title | |||
@@ -251,53 +184,10 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { | |||
this.pdfDoc.getRoot().setLanguage(XMLUtil.toRFC3066(getContext().getLanguage())); | |||
} | |||
if (getUserAgent().isAccessibilityEnabled()) { | |||
processStructureTree(); | |||
} | |||
} | |||
private void processStructureTree() { | |||
if (this.pdfDoc.getRoot().getLanguage() == null) { | |||
String fallbackLanguage; | |||
if (this.pdfDoc.getProfile().getPDFAMode().isPDFA1LevelA()) { | |||
//According to Annex B of ISO-19005-1:2005(E), section B.2 | |||
fallbackLanguage = "x-unknown"; | |||
} else { | |||
//No language has been set on the first page-sequence, so fall back to "en". | |||
fallbackLanguage = "en"; | |||
} | |||
this.pdfDoc.getRoot().setLanguage(fallbackLanguage); | |||
} | |||
/* Retrieve the structure element of type "Document" */ | |||
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); | |||
StructureTree structureTree = getUserAgent().getStructureTree(); | |||
NodeList nodes = structureTree.getPageSequence(++pageSequenceCounter); | |||
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(structElemPart, | |||
FOToPDFRoleMap.mapFormattingObject(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); | |||
} | |||
} | |||
if (accessEnabled) { | |||
NodeList nodes = getUserAgent().getStructureTree().getPageSequence( | |||
++pageSequenceNumber); | |||
logicalStructureHandler.processStructureTree(nodes, getContext().getLanguage()); | |||
} | |||
} | |||
@@ -309,11 +199,6 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { | |||
/** {@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(); | |||
PageBoundaries boundaries = new PageBoundaries(size, getContext().getForeignAttributes()); | |||
@@ -340,8 +225,10 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { | |||
toPointAndScale(mediaBox, scaleX, scaleY), | |||
toPointAndScale(cropBox, scaleX, scaleY), | |||
toPointAndScale(bleedBox, scaleX, scaleY), | |||
toPointAndScale(trimBox, scaleX, scaleY), | |||
parentTreeKey); | |||
toPointAndScale(trimBox, scaleX, scaleY)); | |||
if (accessEnabled) { | |||
logicalStructureHandler.startPage(currentPage); | |||
} | |||
pdfUtil.generatePageLabel(index, name); | |||
@@ -366,7 +253,7 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { | |||
/** {@inheritDoc} */ | |||
public IFPainter startPageContent() throws IFException { | |||
return new PDFPainter(this); | |||
return new PDFPainter(this, logicalStructureHandler); | |||
} | |||
/** {@inheritDoc} */ | |||
@@ -376,6 +263,9 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { | |||
/** {@inheritDoc} */ | |||
public void endPage() throws IFException { | |||
if (accessEnabled) { | |||
logicalStructureHandler.endPage(); | |||
} | |||
try { | |||
this.documentNavigationHandler.commit(); | |||
this.pdfDoc.registerObject(generator.getStream()); | |||
@@ -429,196 +319,4 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { | |||
} | |||
} | |||
/** | |||
* Used for accessibility | |||
* @param position in parentTree | |||
* @param o reference of PDFObject to be added to parentTree | |||
*/ | |||
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 | |||
*/ | |||
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 | |||
*/ | |||
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 | |||
*/ | |||
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 | |||
*/ | |||
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 | |||
*/ | |||
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 | |||
*/ | |||
void addChildToStructElemText(String ptr, int mcid) { | |||
PDFStructElem tempStructElem = (PDFStructElem) structTreeMap.get(ptr); | |||
if (tempStructElem != null) { | |||
PDFDictionary dict = new PDFDictionary(); | |||
dict.put("Type", new PDFName("MCR")); | |||
dict.put("Pg", this.currentPage); | |||
dict.put("MCID", mcid); | |||
tempStructElem.addKid(dict); | |||
if (!tempStructElem.getLevel1()) { | |||
addMeToParent(tempStructElem); | |||
} | |||
} | |||
//tempStructElem is null, for example inside fo:leaders in which case | |||
//the text shall be marked as artifact | |||
} | |||
/** | |||
* 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 | |||
*/ | |||
void incMCID() { | |||
this.mcidKey++; | |||
} | |||
/** | |||
* MCID is a sequential number per page | |||
* @return MCID value | |||
*/ | |||
int getMCID() { | |||
return this.mcidKey; | |||
} | |||
/** | |||
* Used for accessibility | |||
* @param ptr pointer into map of all structElems | |||
* @return type of found structElem | |||
*/ | |||
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(parent, | |||
FOToPDFRoleMap.mapFormattingObject(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 | |||
*/ | |||
int getPageLinkCountPlusPageParentKey() { | |||
this.pageLinkCount++; | |||
return (this.parentTreeKey + this.pageLinkCount); | |||
} | |||
/** | |||
* used for accessibility | |||
* @return current parentTreeKey | |||
*/ | |||
int getCurrentParentTreeKey() { | |||
return this.parentTreeKey; | |||
} | |||
} |
@@ -117,10 +117,7 @@ public class PDFDocumentNavigationHandler implements IFDocumentNavigationHandler | |||
//accessibility: ptr has a value | |||
String ptr = link.getAction().getPtr(); | |||
if (ptr != null && ptr.length() > 0) { | |||
this.documentHandler.addLinkToStructElem(ptr, pdfLink); | |||
int id = this.documentHandler.getPageLinkCountPlusPageParentKey(); | |||
pdfLink.setStructParent(id); | |||
this.documentHandler.addToParentTree(id, pdfLink ); | |||
documentHandler.getLogicalStructureHandler().addLinkContentItem(pdfLink, ptr); | |||
} | |||
documentHandler.currentPage.addAnnotation(pdfLink); | |||
} |
@@ -0,0 +1,300 @@ | |||
/* | |||
* 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.render.pdf; | |||
import java.util.HashMap; | |||
import java.util.Locale; | |||
import java.util.Map; | |||
import org.w3c.dom.Node; | |||
import org.w3c.dom.NodeList; | |||
import org.apache.fop.pdf.PDFArray; | |||
import org.apache.fop.pdf.PDFDictionary; | |||
import org.apache.fop.pdf.PDFDocument; | |||
import org.apache.fop.pdf.PDFLink; | |||
import org.apache.fop.pdf.PDFName; | |||
import org.apache.fop.pdf.PDFPage; | |||
import org.apache.fop.pdf.PDFParentTree; | |||
import org.apache.fop.pdf.PDFStructElem; | |||
import org.apache.fop.pdf.PDFStructTreeRoot; | |||
/** | |||
* Handles the creation of the logical structure in the PDF document. | |||
*/ | |||
class PDFLogicalStructureHandler { | |||
private static final PDFName MCR = new PDFName("MCR"); | |||
private static final PDFName OBJR = new PDFName("OBJR"); | |||
private static final MarkedContentInfo ARTIFACT = new MarkedContentInfo(null, -1, null); | |||
private final PDFDocument pdfDoc; | |||
/** | |||
* Map of references to the corresponding structure elements. | |||
*/ | |||
private final Map structTreeMap = new HashMap(); | |||
private final PDFParentTree parentTree = new PDFParentTree(); | |||
private int parentTreeKey; | |||
private PDFPage currentPage; | |||
/** | |||
* The array of references, from marked-content sequences in the current | |||
* page, to their parent structure elements. This will be a value in the | |||
* structure parent tree, whose corresponding key will be the page's | |||
* StructParents entry. | |||
*/ | |||
private PDFArray pageParentTreeArray; | |||
private PDFStructElem rootStructureElement; | |||
/** | |||
* Class providing the necessary information for bracketing content | |||
* associated to a structure element as a marked-content sequence. | |||
*/ | |||
static final class MarkedContentInfo { | |||
/** | |||
* A value that can be used for the tag operand of a marked-content | |||
* operator. This is the structure type of the corresponding structure | |||
* element. | |||
*/ | |||
final String tag; | |||
/** | |||
* The value for the MCID entry of the marked-content sequence's property list. | |||
*/ | |||
final int mcid; | |||
private final PDFStructElem parent; | |||
private MarkedContentInfo(String tag, int mcid, PDFStructElem parent) { | |||
this.tag = tag; | |||
this.mcid = mcid; | |||
this.parent = parent; | |||
} | |||
} | |||
/** | |||
* Creates a new instance for handling the logical structure of the given document. | |||
* | |||
* @param pdfDoc a document | |||
*/ | |||
PDFLogicalStructureHandler(PDFDocument pdfDoc) { | |||
this.pdfDoc = pdfDoc; | |||
PDFStructTreeRoot structTreeRoot = pdfDoc.getFactory().makeStructTreeRoot(parentTree); | |||
rootStructureElement = pdfDoc.getFactory().makeStructureElement( | |||
FOToPDFRoleMap.mapFormattingObject("root", structTreeRoot), structTreeRoot); | |||
structTreeRoot.addKid(rootStructureElement); | |||
} | |||
/** | |||
* Converts the given structure tree into PDF. | |||
* | |||
* @param structureTree the structure tree of the current page sequence | |||
* @param language language set on the page sequence | |||
*/ | |||
void processStructureTree(NodeList structureTree, Locale language) { | |||
pdfDoc.enforceLanguageOnRoot(); | |||
PDFStructElem structElemPart = pdfDoc.getFactory().makeStructureElement( | |||
FOToPDFRoleMap.mapFormattingObject("page-sequence", rootStructureElement), | |||
rootStructureElement); | |||
rootStructureElement.addKid(structElemPart); | |||
if (language != null) { | |||
structElemPart.setLanguage(language); | |||
} | |||
for (int i = 0, n = structureTree.getLength(); i < n; i++) { | |||
Node node = structureTree.item(i); | |||
if (node.getNodeName().equals("fo:flow") | |||
|| node.getNodeName().equals("fo:static-content")) { | |||
PDFStructElem structElemSect = pdfDoc.getFactory().makeStructureElement( | |||
FOToPDFRoleMap.mapFormattingObject(node.getLocalName(), structElemPart), | |||
structElemPart); | |||
structElemPart.addKid(structElemSect); | |||
NodeList childNodes = node.getChildNodes(); | |||
for (int j = 0, m = childNodes.getLength(); j < m; j++) { | |||
processNode(childNodes.item(j), structElemSect, true); | |||
} | |||
} | |||
} | |||
} | |||
private void processNode(Node node, PDFStructElem parent, boolean addKid) { | |||
Node attr = node.getAttributes().getNamedItem("foi:ptr"); | |||
assert attr != null; | |||
String ptr = attr.getNodeValue(); | |||
String nodeName = node.getLocalName(); | |||
PDFStructElem structElem = pdfDoc.getFactory().makeStructureElement( | |||
FOToPDFRoleMap.mapFormattingObject(nodeName, parent), parent); | |||
// TODO necessary? If a page-sequence is empty (e.g., contains a single | |||
// empty fo:block), should the block still be added to the structure | |||
// tree? This is not being done for descendant empty elements... | |||
if (addKid) { | |||
parent.addKid(structElem); | |||
} | |||
if (nodeName.equals("external-graphic") || nodeName.equals("instream-foreign-object")) { | |||
Node altTextNode = node.getAttributes().getNamedItem("fox:alt-text"); | |||
if (altTextNode != null) { | |||
structElem.put("Alt", altTextNode.getNodeValue()); | |||
} else { | |||
// TODO route that to event notification system | |||
// log.warn("fo:" + s | |||
// + " requires an alternative text attribute fox:alt-text for accessibility"); | |||
structElem.put("Alt", "No alternate text specified"); | |||
} | |||
} | |||
structTreeMap.put(ptr, structElem); | |||
NodeList nodes = node.getChildNodes(); | |||
for (int i = 0, n = nodes.getLength(); i < n; i++) { | |||
processNode(nodes.item(i), structElem, false); | |||
} | |||
} | |||
private int getNextParentTreeKey() { | |||
return parentTreeKey++; | |||
} | |||
/** | |||
* Receive notification of the beginning of a new page. | |||
* | |||
* @param page the page that will be rendered in PDF | |||
*/ | |||
void startPage(PDFPage page) { | |||
currentPage = page; | |||
currentPage.setStructParents(getNextParentTreeKey()); | |||
pageParentTreeArray = new PDFArray(); | |||
} | |||
/** | |||
* Receive notification of the end of the current page. | |||
*/ | |||
void endPage() { | |||
// TODO | |||
// Values in a number tree must be indirect references to the PDF | |||
// objects associated to the keys. To enforce that the array is | |||
// registered to the PDF document. Unfortunately that can't be done | |||
// earlier since a call to PDFContentGenerator.flushPDFDoc can be made | |||
// before the array is complete, which would result in only part of it | |||
// being output to the PDF. | |||
// This should really be handled by PDFNumsArray | |||
pdfDoc.registerObject(pageParentTreeArray); | |||
parentTree.getNums().put(currentPage.getStructParents(), pageParentTreeArray); | |||
} | |||
private MarkedContentInfo addToParentTree(String reference) { | |||
PDFStructElem parent = (PDFStructElem) structTreeMap.get(reference); | |||
if (parent == null) { | |||
return ARTIFACT; | |||
} else { | |||
pageParentTreeArray.add(parent); | |||
String type = parent.getStructureType().toString(); | |||
int mcid = pageParentTreeArray.length() - 1; | |||
return new MarkedContentInfo(type, mcid, parent); | |||
} | |||
} | |||
/** | |||
* Adds a content item corresponding to text into the structure tree, if | |||
* there is a structure element associated to it. | |||
* | |||
* @param parentReference reference to the parent structure element of the | |||
* piece of text | |||
* @return the necessary information for bracketing the content as a | |||
* marked-content sequence. If there is no element in the structure tree | |||
* associated to that content, returns an instance whose | |||
* {@link MarkedContentInfo#tag} value is <code>null</code>. The content | |||
* must then be treated as an artifact. | |||
*/ | |||
MarkedContentInfo addTextContentItem(String parentReference) { | |||
MarkedContentInfo mci = addToParentTree(parentReference); | |||
if (mci != ARTIFACT) { | |||
PDFDictionary contentItem = new PDFDictionary(); | |||
contentItem.put("Type", MCR); | |||
contentItem.put("Pg", this.currentPage); | |||
contentItem.put("MCID", mci.mcid); | |||
mci.parent.addKid(contentItem); | |||
} | |||
return mci; | |||
} | |||
/** | |||
* Adds a content item corresponding to an image into the structure tree, if | |||
* there is a structure element associated to it. | |||
* | |||
* @param parentReference reference to the parent structure element of the | |||
* image | |||
* @return the necessary information for bracketing the content as a | |||
* marked-content sequence. If there is no element in the structure tree | |||
* associated to that image, returns an instance whose | |||
* {@link MarkedContentInfo#tag} value is <code>null</code>. The image | |||
* must then be treated as an artifact. | |||
*/ | |||
MarkedContentInfo addImageContentItem(String parentReference) { | |||
MarkedContentInfo mci = addToParentTree(parentReference); | |||
if (mci != ARTIFACT) { | |||
mci.parent.setMCIDKid(mci.mcid); | |||
mci.parent.setPage(this.currentPage); | |||
} | |||
return mci; | |||
} | |||
// While the PDF spec allows images to be referred as PDF objects, this | |||
// makes the Acrobat Pro checker complain that the image is not accessible. | |||
// Its alt-text is still read aloud though. Using marked-content sequences | |||
// like for text works. | |||
// MarkedContentInfo addImageObject(String parentReference) { | |||
// MarkedContentInfo mci = addToParentTree(parentReference); | |||
// if (mci != ARTIFACT) { | |||
// PDFDictionary contentItem = new PDFDictionary(); | |||
// contentItem.put("Type", OBJR); | |||
// contentItem.put("Pg", this.currentPage); | |||
// contentItem.put("Obj", null); | |||
// mci.parent.addKid(contentItem); | |||
// } | |||
// return mci; | |||
// } | |||
/** | |||
* Adds a content item corresponding to the given link into the structure | |||
* tree. | |||
* | |||
* @param link a link | |||
* @param reference reference to the corresponding parent structure element | |||
*/ | |||
void addLinkContentItem(PDFLink link, String reference) { | |||
int structParent = getNextParentTreeKey(); | |||
link.setStructParent(structParent); | |||
parentTree.getNums().put(structParent, link); | |||
PDFDictionary contentItem = new PDFDictionary(); | |||
contentItem.put("Type", OBJR); | |||
contentItem.put("Pg", this.currentPage); | |||
contentItem.put("Obj", link); | |||
PDFStructElem parent = (PDFStructElem) structTreeMap.get(reference); | |||
parent.addKid(contentItem); | |||
} | |||
} |
@@ -29,9 +29,6 @@ import java.io.IOException; | |||
import org.w3c.dom.Document; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.fonts.Font; | |||
import org.apache.fop.fonts.FontInfo; | |||
import org.apache.fop.fonts.FontTriplet; | |||
@@ -47,6 +44,7 @@ import org.apache.fop.render.intermediate.AbstractIFPainter; | |||
import org.apache.fop.render.intermediate.IFContext; | |||
import org.apache.fop.render.intermediate.IFException; | |||
import org.apache.fop.render.intermediate.IFState; | |||
import org.apache.fop.render.pdf.PDFLogicalStructureHandler.MarkedContentInfo; | |||
import org.apache.fop.traits.BorderProps; | |||
import org.apache.fop.traits.RuleStyle; | |||
import org.apache.fop.util.CharUtilities; | |||
@@ -56,9 +54,6 @@ import org.apache.fop.util.CharUtilities; | |||
*/ | |||
public class PDFPainter extends AbstractIFPainter { | |||
/** logging instance */ | |||
private static Log log = LogFactory.getLog(PDFPainter.class); | |||
private final PDFDocumentHandler documentHandler; | |||
/** The current content generator */ | |||
@@ -66,19 +61,22 @@ public class PDFPainter extends AbstractIFPainter { | |||
private final PDFBorderPainter borderPainter; | |||
private boolean accessEnabled = false; | |||
private boolean accessEnabled; | |||
private int mcid; // used for accessibility | |||
private MarkedContentInfo imageMCI; | |||
private String structElemType; // used for accessibility | |||
private PDFLogicalStructureHandler logicalStructureHandler; | |||
/** | |||
* Default constructor. | |||
* @param documentHandler the parent document handler | |||
* @param logicalStructureHandler the logical structure handler | |||
*/ | |||
public PDFPainter(PDFDocumentHandler documentHandler) { | |||
public PDFPainter(PDFDocumentHandler documentHandler, | |||
PDFLogicalStructureHandler logicalStructureHandler) { | |||
super(); | |||
this.documentHandler = documentHandler; | |||
this.logicalStructureHandler = logicalStructureHandler; | |||
this.generator = documentHandler.generator; | |||
this.borderPainter = new PDFBorderPainter(this.generator); | |||
this.state = IFState.create(); | |||
@@ -137,52 +135,29 @@ public class PDFPainter extends AbstractIFPainter { | |||
String ptr = getContext().getStructurePointer(); | |||
prepareImageMCID(ptr); | |||
placeImageAccess(rect, xobject); | |||
addImageMCID(ptr); | |||
} else { | |||
placeImage(rect, xobject); | |||
} | |||
return; | |||
} | |||
if (accessEnabled && getContext().hasStructurePointer()) { | |||
String ptr = getContext().getStructurePointer(); | |||
prepareImageMCID(ptr); | |||
drawImageUsingURI(uri, rect); | |||
addImageMCID(ptr); | |||
} else { | |||
if (accessEnabled && getContext().hasStructurePointer()) { | |||
String ptr = getContext().getStructurePointer(); | |||
prepareImageMCID(ptr); | |||
} | |||
drawImageUsingURI(uri, rect); | |||
flushPDFDoc(); | |||
} | |||
flushPDFDoc(); | |||
} | |||
private void prepareImageMCID(String ptr) { | |||
mcid = this.documentHandler.getMCID(); | |||
mcid++; // fix for Acro Checker | |||
this.documentHandler.incMCID(); // simulating a parent text element | |||
structElemType = this.documentHandler.getStructElemType(ptr); | |||
if (structElemType != null) { | |||
this.documentHandler.addToTempList( | |||
this.documentHandler.getCurrentParentTreeKey(), | |||
this.documentHandler.getParentTrailerObject(ptr)); | |||
this.documentHandler.addToTempList( | |||
this.documentHandler.getCurrentParentTreeKey(), | |||
this.documentHandler.getTrailerObject(ptr)); | |||
} | |||
} | |||
private void addImageMCID(String ptr) { | |||
if (this.structElemType != null) { | |||
this.documentHandler.addChildToStructElemImage(ptr, mcid); | |||
this.documentHandler.incMCID(); | |||
} | |||
//If structElemType is null, it means "Artifact" mode (ex. leader with use-content). | |||
imageMCI = logicalStructureHandler.addImageContentItem(ptr); | |||
} | |||
/** {@inheritDoc} */ | |||
protected RenderingContext createRenderingContext() { | |||
PDFRenderingContext pdfContext = new PDFRenderingContext( | |||
getUserAgent(), generator, this.documentHandler.currentPage, getFontInfo()); | |||
pdfContext.setMCID(mcid); | |||
pdfContext.setStructElemType(structElemType); | |||
pdfContext.setMCID(imageMCI.mcid); | |||
pdfContext.setStructElemType(imageMCI.tag); | |||
return pdfContext; | |||
} | |||
@@ -212,7 +187,7 @@ public class PDFPainter extends AbstractIFPainter { | |||
* @param xobj the image XObject | |||
*/ | |||
private void placeImageAccess(Rectangle rect, PDFXObject xobj) { | |||
generator.saveGraphicsState(structElemType, mcid); | |||
generator.saveGraphicsState(imageMCI.tag, imageMCI.mcid); | |||
generator.add(format(rect.width) + " 0 0 " | |||
+ format(-rect.height) + " " | |||
+ format(rect.x) + " " | |||
@@ -226,11 +201,8 @@ public class PDFPainter extends AbstractIFPainter { | |||
if (accessEnabled && getContext().hasStructurePointer()) { | |||
String ptr = getContext().getStructurePointer(); | |||
prepareImageMCID(ptr); | |||
drawImageUsingDocument(doc, rect); | |||
addImageMCID(ptr); | |||
} else { | |||
drawImageUsingDocument(doc, rect); | |||
} | |||
drawImageUsingDocument(doc, rect); | |||
flushPDFDoc(); | |||
} | |||
@@ -326,21 +298,13 @@ public class PDFPainter extends AbstractIFPainter { | |||
throws IFException { | |||
if (accessEnabled) { | |||
String ptr = getContext().getStructurePointer(); | |||
int mcId; | |||
String structElType = null; | |||
if (ptr != null && ptr.length() > 0) { | |||
mcId = this.documentHandler.getMCID(); | |||
structElType = this.documentHandler.getStructElemType(ptr); | |||
this.documentHandler.addToTempList( | |||
this.documentHandler.getCurrentParentTreeKey(), | |||
this.documentHandler.getTrailerObject(ptr)); | |||
MarkedContentInfo mci = logicalStructureHandler.addTextContentItem(ptr); | |||
if (generator.getTextUtil().isInTextObject()) { | |||
generator.separateTextElements(mcId, structElType); | |||
generator.separateTextElements(mci.mcid, mci.tag); | |||
} | |||
generator.updateColor(state.getTextColor(), true, null); | |||
generator.beginTextObjectAccess(mcId, structElType); | |||
this.documentHandler.addChildToStructElemText(ptr, mcId); | |||
this.documentHandler.incMCID(); | |||
generator.beginTextObjectAccess(mci.mcid, mci.tag); | |||
} else { | |||
// <fo:leader leader-pattern="use-content"> | |||
// Leader content is marked as "/Artifact" |