diff options
author | Vincent Hennebert <vhennebert@apache.org> | 2009-10-12 16:26:00 +0000 |
---|---|---|
committer | Vincent Hennebert <vhennebert@apache.org> | 2009-10-12 16:26:00 +0000 |
commit | 36243dc349adb041b2eab607f5c5081de4d55542 (patch) | |
tree | 23af18f4e0c3de4b3df17058bd873b8017c37d02 | |
parent | 618e88ae22d5371122621e4df84fcbd66bc1ece8 (diff) | |
download | xmlgraphics-fop-36243dc349adb041b2eab607f5c5081de4d55542.tar.gz xmlgraphics-fop-36243dc349adb041b2eab607f5c5081de4d55542.zip |
Extracted code related to logical structure out of PDFDocumentHandler and put it in its own PDFLogicalStructureHandler class. Vastly simplified the building of the structure tree in the same time.
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-ffa450edef68
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFDocument.java | 19 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFFactory.java | 34 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFLink.java | 9 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFNumsArray.java | 22 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFPage.java | 12 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFStructElem.java | 123 | ||||
-rw-r--r-- | src/java/org/apache/fop/pdf/PDFStructTreeRoot.java | 19 | ||||
-rw-r--r-- | src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java | 3 | ||||
-rw-r--r-- | src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java | 352 | ||||
-rw-r--r-- | src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java | 5 | ||||
-rw-r--r-- | src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java | 300 | ||||
-rw-r--r-- | src/java/org/apache/fop/render/pdf/PDFPainter.java | 78 |
12 files changed, 482 insertions, 494 deletions
diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java index 46effdfd6..f0a777bdd 100644 --- a/src/java/org/apache/fop/pdf/PDFDocument.java +++ b/src/java/org/apache/fop/pdf/PDFDocument.java @@ -355,6 +355,25 @@ public class PDFDocument { } /** + * 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. * * @return the {@link PDFInfo} object diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index db2e99875..a1500c509 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -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,16 +889,34 @@ 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). * * @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 66791e3ba..934932648 100644 --- a/src/java/org/apache/fop/pdf/PDFLink.java +++ b/src/java/org/apache/fop/pdf/PDFLink.java @@ -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); } /** diff --git a/src/java/org/apache/fop/pdf/PDFNumsArray.java b/src/java/org/apache/fop/pdf/PDFNumsArray.java index e1c38dd48..6db7b02ac 100644 --- a/src/java/org/apache/fop/pdf/PDFNumsArray.java +++ b/src/java/org/apache/fop/pdf/PDFNumsArray.java @@ -57,8 +57,26 @@ public class PDFNumsArray extends PDFObject { * @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} */ diff --git a/src/java/org/apache/fop/pdf/PDFPage.java b/src/java/org/apache/fop/pdf/PDFPage.java index 1bcaa65c6..7a5365761 100644 --- a/src/java/org/apache/fop/pdf/PDFPage.java +++ b/src/java/org/apache/fop/pdf/PDFPage.java @@ -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"); } /** diff --git a/src/java/org/apache/fop/pdf/PDFStructElem.java b/src/java/org/apache/fop/pdf/PDFStructElem.java index 2f8898a19..4fb8cbcd5 100644 --- a/src/java/org/apache/fop/pdf/PDFStructElem.java +++ b/src/java/org/apache/fop/pdf/PDFStructElem.java @@ -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"); diff --git a/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java b/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java index e530b1582..0840c7a4b 100644 --- a/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java +++ b/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java @@ -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); } @@ -50,14 +43,6 @@ public class PDFStructTreeRoot extends PDFDictionary { } /** - * 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 */ diff --git a/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java b/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java index f0cc39698..e3122ec39 100644 --- a/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java +++ b/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java @@ -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() { } } diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java index 12c796c5a..fab7f8403 100644 --- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java @@ -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; - } - } diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java index 138129334..2407e0dd2 100644 --- a/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFDocumentNavigationHandler.java @@ -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); } diff --git a/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java b/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java new file mode 100644 index 000000000..e1f1fae68 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/PDFLogicalStructureHandler.java @@ -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); + } + +} diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java index 4c9baf0f6..4b9f47a4e 100644 --- a/src/java/org/apache/fop/render/pdf/PDFPainter.java +++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java @@ -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" |