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)); + } } }