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/11/01 07:24:52 UTC
svn commit: r1883031 - in /pdfbox/branches/2.0/pdfbox/src:
main/java/org/apache/pdfbox/pdmodel/fixup/
main/java/org/apache/pdfbox/pdmodel/fixup/processor/
test/java/org/apache/pdfbox/pdmodel/interactive/form/
Author: msahyoun
Date: Sun Nov 1 07:24:52 2020
New Revision: 1883031
URL: http://svn.apache.org/viewvc?rev=1883031&view=rev
Log:
PDFBOX-3891: handle adding nested fields; move handling decision from processor to fixup
Modified:
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/AcroFormDefaultFixup.java
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/processor/AcroFormOrphanWidgetsProcessor.java
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFromAnnotsTest.java
Modified: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/AcroFormDefaultFixup.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/AcroFormDefaultFixup.java?rev=1883031&r1=1883030&r2=1883031&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/AcroFormDefaultFixup.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/AcroFormDefaultFixup.java Sun Nov 1 07:24:52 2020
@@ -20,6 +20,7 @@ import org.apache.pdfbox.pdmodel.PDDocum
import org.apache.pdfbox.pdmodel.fixup.processor.AcroFormDefaultsProcessor;
import org.apache.pdfbox.pdmodel.fixup.processor.AcroFormGenerateAppearancesProcessor;
import org.apache.pdfbox.pdmodel.fixup.processor.AcroFormOrphanWidgetsProcessor;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
public class AcroFormDefaultFixup extends AbstractFixup
{
@@ -30,8 +31,29 @@ public class AcroFormDefaultFixup extend
@Override
public void apply() {
- new AcroFormOrphanWidgetsProcessor(document).process();
new AcroFormDefaultsProcessor(document).process();
- new AcroFormGenerateAppearancesProcessor(document).process();
+
+ /*
+ * Get the AcroForm in it's current state.
+ *
+ * Also note: getAcroForm() applies a default fixup which this processor
+ * is part of. So keep the null parameter otherwise this will end
+ * in an endless recursive call
+ */
+ PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm(null);
+
+ // PDFBOX-4985
+ // build the visual appearance as there is none for the widgets
+ if (acroForm != null && acroForm.getNeedAppearances())
+ {
+ if (acroForm.getFields().isEmpty())
+ {
+ new AcroFormOrphanWidgetsProcessor(document).process();
+ }
+
+ // PDFBOX-4985
+ // build the visual appearance as there is none for the widgets
+ new AcroFormGenerateAppearancesProcessor(document).process();
+ }
}
}
\ No newline at end of file
Modified: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/processor/AcroFormOrphanWidgetsProcessor.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/processor/AcroFormOrphanWidgetsProcessor.java?rev=1883031&r1=1883030&r2=1883031&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/processor/AcroFormOrphanWidgetsProcessor.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/fixup/processor/AcroFormOrphanWidgetsProcessor.java Sun Nov 1 07:24:52 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;
@@ -63,9 +66,7 @@ public class AcroFormOrphanWidgetsProces
*/
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm(null);
- // PDFBOX-4985 AcroForm with NeedAppearances true and empty fields array
- // but Widgets in page annotations
- if (acroForm != null && acroForm.getNeedAppearances() && acroForm.getFields().isEmpty())
+ if (acroForm != null)
{
resolveFieldsFromWidgets(acroForm);
}
@@ -73,32 +74,78 @@ public class AcroFormOrphanWidgetsProces
private void resolveFieldsFromWidgets(PDAcroForm acroForm)
{
+ Map<String, PDField> nonTerminalFieldsMap = new HashMap<String, PDField>();
+
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;
}
/*
Modified: pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFromAnnotsTest.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFromAnnotsTest.java?rev=1883031&r1=1883030&r2=1883031&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFromAnnotsTest.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFromAnnotsTest.java Sun Nov 1 07:24:52 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.cos.COSArray;
import org.apache.pdfbox.cos.COSDictionary;
@@ -27,7 +30,9 @@ 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.fixup.AbstractFixup;
import org.apache.pdfbox.pdmodel.fixup.AcroFormDefaultFixup;
+import org.apache.pdfbox.pdmodel.fixup.processor.AcroFormOrphanWidgetsProcessor;
import org.junit.Test;
/**
@@ -161,4 +166,117 @@ public class PDAcroFormFromAnnotsTest
IOUtils.closeQuietly(testPdf);
}
}
+
+ /**
+ * PDFBOX-3891 AcroForm with empty fields entry
+ *
+ * With the default correction nothing shall be added
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testFromAnnots3891DontCreateFields() throws IOException
+ {
+
+ String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12881055/merge-test.pdf";
+
+ PDDocument testPdf = null;
+ try
+ {
+ testPdf = PDDocument.load(new URL(sourceUrl).openStream());
+ PDDocumentCatalog catalog = testPdf.getDocumentCatalog();
+ // need to do a low level cos access as the PDModel access will build the AcroForm
+ COSDictionary cosAcroForm = (COSDictionary) catalog.getCOSObject().getDictionaryObject(COSName.ACRO_FORM);
+ COSArray cosFields = (COSArray) cosAcroForm.getDictionaryObject(COSName.FIELDS);
+ assertEquals("Initially there shall be 0 fields", 0, cosFields.size());
+ PDAcroForm acroForm = catalog.getAcroForm();
+ assertEquals("After call with default correction there shall be 0 fields", 0, acroForm.getFields().size());
+ }
+ finally
+ {
+ IOUtils.closeQuietly(testPdf);
+ }
+ }
+
+ /**
+ * PDFBOX-3891 AcroForm with empty fields entry
+ *
+ * Special fixup to create fields
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testFromAnnots3891CreateFields() throws IOException
+ {
+
+ String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12881055/merge-test.pdf";
+ String acrobatSourceUrl = "https://issues.apache.org/jira/secure/attachment/13014447/merge-test-na-acrobat.pdf";
+
+ int numFormFieldsByAcrobat = 0;
+
+ // will build the expected fields using the acrobat source document
+ Map<String, PDField> fieldsByName = new HashMap<String, PDField>();
+
+ PDDocument testPdf = null;
+ try
+ {
+ testPdf = PDDocument.load(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);
+ }
+ }
+ finally
+ {
+ IOUtils.closeQuietly(testPdf);
+ }
+
+ try
+ {
+ testPdf = PDDocument.load(new URL(sourceUrl).openStream());
+ PDDocumentCatalog catalog = testPdf.getDocumentCatalog();
+ // need to do a low level cos access as the PDModel access will build the AcroForm
+ COSDictionary cosAcroForm = (COSDictionary) catalog.getCOSObject().getDictionaryObject(COSName.ACRO_FORM);
+ COSArray cosFields = (COSArray) cosAcroForm.getDictionaryObject(COSName.FIELDS);
+ 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());
+
+ // 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));
+ }
+ }
+ finally
+ {
+ IOUtils.closeQuietly(testPdf);
+ }
+ }
+
+ /*
+ * Create fields from widget annotations
+ */
+ class CreateFieldsFixup extends AbstractFixup
+ {
+ CreateFieldsFixup(PDDocument document)
+ {
+ super(document);
+ }
+
+ @Override
+ public void apply() {
+ new AcroFormOrphanWidgetsProcessor(document).process();
+
+ }
+ }
}