You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ti...@apache.org on 2024/12/24 12:05:35 UTC

svn commit: r1922670 - in /pdfbox/branches/2.0/pdfbox/src: main/java/org/apache/pdfbox/multipdf/Splitter.java test/java/org/apache/pdfbox/multipdf/PDFMergerUtilityTest.java

Author: tilman
Date: Tue Dec 24 12:05:35 2024
New Revision: 1922670

URL: http://svn.apache.org/viewvc?rev=1922670&view=rev
Log:
PDFBOX-5929: remove orphan annotations from the structure tree if annotations were removed from the page

Modified:
    pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/multipdf/Splitter.java
    pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/multipdf/PDFMergerUtilityTest.java

Modified: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/multipdf/Splitter.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/multipdf/Splitter.java?rev=1922670&r1=1922669&r2=1922670&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/multipdf/Splitter.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/multipdf/Splitter.java Tue Dec 24 12:05:35 2024
@@ -455,6 +455,10 @@ public class Splitter
                     // replace annotation with clone
                     dstDict.setItem(COSName.OBJ, dstObj);
                 }
+                else
+                {
+                    removePossibleOrphanAnnotation(srcObj, srcDict, currentPageDict, dstDict);
+                }
             }
             else
             {
@@ -493,6 +497,35 @@ public class Splitter
             }
             return dstDict;
         }
+
+        private void removePossibleOrphanAnnotation(COSDictionary srcObj, COSDictionary srcDict,
+                COSDictionary currentPageDict, COSDictionary dstDict)
+        {
+            // PDFBOX-5929: Check whether this is an "orphan" annotation that isn't in the page
+            COSBase objType = srcObj.getDictionaryObject(COSName.TYPE);
+            COSBase objSubtype = srcObj.getDictionaryObject(COSName.SUBTYPE);
+            if (COSName.ANNOT.equals(objType) || COSName.LINK.equals(objSubtype))
+            {
+                COSDictionary srcPageDict = srcDict.getCOSDictionary(COSName.PG);
+                if (srcPageDict == null)
+                {
+                    // /Pg entry is not always on this level
+                    srcPageDict = currentPageDict;
+                }
+                if (srcPageDict != null)
+                {
+                    COSArray annotationArray = srcPageDict.getCOSArray(COSName.ANNOTS);
+                    if (annotationArray == null || annotationArray.indexOfObject(srcObj) == -1)
+                    {
+                        // Ideally the entire OBJR entry should be removed.
+                        // Removing the OBJ entry is done to avoid potential page orphans
+                        // from the annotation destination.
+                        LOG.warn("An annotation OBJ that isn't in the page has been removed from the structure tree");
+                        dstDict.removeItem(COSName.OBJ);
+                    }
+                }
+            }
+        }
     }
 
     // Look for /StructParent and /StructParents and add them to the destination tree

Modified: pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/multipdf/PDFMergerUtilityTest.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/multipdf/PDFMergerUtilityTest.java?rev=1922670&r1=1922669&r2=1922670&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/multipdf/PDFMergerUtilityTest.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/multipdf/PDFMergerUtilityTest.java Tue Dec 24 12:05:35 2024
@@ -894,6 +894,7 @@ public class PDFMergerUtilityTest extend
                     PDPage page = annotation.getPage();
                     if (annotation instanceof PDAnnotationLink)
                     {
+                        // PDFBOX-5928: check whether the destination of a link annotation is an orphan
                         PDAnnotationLink link = (PDAnnotationLink) annotation;
                         PDDestination destination = link.getDestination();
                         if (destination == null)
@@ -1085,6 +1086,33 @@ public class PDFMergerUtilityTest extend
         dstDoc.close();
         doc.close();
     }
+
+    /**
+     * PDFBOX-5929: Check that orphan annotations are removed from the structure tree if annotations
+     * were removed from the pages (don't do that!).
+     *
+     * @throws IOException
+     */
+    public void testSplitWithStructureTreeAndDestinationsAndRemovedAnnotations() throws IOException
+    {
+        PDDocument doc = PDDocument.load(new File(SRCDIR, "PDFBOX-5762-722238.pdf"));
+        Splitter splitter = new Splitter();
+        for (PDPage page : doc.getPages())
+        {
+            page.setAnnotations(Collections.EMPTY_LIST);
+        }
+        splitter.setStartPage(1);
+        splitter.setEndPage(2);
+        splitter.setSplitAtPage(2);
+        List<PDDocument> splitResult = splitter.split(doc);
+        assertEquals(1, splitResult.size());
+        PDDocument dstDoc = splitResult.get(0);
+
+        assertEquals(2, dstDoc.getNumberOfPages());
+        checkForPageOrphans(dstDoc);
+        dstDoc.close();
+        doc.close();
+    }
 
     /**
      * Check for the bug that happened in PDFBOX-5792, where a destination was outside a target