PDFBox将数据插入多页doc

时间:2016-10-19 20:06:42

标签: pdf groovy textfield pdfbox

我试图创建一个包含字段的简单pdf多页文档。为此,我有一个模板pdf,在代码中我根据需要多次克隆该模板,以便自己创建文档。

问题在于插入一些数据。我尝试插入到文档中的数据类型不应该跨页面进行更改。而不是那样,它在所有页面中都保持静态,就像" Pages"表示此文档包含的页数的数字。

现在,在我的模板pdf中,我有一些文本字段,例如" Shipper1"和"页面"。我希望能够将我的数据插入到这些文本字段中,以便文档中的所有页面都将在其#34; Shipper1"和"页面"字段。

我的代码目前仅在第一页上执行此操作。它完美地显示了数据。另一方面,当我转到另一页时,数据不会显示在那里。它只是显示一个空字段。

以下是我启动pdf文档的代码:

static void initiatePdf() {
        // Initiate a new PDF Box object and get the acro form from it
        File file = new File(Constants.Paths.EMPTY_DOC)
        PDDocument tempDoc

        Evaluator evaluator = new Evaluator(metaHolder)
        int numPages = evaluator.getNumOfPagesRequired(objects)

        FieldRenamer renamer = new FieldRenamer()

        PDResources res = new PDResources()
        COSDictionary acroFormDict = new COSDictionary()

        List<PDField> fields = []

        Closure isFieldExist = {List<PDField> elements, String fieldName ->
            elements.findAll{it.getFullyQualifiedName() == fieldName}.size() > 0
        }

        for(int i = 0; i < numPages; i++) {
            tempDoc = new PDDocument().load(file)

            PDDocumentCatalog docCatalog = tempDoc.getDocumentCatalog()
            PDAcroForm acroForm = docCatalog.acroForm

            PDPage page = (PDPage) docCatalog.getPages().get(0)

            renamer.setCurrentForm(acroForm)

            if(i == 0) {
                res = acroForm.getDefaultResources()
                acroFormDict.mergeInto(acroForm.getCOSObject())
                renamer.renameFields(1)
            } else
                renamer.renameFields(i*10+1)

            List<PDField> newFields = acroForm.fields.findAll { PDField newField ->
                isFieldExist(fields, newField.getFullyQualifiedName()) == false
            }

            fields.addAll(newFields)
            document.addPage(page)
        }

        PDAcroForm acroForm = new PDAcroForm(document, acroFormDict);
        acroForm.setFields(fields)

        acroForm.setDefaultResources(res);

        document.documentCatalog.setAcroForm(acroForm)
    }

首先要做的几件事: metaHolder实例保存有关所有信息 位于acro表单内的字段。信息是:字段名称,字段小部件宽度,字段字体和字体大小

evaluator只是Evaluator类的实例。其目的是分析动态数据并确定包含所有文本数据所需的页数。

以下是我尝试使用文字填充字段的地方:

static void populateData() {
    def properties = ["$Constants.Fields.SHIPPER" : "David"]
    FieldPopulater populater = new FieldPopulater(document, metaHolder)

    populater.populateStaticFields(properties)
}

FieldPopulater类:

package app.components

import app.StringUtils
import app.components.entities.DGObject
import app.components.entities.FieldMeta
import org.apache.pdfbox.pdmodel.PDDocument
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm
import org.apache.pdfbox.pdmodel.interactive.form.PDField

/**
 * Created by David on 18/10/2016.
 */
class FieldPopulater {

    PDAcroForm acroForm
    FormMetaHolder metaHolder
    FieldPopulater(PDDocument document, FormMetaHolder metaHolder) {
        this.acroForm = document.getDocumentCatalog().acroForm
        this.metaHolder = metaHolder
    }

    void populateStaticFields(properties) {
        List<PDField> fields = []
        properties.each {fieldName, data ->
            FieldMeta fieldMeta = metaHolder.getMetaData(fieldName)

            fields = acroForm.fields.findAll { PDField field ->
                String currentName = field.getFullyQualifiedName()
                char lastChar = currentName[-1]
                if(Character.isDigit(lastChar)) {
                    currentName = currentName.substring(0,currentName.size()-1)
                }

                currentName == fieldName
            }

            if(fields.size() > 1) {
                int counter = 1
                String tempData = data
                String currentFitData
                while(tempData.isEmpty() != true) {
                    int maxWords = Utils.getMaxWords(tempData, fieldMeta)
                    currentFitData = StringUtils.getTextByWords(tempData, maxWords)
                    tempData = StringUtils.chopTextByWords(tempData, maxWords)

                    PDField field = fields.find{it.getFullyQualifiedName()[-1] == "$counter"}
                    field?.setValue(currentFitData)

                    counter++
                }
            } else {
                PDField tempField = fields[0]
                tempField.setValue(data)
            }
        }
    }
}

结果是,在第一页中,字段&#34;托运人&#34;价值为#34;大卫&#34; 在第二页中,字段&#34;托运人&#34;是空的。

这是一张图片。第一页:

第二页:

这里有什么问题?

更新:我尝试将每个新的acro表单的小部件添加到当前页面,这样每个字段都会有一些代表该字段的子小部件,但它仍然无效。

 // All the widgets that are associated with the fields
 List<PDAnnotationWidget> widgets = acroForm.fields.collect {PDField field -> field.getWidgets().get(0)}

 page.annotations.addAll(widgets)

更新:我还尝试将字段的当前窗口小部件添加到父字段的窗口小部件集合中。这是代码:

List<PDAnnotationWidget> widgets = []
// All the widgets that are associated with the fields
acroForm.fields.each {PDField field ->
        PDAnnotationWidget widget = field.widgets.get(0)

        // Adding the following widget to the page and to the field's list of annotation widgets
        widgets.add(widget)
        fields.find {it.getFullyQualifiedName() == field.getFullyQualifiedName()}?.widgets.add(widget)
            }

 page.annotations.addAll(widgets)

1 个答案:

答案 0 :(得分:0)

你想要的是拥有相同领域的sereval视觉表示。这是通过为这样一个字段提供几个注释小部件来完成的。

在PDF中,当一个字段只有一个注释窗口小部件时,它们共享一个公共字典。当它有多个时,注释小部件位于该字段的子列表中。

如果您想要为一个字段添加多个注释小部件,则需要使用new PDAnnotationWidget()创建注释小部件,而不是调用field.getWidgets().get(0)并使用该小部件。必须将这些小部件添加到列表中,并且必须使用setWidgets()将此列表分配给该字段。对于每个小部件,您必须拨打setRectangle()setPage()以及setParent()

这方面的一个例子是新的CreateMultiWidgetsForm.java example。 {2.0}中尚未提供setParent()方法(但将在2.0.4中)。在这个答案中,它被一个以不太优雅的方式做同样事情的调用所取代。

public final class CreateMultiWidgetsForm
{
    private CreateMultiWidgetsForm()
    {
    }

    public static void main(String[] args) throws IOException
    {
        // Create a new document with 2 empty pages.
        PDDocument document = new PDDocument();
        PDPage page1 = new PDPage(PDRectangle.A4);
        document.addPage(page1);
        PDPage page2 = new PDPage(PDRectangle.A4);
        document.addPage(page2);

        // Adobe Acrobat uses Helvetica as a default font and 
        // stores that under the name '/Helv' in the resources dictionary
        PDFont font = PDType1Font.HELVETICA;
        PDResources resources = new PDResources();
        resources.put(COSName.getPDFName("Helv"), font);

        // Add a new AcroForm and add that to the document
        PDAcroForm acroForm = new PDAcroForm(document);
        document.getDocumentCatalog().setAcroForm(acroForm);

        // Add and set the resources and default appearance at the form level
        acroForm.setDefaultResources(resources);

        // Acrobat sets the font size on the form level to be
        // auto sized as default. This is done by setting the font size to '0'
        String defaultAppearanceString = "/Helv 0 Tf 0 g";
        acroForm.setDefaultAppearance(defaultAppearanceString);

        // Add a form field to the form.
        PDTextField textBox = new PDTextField(acroForm);
        textBox.setPartialName("SampleField");
        // Acrobat sets the font size to 12 as default
        // This is done by setting the font size to '12' on the
        // field level. 
        // The text color is set to blue in this example.
        // To use black, replace "0 0 1 rg" with "0 0 0 rg" or "0 g".
        defaultAppearanceString = "/Helv 12 Tf 0 0 1 rg";
        textBox.setDefaultAppearance(defaultAppearanceString);

        // add the field to the AcroForm
        acroForm.getFields().add(textBox);

        // Specify 1st annotation associated with the field
        PDAnnotationWidget widget1 = new PDAnnotationWidget();
        PDRectangle rect = new PDRectangle(50, 750, 250, 50);
        widget1.setRectangle(rect);
        widget1.setPage(page1);
        widget1.getCOSObject().setItem(COSName.PARENT, textBox);

        // Specify 2nd annotation associated with the field
        PDAnnotationWidget widget2 = new PDAnnotationWidget();
        PDRectangle rect2 = new PDRectangle(200, 650, 100, 50);
        widget2.setRectangle(rect2);
        widget2.setPage(page2);
        widget2.getCOSObject().setItem(COSName.PARENT, textBox);

        // set green border and yellow background for 1st widget
        // if you prefer defaults, just delete this code block
        PDAppearanceCharacteristicsDictionary fieldAppearance1
                = new PDAppearanceCharacteristicsDictionary(new COSDictionary());
        fieldAppearance1.setBorderColour(new PDColor(new float[]{0,1,0}, PDDeviceRGB.INSTANCE));
        fieldAppearance1.setBackground(new PDColor(new float[]{1,1,0}, PDDeviceRGB.INSTANCE));
        widget1.setAppearanceCharacteristics(fieldAppearance1);

        // set red border and green background for 2nd widget
        // if you prefer defaults, just delete this code block
        PDAppearanceCharacteristicsDictionary fieldAppearance2
                = new PDAppearanceCharacteristicsDictionary(new COSDictionary());
        fieldAppearance2.setBorderColour(new PDColor(new float[]{1,0,0}, PDDeviceRGB.INSTANCE));
        fieldAppearance2.setBackground(new PDColor(new float[]{0,1,0}, PDDeviceRGB.INSTANCE));
        widget2.setAppearanceCharacteristics(fieldAppearance2);

        List <PDAnnotationWidget> widgets = new ArrayList<PDAnnotationWidget>();
        widgets.add(widget1);
        widgets.add(widget2);
        textBox.setWidgets(widgets);

        // make sure the annotations are visible on screen and paper
        widget1.setPrinted(true);
        widget2.setPrinted(true);

        // Add the annotations to the pages
        page1.getAnnotations().add(widget1);
        page2.getAnnotations().add(widget2);

        // set the field value
        textBox.setValue("Sample field");

        document.save("MultiWidgetsForm.pdf");
        document.close();
    }
}