FOP-2436: Merging of Tagged (Accessible) PDF fixes

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop-pdf-images/branches/Temp_MergeTaggedPDF@1680278 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/fop.jar b/lib/fop.jar
index 08bc99a..828d86c 100644
--- a/lib/fop.jar
+++ b/lib/fop.jar
Binary files differ
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/DocumentRootModifier.java b/src/java/org/apache/fop/render/pdf/pdfbox/DocumentRootModifier.java
index 944916a..72a0d9d 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/DocumentRootModifier.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/DocumentRootModifier.java
@@ -37,7 +37,7 @@
 
     public void structTreeRootEntriesToCopy(COSDictionary structRootDict) throws IOException {
         checkForMap(structRootDict, COSName.ROLE_MAP.getName());
-        checkForMap(structRootDict, "CLASS_MAP");
+        checkForMap(structRootDict, "ClassMap");
     }
 
     private void checkForMap(COSDictionary structRootDict, String mapName) throws IOException {
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/PageParentTreeFinder.java b/src/java/org/apache/fop/render/pdf/pdfbox/PageParentTreeFinder.java
index 2bebf93..f998476 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/PageParentTreeFinder.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/PageParentTreeFinder.java
@@ -47,8 +47,7 @@
         }
         if (position != -1) {
             PDNumberTreeNode srcNumberTreeNode = srcDoc.getDocumentCatalog().getStructureTreeRoot().getParentTree();
-            COSArray parentTreeKids = traverseParentTree(srcNumberTreeNode.getCOSDictionary(), position);
-            return removeNonCOSObjects(parentTreeKids);
+            return traverseParentTree(srcNumberTreeNode.getCOSDictionary(), position);
         }
         return new COSArray();
     }
@@ -105,7 +104,6 @@
                         COSArray nums = (COSArray) kidCOSObj.getDictionaryObject(COSName.NUMS);
                         pageParentTree = (COSArray) nums.getObject(((position - lowerLimit) * 2) + 1);
                         numList.add(pageParentTree);
-                        return;
                     }
                 } else {
                     COSArray nums = (COSArray) kidCOSObj.getDictionaryObject(COSName.NUMS);
@@ -136,14 +134,4 @@
         }
         return new COSArray();
     }
-    private COSArray removeNonCOSObjects(COSArray pageParentTreeArray) {
-        COSArray objectList = new COSArray();
-        for (COSBase entry : pageParentTreeArray) {
-            if (entry instanceof COSObject) {
-                COSObject entryObj = (COSObject)entry;
-                objectList.add(entryObj);
-            }
-        }
-        return objectList;
-    }
 }
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/StructureTreeMerger.java b/src/java/org/apache/fop/render/pdf/pdfbox/StructureTreeMerger.java
index 66f8d17..5341081 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/StructureTreeMerger.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/StructureTreeMerger.java
@@ -31,9 +31,11 @@
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSInteger;
 import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSNull;
 import org.apache.pdfbox.cos.COSObject;
-
 import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf.StandardStructureTypes;
+
 
 import org.apache.fop.pdf.PDFDictionary;
 import org.apache.fop.pdf.PDFDocument;
@@ -42,6 +44,8 @@
 import org.apache.fop.pdf.PDFPage;
 import org.apache.fop.pdf.PDFReference;
 import org.apache.fop.pdf.PDFStructElem;
+
+
 import org.apache.fop.render.pdf.PDFLogicalStructureHandler;
 
 public class StructureTreeMerger {
@@ -58,6 +62,7 @@
     private int currentMCID;
     private List<COSObject> topElems = new ArrayList<COSObject>();
     private COSArray extra = new COSArray();
+    private COSArray originalParentTree = new COSArray();
 
     public StructureTreeMerger(PDFStructElem currentSessionElem, PDFLogicalStructureHandler logicalStructHandler,
                                PDFBoxAdapter adapter, PDPage srcPage) {
@@ -75,6 +80,8 @@
     }
 
     public void copyStructure(COSArray pageParentTreeArray) throws IOException {
+        originalParentTree = pageParentTreeArray;
+        pageParentTreeArray = removeNonCOSObjects(pageParentTreeArray);
         for (COSBase entry : pageParentTreeArray) {
             COSObject entryObj = (COSObject)entry;
             createPageStructElements(entryObj);
@@ -146,7 +153,7 @@
         for (COSName name : names) {
             if (baseDic.keySet().contains(name)) {
                 if (name.equals(COSName.PG)) {
-                    elem.put("Pg", targetPage.makeReference());
+                    elem.put(COSName.PG.getName(), targetPage.makeReference());
                 } else {
                     elem.put(name.getName(), adapter.cloneForNewDocument(baseDic.getItem(name)));
                 }
@@ -176,20 +183,19 @@
             elem.setParent(currentSessionElem);
             currentSessionElem.addKid(elem);
             topElems.add(cosElem);
-            return;
         } else if (elemParent != null) {
-            checkIlfStructureTypeIsPresent(parentElemDictionary, "TR");
-            if (!checkIlfStructureTypeIsPresent(parentElemDictionary, "TR")) {
+            if (!checkIfStructureTypeIsPresent(parentElemDictionary, StandardStructureTypes.TR)) {
                 elem.setParent(elemParent);
                 int position = StructureTreeMergerUtil.findObjectPositionInKidsArray(cosElem);
                 elemParent.addKidInSpecificOrder(position, elem);
             }
-        } else if (!checkIlfStructureTypeIsPresent(parentElemDictionary, "Document")) {
+        } else if (!checkIfStructureTypeIsPresent(parentElemDictionary, StandardStructureTypes.DOCUMENT)) {
             elemParent = createAndRegisterStructElem(cosParentElem);
             copyElemEntries(cosParentElem, elemParent);
             elem.setParent(elemParent);
             fillKidsWithNull(elemParent, (COSDictionary)cosParentElem.getObject());
-            if (((COSName)parentElemDictionary.getDictionaryObject(COSName.S)).getName().equals("TR")) {
+            if (((COSName)parentElemDictionary.getDictionaryObject(COSName.S)).getName()
+                .equals(StandardStructureTypes.TR)) {
                 COSBase rowKids = parentElemDictionary.getItem(COSName.K);
                 createKids(rowKids, parentElemDictionary, elemParent, true);
             } else {
@@ -293,14 +299,19 @@
     private void createKidFromCOSDictionary(COSDictionary mcrDict, PDFStructElem parent, COSDictionary baseDict)
         throws IOException {
         Collection<COSName> exclude = Arrays.asList(COSName.PG);
+        PDFReference referenceObj;
         if (isElementFromSourcePage(mcrDict, baseDict)) {
             PDFDictionary contentItem = (PDFDictionary)adapter.cloneForNewDocument(mcrDict, mcrDict, exclude);
             if (mcrDict.keySet().contains(COSName.TYPE)) {
                 String type = ((COSName) mcrDict.getDictionaryObject(COSName.TYPE)).getName();
                 if (type.equals("OBJR")) {
                     COSObject obj = (COSObject) mcrDict.getItem(COSName.OBJ);
-                    PDFReference referenceObj = ((PDFObject) adapter.getCachedClone(obj)).makeReference();
-                    contentItem.put("Obj", referenceObj);
+                    if (adapter.getCachedClone(obj) == null) {
+                        referenceObj = null;
+                    } else {
+                        referenceObj = ((PDFObject) adapter.getCachedClone(obj)).makeReference();
+                    }
+                    contentItem.put(COSName.OBJ.getName(), referenceObj);
                     updateStructParentAndAddToPageParentTree(referenceObj, parent);
                 } else if (type.equals("MCR")) {
                     updateMCIDEntry(contentItem);
@@ -309,9 +320,9 @@
                 }
             }
             if (mcrDict.keySet().contains(COSName.PG)) {
-                contentItem.put("Pg", targetPage.makeReference());
+                contentItem.put(COSName.PG.getName(), targetPage.makeReference());
             } else {
-                parent.put("Pg", targetPage.makeReference());
+                parent.put(COSName.PG.getName(), targetPage.makeReference());
             }
             parent.addKid(contentItem);
         } else {
@@ -327,10 +338,10 @@
 
     private void updateMCIDEntry(PDFDictionary mcrDictionary) {
         if (currentMCID > 0) {
-            int oldMCID = (((PDFNumber)mcrDictionary.get("MCID")).getNumber()).intValue();
+            int oldMCID = (((PDFNumber)mcrDictionary.get(COSName.MCID.getName())).getNumber()).intValue();
             PDFNumber number = new PDFNumber();
             number.setNumber(oldMCID + currentMCID);
-            mcrDictionary.put("MCID", number);
+            mcrDictionary.put(COSName.MCID.getName(), number);
         }
     }
 
@@ -352,26 +363,39 @@
         return false;
     }
 
+
     public void addToPageParentTreeArray() {
-        for (PDFStructElem entry : markedContentMap.values()) {
+        List<PDFStructElem> complete = restoreNullValuesInParentTree();
+        for (PDFStructElem entry : complete) {
             logicalStructHandler.getPageParentTree().add(entry);
         }
     }
 
-    private void updateStructParentAndAddToPageParentTree(PDFReference obj, PDFStructElem elem) {
-        PDFObject referenceObj = obj.getObject();
-        assert referenceObj instanceof PDFDictionary;
-        int nextParentTreeKey;
-        PDFDictionary objDict = (PDFDictionary)referenceObj;
-        nextParentTreeKey = logicalStructHandler.getNextParentTreeKey();
-        objDict.put("StructParent", nextParentTreeKey);
-        logicalStructHandler.getParentTree().addToNums(nextParentTreeKey, elem);
+    private List<PDFStructElem> restoreNullValuesInParentTree() {
+        int total = markedContentMap.size();
+        List<PDFStructElem> list = new ArrayList<PDFStructElem>(markedContentMap.values());
+        List<PDFStructElem> complete = new ArrayList<PDFStructElem>(total);
+        for (COSBase base : originalParentTree) {
+            if (base instanceof COSNull || base == null) {
+                complete.add(null);
+            } else {
+                complete.add(list.get(0));
+                list.remove(0);
+            }
+        }
+        return complete;
     }
 
-    public void setCurrentSessionElemKid() {
-        PDFNumber num = new PDFNumber();
-        createKidEntryFromInt(num, currentSessionElem);
-        addToPageParentTreeArray();
+    private void updateStructParentAndAddToPageParentTree(PDFReference obj, PDFStructElem elem) {
+        int nextParentTreeKey = logicalStructHandler.getNextParentTreeKey();
+        if (obj != null) {
+            PDFObject referenceObj = obj.getObject();
+            assert referenceObj instanceof PDFDictionary;
+            PDFDictionary objDict = (PDFDictionary)referenceObj;
+            objDict.put((COSName.STRUCT_PARENT).getName(), nextParentTreeKey);
+        }
+
+        logicalStructHandler.getParentTree().addToNums(nextParentTreeKey, elem);
     }
 
     private void findLeafNodesInPageFromStructElemObjects(COSBase entry) throws IOException {
@@ -437,7 +461,7 @@
         }
     }
 
-    private boolean checkIlfStructureTypeIsPresent(COSDictionary elemDictionary, String type) {
+    private boolean checkIfStructureTypeIsPresent(COSDictionary elemDictionary, String type) {
         String potentialCustomElemType = ((COSName)elemDictionary.getDictionaryObject(COSName.S)).getName();
         if (type.equals(potentialCustomElemType)) {
             return true;
@@ -447,4 +471,19 @@
         }
     }
 
+    private COSArray removeNonCOSObjects(COSArray pageParentTreeArray) {
+        COSArray objectList = new COSArray();
+        for (COSBase entry : pageParentTreeArray) {
+            if (entry instanceof COSObject) {
+                COSObject entryObj = (COSObject)entry;
+                objectList.add(entryObj);
+            }
+        }
+        return objectList;
+    }
+    public void setCurrentSessionElemKid() {
+        PDFNumber num = new PDFNumber();
+        createKidEntryFromInt(num, currentSessionElem);
+        addToPageParentTreeArray();
+    }
 }
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/TaggedPDFConductor.java b/src/java/org/apache/fop/render/pdf/pdfbox/TaggedPDFConductor.java
index c68b43a..df3de7f 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/TaggedPDFConductor.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/TaggedPDFConductor.java
@@ -36,7 +36,6 @@
 public class TaggedPDFConductor {
 
     private PDPage srcPage;
-    private PDFDocument pdfDoc;
     private PDFPage targetPage;
     private DocumentRootModifier rootMod;
     private StructureTreeMerger merger;
@@ -46,7 +45,7 @@
                               PDFBoxAdapter adapter) {
         this.srcPage = srcPage;
         this.targetPage = adapter.getTargetPage();
-        this.pdfDoc = targetPage.getDocument();
+        PDFDocument pdfDoc = targetPage.getDocument();
         this.rootMod = new DocumentRootModifier(adapter, pdfDoc);
         merger = new StructureTreeMerger(currentSessionElem, logicalStructHandler, adapter, srcPage);
     }
@@ -78,6 +77,7 @@
             merger.currentSessionElem.put(COSName.PG.getName(), targetPage.makeReference());
         } else {
             merger.currentSessionElem.put("S", new PDFName("Div"));
+            merger.currentSessionElem.remove("Alt");
         }
     }
     private boolean isInputPDFTagged(PDDocument srcDoc) {
diff --git a/test/java/org/apache/fop/render/pdf/DocumentRootModifierTestCase.java b/test/java/org/apache/fop/render/pdf/DocumentRootModifierTestCase.java
index 077132d..62bdc17 100644
--- a/test/java/org/apache/fop/render/pdf/DocumentRootModifierTestCase.java
+++ b/test/java/org/apache/fop/render/pdf/DocumentRootModifierTestCase.java
@@ -24,6 +24,7 @@
 
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
 
 import org.apache.fop.pdf.PDFArray;
 import org.apache.fop.pdf.PDFDictionary;
@@ -32,42 +33,66 @@
 import org.apache.fop.pdf.PDFPage;
 import org.apache.fop.pdf.PDFResources;
 import org.apache.fop.pdf.PDFStructTreeRoot;
+
 import org.apache.fop.render.pdf.pdfbox.DocumentRootModifier;
 import org.apache.fop.render.pdf.pdfbox.PDFBoxAdapter;
 
 import junit.framework.Assert;
 
 public class DocumentRootModifierTestCase {
+    private static final String CLASSMAP = "test/resources/classMap.pdf";
 
     @Test
     public void testStructTreeRootEntriesToCopy() throws IOException {
         Rectangle2D r = new Rectangle2D.Double();
-        PDFPage page = new PDFPage(new PDFResources(0), 0, r, r, r, r);
-        page.setObjectNumber(1);
         PDFDocument pdfDoc = new PDFDocument("");
+        PDFPage page = new PDFPage(new PDFResources(pdfDoc), 0, r, r, r, r);
+        page.setObjectNumber(1);
         page.setDocument(pdfDoc);
         pdfDoc.makeStructTreeRoot(null);
         PDFStructTreeRoot structTreeRoot = pdfDoc.getRoot().getStructTreeRoot();
         PDFDictionary rootBaseRoleMap = new PDFDictionary();
-        PDFName para = new PDFName("P");
-        rootBaseRoleMap.put("MyPara", para);
-        structTreeRoot.put("RoleMap", rootBaseRoleMap);
         PDFBoxAdapter adapter = new PDFBoxAdapter(page, new HashMap(),  new HashMap<Integer, PDFArray>());
+        DocumentRootModifier modifier = new DocumentRootModifier(adapter, pdfDoc);
         COSDictionary root = new COSDictionary();
         COSDictionary mapRole = new COSDictionary();
         mapRole.setName("Icon", "Figure");
         root.setItem(COSName.ROLE_MAP, mapRole);
-        DocumentRootModifier modifier = new DocumentRootModifier(adapter, pdfDoc);
         modifier.structTreeRootEntriesToCopy(root);
         structTreeRoot = pdfDoc.getRoot().getStructTreeRoot();
         PDFDictionary baseRoot = (PDFDictionary) structTreeRoot.get("RoleMap");
-        PDFName nameIcon = (PDFName) baseRoot.get("Icon");
-        PDFName myPara = (PDFName)baseRoot.get("MyPara");
-        String test = nameIcon.getName();
-        String expected = "Figure";
+        String test = baseRoot.get("Icon").toString();
+        String expected = "/Figure";
+        Assert.assertEquals(test, expected);
+
+        PDFName para = new PDFName("P");
+        rootBaseRoleMap.put("MyPara", para);
+        structTreeRoot.put("RoleMap", rootBaseRoleMap);
+        modifier.structTreeRootEntriesToCopy(root);
+        structTreeRoot = pdfDoc.getRoot().getStructTreeRoot();
+        PDFDictionary baseRoot2 = (PDFDictionary) structTreeRoot.get("RoleMap");
+        PDFName nameIcon = (PDFName) baseRoot2.get("Icon");
+        PDFName myPara = (PDFName)baseRoot2.get("MyPara");
+        test = nameIcon.getName();
+        expected = "Figure";
         Assert.assertEquals(test, expected);
         test = myPara.getName();
         expected = "P";
         Assert.assertEquals(test, expected);
+
+
+        PDDocument doc = PDDocument.load(CLASSMAP);
+        COSDictionary temp = (COSDictionary)doc.getDocumentCatalog().getStructureTreeRoot().getCOSObject();
+        PDFDictionary classMap = new PDFDictionary();
+        PDFDictionary inner = new PDFDictionary();
+        inner.put("StartIndent", 0);
+        classMap.put("Normal2", inner);
+        structTreeRoot.put("ClassMap", classMap);
+        modifier.structTreeRootEntriesToCopy(temp);
+        structTreeRoot = pdfDoc.getRoot().getStructTreeRoot();
+        PDFDictionary testDict = (PDFDictionary)structTreeRoot.get("ClassMap");
+        Assert.assertNotNull(testDict.get("Normal2"));
     }
+
 }
+
diff --git a/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java b/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java
index 7655780..48529ed 100644
--- a/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java
+++ b/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java
@@ -76,7 +76,6 @@
 
 public class PDFBoxAdapterTestCase {
     private Rectangle2D r = new Rectangle2D.Double();
-    private PDFPage pdfpage = new PDFPage(new PDFResources(0), 0, r, r, r, r);
     private static final String CFF1 = "test/resources/2fonts.pdf";
     private static final String CFF2 = "test/resources/2fonts2.pdf";
     private static final String CFF3 = "test/resources/simpleh.pdf";
@@ -98,6 +97,7 @@
 
     private PDFBoxAdapter getPDFBoxAdapter() {
         PDFDocument doc = new PDFDocument("");
+        PDFPage pdfpage = new PDFPage(new PDFResources(doc), 0, r, r, r, r);
         doc.setMergeFontsEnabled(true);
         pdfpage.setDocument(doc);
         pdfpage.setObjectNumber(1);
@@ -286,7 +286,9 @@
 
     @Test
     public void testStream() throws Exception {
-        pdfpage.setDocument(new PDFDocument(""));
+        PDFDocument pdfdoc = new PDFDocument("");
+        PDFPage pdfpage = new PDFPage(new PDFResources(pdfdoc), 0, r, r, r, r);
+        pdfpage.setDocument(pdfdoc);
         PDFBoxAdapter adapter = new PDFBoxAdapter(pdfpage, new HashMap(), new HashMap<Integer, PDFArray>());
         PDDocument doc = PDDocument.load(ROTATE);
         PDPage page = (PDPage) doc.getDocumentCatalog().getAllPages().get(0);
@@ -302,9 +304,10 @@
 
     @Test
     public void testLink() throws Exception {
-        pdfpage.setObjectNumber(1);
         PDFDocument pdfdoc = new PDFDocument("");
+        PDFPage pdfpage = new PDFPage(new PDFResources(pdfdoc), 0, r, r, r, r);
         pdfpage.setDocument(pdfdoc);
+        pdfpage.setObjectNumber(1);
         Map<Integer, PDFArray> pageNumbers = new HashMap<Integer, PDFArray>();
         PDFBoxAdapter adapter = new PDFBoxAdapter(pdfpage, new HashMap(), pageNumbers);
         PDDocument doc = PDDocument.load(LINK);
@@ -315,7 +318,7 @@
         Assert.assertTrue(stream.contains("/Link <</MCID 5 >>BDC"));
         Assert.assertTrue(pageNumbers.size() == 4);
         PDFAnnotList annots = (PDFAnnotList) pdfpage.get("Annots");
-        Assert.assertEquals(annots.toPDFString(), "[\n9 0 R\n12 0 R\n]");
+        Assert.assertEquals(annots.toPDFString(), "[\n1 0 R\n2 0 R\n]");
         doc.close();
     }
 
@@ -333,6 +336,7 @@
         PDDocument doc = PDDocument.load(SHADING);
         ImagePDF img = new ImagePDF(imgi, doc);
         PDFDocument pdfdoc = new PDFDocument("");
+        PDFPage pdfpage = new PDFPage(new PDFResources(pdfdoc), 0, r, r, r, r);
         pdfpage.setDocument(pdfdoc);
         PDFGState g = new PDFGState();
         pdfdoc.assignObjectNumber(g);
@@ -346,6 +350,6 @@
         PDFResources res = c.getPage().getPDFResources();
         OutputStream bos = new ByteArrayOutputStream();
         res.output(bos);
-        Assert.assertTrue(bos.toString().contains("/ExtGState << /GS5"));
+        Assert.assertTrue(bos.toString().contains("/ExtGState << /GS1"));
     }
 }
diff --git a/test/java/org/apache/fop/render/pdf/StructureTreeMergerTestCase.java b/test/java/org/apache/fop/render/pdf/StructureTreeMergerTestCase.java
index e1c4415..1aae1d1 100644
--- a/test/java/org/apache/fop/render/pdf/StructureTreeMergerTestCase.java
+++ b/test/java/org/apache/fop/render/pdf/StructureTreeMergerTestCase.java
@@ -19,13 +19,20 @@
 
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 
+
+
 import org.junit.Test;
 
 import org.apache.pdfbox.cos.COSArray;
 import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSInteger;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSObject;
+
 
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.PDPage;
@@ -49,6 +56,7 @@
 public class StructureTreeMergerTestCase {
     private static final String LINK = "test/resources/linkTagged.pdf";
     private static final String NoParentTree = "test/resources/NoParentTree.pdf";
+    private static final String BrokenLink = "test/resources/brokenLink.pdf";
     private PDFPage pdfPage;
     private PDFDocument pdfDoc;
     private PDFBoxAdapter adapter;
@@ -121,7 +129,7 @@
 
     private void setUp() {
         Rectangle2D r = new Rectangle2D.Double();
-        pdfPage = new PDFPage(new PDFResources(0), 0, r, r, r, r);
+        pdfPage = new PDFPage(new PDFResources(pdfDoc), 0, r, r, r, r);
         pdfDoc = new PDFDocument(" ");
         pdfDoc.makeStructTreeRoot(null);
         pdfPage.setObjectNumber(1);
@@ -146,4 +154,48 @@
             checkNoParentTree(kid, index);
         }
     }
+
+
+    @Test
+    public void testNullEntriesInParentTree() throws IOException {
+        setUp();
+        PDDocument doc = PDDocument.load(LINK);
+        PDPage srcPage = doc.getPage(0);
+        PageParentTreeFinder finder = new PageParentTreeFinder(srcPage);
+        COSArray markedContentParents = finder.getPageParentTreeArray(doc);
+        markedContentParents.add(0, null);
+        PDFStructElem elem = new PDFStructElem();
+        elem.setObjectNumber(2);
+        adapter = new PDFBoxAdapter(pdfPage, new HashMap(), new HashMap<Integer, PDFArray>());
+        PDFLogicalStructureHandler handler = setUpPDFLogicalStructureHandler();
+        StructureTreeMerger merger = new StructureTreeMerger(elem, handler, adapter, srcPage);
+        merger.copyStructure(markedContentParents);
+        PDFArray array = handler.getPageParentTree();
+        Assert.assertNull(array.get(0));
+    }
+    @Test
+    public void checkNullCOSObject() throws IOException {
+        setUp();
+        PDDocument doc = PDDocument.load(BrokenLink);
+        PDPage srcPage = doc.getPage(0);
+        PageParentTreeFinder finder = new PageParentTreeFinder(srcPage);
+        COSArray markedContentParents = finder.getPageParentTreeArray(doc);
+        COSObject nullObj = new COSObject(null);
+        nullObj.setObjectNumber(COSInteger.get(100));
+        nullObj.setGenerationNumber(COSInteger.ZERO);
+        PDFStructElem elem = new PDFStructElem();
+        elem.setObjectNumber(2);
+        COSObject parent = (COSObject)markedContentParents.get(1);
+        COSArray kids = (COSArray) parent.getDictionaryObject(COSName.K);
+        COSDictionary kid = (COSDictionary) kids.get(1);
+        kid.setItem(COSName.OBJ, nullObj);
+        adapter = new PDFBoxAdapter(pdfPage, new HashMap(), new HashMap<Integer, PDFArray>());
+        PDFLogicalStructureHandler handler = setUpPDFLogicalStructureHandler();
+        StructureTreeMerger merger = new StructureTreeMerger(elem, handler, adapter, srcPage);
+        merger.copyStructure(markedContentParents);
+        PDFArray array = handler.getPageParentTree();
+        PDFStructElem parentElem = (PDFStructElem)array.get(1);
+        PDFDictionary objrDict = (PDFDictionary) parentElem.getKids().get(1);
+        Assert.assertNull(objrDict.get("Obj"));
+    }
 }
diff --git a/test/java/org/apache/fop/render/pdf/TaggedPDFConductorTestCase.java b/test/java/org/apache/fop/render/pdf/TaggedPDFConductorTestCase.java
index 511a075..e058a69 100644
--- a/test/java/org/apache/fop/render/pdf/TaggedPDFConductorTestCase.java
+++ b/test/java/org/apache/fop/render/pdf/TaggedPDFConductorTestCase.java
@@ -66,8 +66,10 @@
         Assert.assertEquals(print(elem), "/Div/Part/Sect/Table/TBody/TR/TD/P/TD/P/TR/TD/TD");
 
         elem = new PDFStructElem();
+        elem.put("Alt", "alt-text");
         runConductor(OTF, elem);
         Assert.assertEquals(print(elem), "/Div/Part/Art/P/Span");
+        Assert.assertNull(elem.get("Alt"));
     }
 
     private String print(PDFStructElem x) throws IOException {
@@ -93,7 +95,7 @@
 
     private void setUp() {
         Rectangle2D r = new Rectangle2D.Double();
-        pdfPage = new PDFPage(new PDFResources(0), 0, r, r, r, r);
+        pdfPage = new PDFPage(new PDFResources(pdfDoc), 0, r, r, r, r);
         pdfDoc = new PDFDocument(" ");
         pdfDoc.makeStructTreeRoot(null);
         pdfPage.setObjectNumber(1);
diff --git a/test/resources/brokenLink.pdf b/test/resources/brokenLink.pdf
new file mode 100644
index 0000000..7b46920
--- /dev/null
+++ b/test/resources/brokenLink.pdf
Binary files differ
diff --git a/test/resources/classMap.pdf b/test/resources/classMap.pdf
new file mode 100755
index 0000000..4024554
--- /dev/null
+++ b/test/resources/classMap.pdf
Binary files differ