Author: msahyoun
Date: Sat Apr 14 16:54:48 2018
New Revision: 1829151

URL: http://svn.apache.org/viewvc?rev=1829151&view=rev
Log:
PDFBOX-3809: flatten only specified fields

Modified:
    
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java
    
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormTest.java

Modified: 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java?rev=1829151&r1=1829150&r2=1829151&view=diff
==============================================================================
--- 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java
 (original)
+++ 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java
 Sat Apr 14 16:54:48 2018
@@ -251,6 +251,11 @@ public final class PDAcroForm implements
      */
     public void flatten(List<PDField> fields, boolean refreshAppearances) 
throws IOException
     {
+        // Nothing to flatten if there are no fields provided
+        if (fields.isEmpty()) {
+            return;
+        }
+
         // for dynamic XFA forms there is no flatten as this would mean to do 
a rendering
         // from the XFA content into a static PDF.
         if (xfaIsDynamic())
@@ -271,17 +276,21 @@ public final class PDAcroForm implements
         
         // the content stream to write to
         PDPageContentStream contentStream;
+
+        // get the widgets per page
+        Map<COSDictionary,Map<COSDictionary,PDAnnotationWidget>> 
pagesWidgetsMap = buildPagesWidgetsMap(fields);
         
         // preserve all non widget annotations
         for (PDPage page : document.getPages())
         {
+            Map<COSDictionary,PDAnnotationWidget> widgetsForPageMap = 
pagesWidgetsMap.get(page.getCOSObject());
             isContentStreamWrapped = false;
             
             List<PDAnnotation> annotations = new ArrayList<PDAnnotation>();
             
             for (PDAnnotation annotation: page.getAnnotations())
             {
-                if (!(annotation instanceof PDAnnotationWidget))
+                if (widgetsForPageMap != null && 
widgetsForPageMap.get(annotation.getCOSObject()) == null)
                 {
                     annotations.add(annotation);                 
                 }
@@ -350,7 +359,7 @@ public final class PDAcroForm implements
         }
         
         // remove the fields
-        setFields(Collections.<PDField>emptyList());
+        removeFields(fields);
         
         // remove XFA for hybrid forms
         dictionary.removeItem(COSName.XFA);
@@ -703,25 +712,6 @@ public final class PDAcroForm implements
         dictionary.setFlag(COSName.SIG_FLAGS, FLAG_APPEND_ONLY, appendOnly);
     }
     
-    private Map<COSDictionary, Integer> buildAnnotationToPageRef() {
-        Map<COSDictionary, Integer> annotationToPageRef = new 
HashMap<COSDictionary, Integer>();
-        
-        int idx = 0;
-        for (PDPage page : document.getPages()) {
-            try {
-                for (PDAnnotation annotation : page.getAnnotations()) {
-                    if (annotation instanceof PDAnnotationWidget) {
-                        annotationToPageRef.put(annotation.getCOSObject(), 
idx);
-                    }
-                }
-            } catch (IOException e) {
-                LOG.warn("Can't retriev annotations for page " + idx);
-            }
-            idx++;
-        }        
-        return annotationToPageRef;
-    }
-    
     /**
      * Check if there is a translation needed to place the annotations content.
      * 
@@ -780,4 +770,75 @@ public final class PDAcroForm implements
         PDResources resources = appearanceStream.getResources();
         return resources != null && 
resources.getXObjectNames().iterator().hasNext();
     }
+
+    private Map<COSDictionary,Map<COSDictionary,PDAnnotationWidget>> 
buildPagesWidgetsMap(List<PDField> fields)
+    {
+        Map<COSDictionary,Map<COSDictionary,PDAnnotationWidget>> 
pagesAnnotationsMap = new 
HashMap<COSDictionary,Map<COSDictionary,PDAnnotationWidget>>();
+        boolean hasMissingPageRef = false;
+        
+        for (PDField field : fields)
+        {
+            List<PDAnnotationWidget> widgets = field.getWidgets();
+            for (PDAnnotationWidget widget : widgets)
+            {
+                PDPage pageForWidget = widget.getPage();
+                if (pageForWidget != null)
+                {
+                    if (pagesAnnotationsMap.get(pageForWidget.getCOSObject()) 
== null)
+                    {
+                        Map<COSDictionary,PDAnnotationWidget> widgetsForPage = 
new HashMap<COSDictionary,PDAnnotationWidget>();
+                        widgetsForPage.put(widget.getCOSObject(), widget);
+                        pagesAnnotationsMap.put(pageForWidget.getCOSObject(), 
widgetsForPage);
+                    }
+                    else
+                    {
+                        Map<COSDictionary,PDAnnotationWidget> widgetsForPage = 
pagesAnnotationsMap.get(pageForWidget.getCOSObject());
+                        widgetsForPage.put(widget.getCOSObject(), widget);
+                    }
+                }
+                else
+                {
+                    hasMissingPageRef = true;
+                }
+            }
+        }
+        
+        // TODO: if there is a widget with a missing page reference 
+        // we'd need to build the map reverse i.e. form the annotations to the 
+        // widget. But this will be much slower so will be omitted for now.
+        if (hasMissingPageRef)
+        {
+            LOG.warn("There has been a widget with a missing page reference. 
Please report to the PDFBox project");
+        }
+        
+        return pagesAnnotationsMap;
+    }
+    
+    private void removeFields(List<PDField> fields)
+    {
+        for (PDField field : fields) {
+            if (field.getParent() == null)
+            {
+                COSArray cosFields = (COSArray) 
dictionary.getDictionaryObject(COSName.FIELDS);
+                for (int i=0; i<cosFields.size(); i++)
+                {
+                    COSDictionary element = (COSDictionary) 
cosFields.getObject(i);
+                    if (field.getCOSObject().equals(element)) {
+                        cosFields.remove(i);
+                    }
+                }
+            }
+            else 
+            {
+                COSArray kids = (COSArray) 
field.getParent().getCOSObject().getDictionaryObject(COSName.KIDS);
+                for (int i=0; i<kids.size(); i++)
+                {
+                    COSDictionary element = (COSDictionary) kids.getObject(i);
+                    if (field.getCOSObject().equals(element)) {
+                        kids.remove(i);
+                    }
+                }
+            }
+        }        
+    }
 }

Modified: 
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormTest.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormTest.java?rev=1829151&r1=1829150&r2=1829151&view=diff
==============================================================================
--- 
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormTest.java
 (original)
+++ 
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormTest.java
 Sat Apr 14 16:54:48 2018
@@ -24,14 +24,18 @@ import static org.junit.Assert.assertTru
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.io.IOUtils;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
 import org.apache.pdfbox.pdmodel.PDPage;
 import org.apache.pdfbox.pdmodel.PDResources;
 import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
 import org.apache.pdfbox.rendering.TestPDFToImage;
 import org.junit.After;
@@ -135,6 +139,41 @@ public class PDAcroFormTest
             System.out.println("Rendering of " + file + " failed or is not 
identical to expected rendering in " + IN_DIR + " directory");
         }
     }
+
+    @Test
+    public void testFlattenSpecificFieldsOnly() throws IOException
+    {
+        File file = new File(OUT_DIR, 
"AlignmentTests-flattened-specificFields.pdf");
+        
+        List<PDField> fieldsToFlatten = new ArrayList<PDField>();
+        
+        PDDocument testPdf = null;
+        try
+        {
+            testPdf = PDDocument.load(new File(IN_DIR, "AlignmentTests.pdf"));
+            PDAcroForm acroFormToFlatten = 
testPdf.getDocumentCatalog().getAcroForm();
+            int numFieldsBeforeFlatten = acroFormToFlatten.getFields().size();
+            int numWidgetsBeforeFlatten = countWidgets(testPdf);
+            
+            
fieldsToFlatten.add(acroFormToFlatten.getField("AlignLeft-Border_Small-Filled"));
+            
fieldsToFlatten.add(acroFormToFlatten.getField("AlignLeft-Border_Medium-Filled"));
+            
fieldsToFlatten.add(acroFormToFlatten.getField("AlignLeft-Border_Wide-Filled"));
+            
fieldsToFlatten.add(acroFormToFlatten.getField("AlignLeft-Border_Wide_Clipped-Filled"));
+            
+            acroFormToFlatten.flatten(fieldsToFlatten, true);
+            int numFieldsAfterFlatten = acroFormToFlatten.getFields().size();
+            int numWidgetsAfterFlatten = countWidgets(testPdf);
+
+            assertEquals(numFieldsBeforeFlatten, numFieldsAfterFlatten + 
fieldsToFlatten.size());
+            assertEquals(numWidgetsBeforeFlatten, numWidgetsAfterFlatten + 
fieldsToFlatten.size());
+            
+            testPdf.save(file);
+        }
+        finally
+        {
+            IOUtils.closeQuietly(testPdf);
+        }
+    } 
     
     /*
      * Test that we do not modify an AcroForm with missing resource information
@@ -239,6 +278,28 @@ public class PDAcroFormTest
         document.close();
         return baos.toByteArray();
     }
-    
+
+    private int countWidgets(PDDocument documentToTest)
+    {
+        int count = 0;
+        for (PDPage page : documentToTest.getPages())
+        {
+            try
+            {
+                for (PDAnnotation annotation : page.getAnnotations())
+                {
+                    if (annotation instanceof PDAnnotationWidget)
+                    {
+                        count ++;
+                    }
+                }
+            }
+            catch (IOException e)
+            {
+                // ignoring
+            }
+        }
+        return count;
+    } 
 }
 


Reply via email to