You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ms...@apache.org on 2020/10/31 19:18:23 UTC

svn commit: r1883025 - in /pdfbox/trunk/pdfbox/src: main/java/org/apache/pdfbox/pdmodel/fixup/processor/AcroFormOrphanWidgetsProcessor.java test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFromAnnotsTest.java

Author: msahyoun
Date: Sat Oct 31 19:18:22 2020
New Revision: 1883025

URL: http://svn.apache.org/viewvc?rev=1883025&view=rev
Log:
PDFBOX-3891: handle adding nested fields; enable test

Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/processor/AcroFormOrphanWidgetsProcessor.java
    pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFromAnnotsTest.java

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/processor/AcroFormOrphanWidgetsProcessor.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/processor/AcroFormOrphanWidgetsProcessor.java?rev=1883025&r1=1883024&r2=1883025&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/processor/AcroFormOrphanWidgetsProcessor.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/processor/AcroFormOrphanWidgetsProcessor.java Sat Oct 31 19:18:22 2020
@@ -17,11 +17,14 @@
 package org.apache.pdfbox.pdmodel.fixup.processor;
 
 import java.io.IOException;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.fontbox.ttf.TrueTypeFont;
+import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.PDPage;
@@ -71,34 +74,83 @@ public class AcroFormOrphanWidgetsProces
 
     private void resolveFieldsFromWidgets(PDAcroForm acroForm)
     {
+        Map<String, PDField> nonTerminalFieldsMap = new HashMap<>();
+
         LOG.debug("rebuilding fields from widgets");
+
         List<PDField> fields = acroForm.getFields();
+
         for (PDPage page : document.getPages())
         {
             try
             {
-                List<PDAnnotation> annots = page.getAnnotations();
-                for (PDAnnotation annot : annots)
-                {
-                    if (annot instanceof PDAnnotationWidget)
-                    {
-                        PDField field = PDFieldFactory.createField(acroForm, annot.getCOSObject(), null);
-                        if (field instanceof PDVariableText)
-                        {
-                            ensureFontResources(acroForm.getDefaultResources(), (PDVariableText) field);
-                        }
-                        fields.add(field);
-                    }
-                }
+                handleAnnotations(acroForm, fields, page.getAnnotations(), nonTerminalFieldsMap);
             }
             catch (IOException ioe)
             {
                 LOG.debug("couldn't read annotations for page " + ioe.getMessage());
             }
         }
+
         acroForm.setFields(fields);
+
+        // ensure that PDVariableText fields have the neccesary resources
+        for (PDField field : acroForm.getFieldTree())
+        {
+            if (field instanceof PDVariableText)
+            {
+                ensureFontResources(acroForm.getDefaultResources(), (PDVariableText) field);
+            }
+        }
     }
 
+    private void handleAnnotations(PDAcroForm acroForm, List<PDField> fields, List<PDAnnotation> annotations, Map<String, PDField> nonTerminalFieldsMap)
+    {
+        for (PDAnnotation annot : annotations)
+        {
+            if (annot instanceof PDAnnotationWidget)
+            {
+                if (annot.getCOSObject().containsKey(COSName.PARENT))
+                {
+                    PDField resolvedField = resolveNonRootField(acroForm, (PDAnnotationWidget) annot, nonTerminalFieldsMap);
+                    if (resolvedField != null)
+                    {
+                        fields.add(resolvedField);
+                    }
+                }
+                else
+                {
+                    fields.add(PDFieldFactory.createField(acroForm, annot.getCOSObject(), null));
+                }
+            }
+        }
+    }
+
+    /*
+     *  Widgets having a /Parent entry are non root fields. Go up until the root node is found
+     *  and handle from there.
+     */
+    private PDField resolveNonRootField(PDAcroForm acroForm, PDAnnotationWidget widget, Map<String, PDField> nonTerminalFieldsMap)
+    {
+        COSDictionary parent = widget.getCOSObject().getCOSDictionary(COSName.PARENT);
+        while (parent.containsKey(COSName.PARENT))
+        {
+            parent = parent.getCOSDictionary(COSName.PARENT);
+        }
+        
+        if (nonTerminalFieldsMap.get(parent.getString(COSName.T)) == null)
+        {
+            PDField field = PDFieldFactory.createField(acroForm, parent, null);
+            nonTerminalFieldsMap.put(field.getFullyQualifiedName(),field);
+
+            return field;
+        }
+
+        // this should not happen
+        return null;
+    }
+
+
     /*
      *  Lookup the font used in the default appearance and if this is 
      *  not available try to find a suitable font and use that.

Modified: pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFromAnnotsTest.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFromAnnotsTest.java?rev=1883025&r1=1883024&r2=1883025&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFromAnnotsTest.java (original)
+++ pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFromAnnotsTest.java Sat Oct 31 19:18:22 2020
@@ -17,9 +17,12 @@
 package org.apache.pdfbox.pdmodel.interactive.form;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 
 import java.io.IOException;
 import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
 
 import org.apache.pdfbox.Loader;
 import org.apache.pdfbox.cos.COSArray;
@@ -168,7 +171,7 @@ public class PDAcroFormFromAnnotsTest
      * 
      * @throws IOException
      */
-    // @Test
+    @Test
     public void testFromAnnots3891CreateFields() throws IOException
     {
 
@@ -177,11 +180,18 @@ public class PDAcroFormFromAnnotsTest
 
         int numFormFieldsByAcrobat = 0;
 
+        // will build the expected fields using the acrobat source document
+        Map<String, PDField> fieldsByName = new HashMap<>();
+
         try (PDDocument testPdf = Loader.loadPDF(new URL(acrobatSourceUrl).openStream()))
         {
             PDDocumentCatalog catalog = testPdf.getDocumentCatalog();
             PDAcroForm acroForm = catalog.getAcroForm(null);
             numFormFieldsByAcrobat = acroForm.getFields().size();
+            for (PDField field : acroForm.getFieldTree())
+            {
+                fieldsByName.put(field.getFullyQualifiedName(), field);
+            }
         }
 
         try (PDDocument testPdf = Loader.loadPDF(new URL(sourceUrl).openStream()))
@@ -193,6 +203,19 @@ public class PDAcroFormFromAnnotsTest
             assertEquals("Initially there shall be 0 fields", 0, cosFields.size());
             PDAcroForm acroForm = catalog.getAcroForm(new CreateFieldsFixup(testPdf));
             assertEquals("After rebuild there shall be " + numFormFieldsByAcrobat + " fields", numFormFieldsByAcrobat, acroForm.getFields().size());
+            testPdf.save("/home/msahyoun/Dokumente/Projekte/pdfbox-tests/PDFBOX-3891/merge-tests-fields-pdfbox.pdf");
+
+            // the the fields found are contained in the map
+            for (PDField field : acroForm.getFieldTree())
+            {
+                assertNotNull(fieldsByName.get(field.getFullyQualifiedName()));
+            }
+
+            // test all fields in the map are also found in the AcroForm
+            for (String fieldName : fieldsByName.keySet())
+            {
+                assertNotNull(acroForm.getField(fieldName));
+            }
         }
     }