【问题标题】:PDFBox inserting data into multi-page docPDFBox将数据插入多页文档
【发布时间】:2016-10-19 20:06:41
【问题描述】:

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

问题在于在其中插入一些数据。我尝试插入文档的数据类型不应该在页面之间改变。相反,它在所有页面中保持静态,就像表示此文档包含的页面数的“Pages”数字一样。

现在,在我的 pdf 模板中,我有一些文本字段,例如“Shipper1”和“Pages”。我希望能够将我的数据插入此文本字段,以便文档中的所有页面在其“Shipper1”和“Pages”字段中都有此值。

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

这是我启动 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)
    }

首先有几件事: metaHolderinstance 保存所有的信息 位于 acro 表单内的字段。信息是:字段名称、字段小部件宽度、字段字体和字体大小

evaluator 只是Evaluator 类的实例。其目的是分析动态数据并决定需要多少页才能包含所有文本数据。

这是我尝试用文本填充字段的地方:

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

    populater.populateStaticFields(properties)
}

FieldPopulator 类:

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

结果是,在第一页中,“Shipper”字段的值为“David” 在第二页中,“发货人”字段为空。

这是一张图片。第一页:

第二页:

这里有什么问题?

更新:我尝试将每个新的 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)

【问题讨论】:

  • 这里已经很晚了,所以只是一个快速提示:要在页面之间具有相同的值,您的字段需要有多个注释小部件,即每个页面上都有一个。该字段必须具有这些小部件作为孩子。每个页面都必须在其注释列表中包含此小部件。获取一些具有此类功能的现有表单文档(或使用 Adob​​e Acrobat 创建一个),并使用 PDFDebugger 进行查看。
  • 我尝试按照您所说的进行操作,即将 acro 表单的小部件添加到页面,但问题仍然存在。我在帖子中添加了我编写的代码
  • 不需要每个页面都有一个单独的小部件。 IE。该字段必须有两个孩子小部件。我给自己做了一个备忘录来扩展 acroform 示例。
  • 好的,我完全按照您所说的进行了尝试,但问题是当我检查每个父字段具有的小部件数量时,在最后阶段,它等于 1。所以这意味着我没有添加孩子们的小部件正确地添加到父字段。我在帖子中添加了新代码
  • 当一个字段需要多个小部件时,您需要使用new PDAnnotationWidget() 创建小部件。这些小部件必须添加到列表中,并且该列表必须分配给带有setWidgets() 的字段。对于每个小部件,您必须调用 setRectangle()setPage()

标签: pdf groovy textfield pdfbox


【解决方案1】:

您想要的是对同一领域有多个视觉表示。这是通过为此类字段设置多个注释小部件来完成的。

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

当您想要一个字段的多个注释小部件时,您需要使用new PDAnnotationWidget() 创建注释小部件,而不是调用field.getWidgets().get(0) 并使用那个。这些小部件必须添加到列表中,并且该列表必须分配给带有setWidgets() 的字段。对于每个小部件,您必须调用 setRectangle()setPage()setParent()

新的CreateMultiWidgetsForm.java example 就是一个例子。 setParent() 方法在 2.0.3 中尚不可用(但将在 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();
    }
}

【讨论】:

    猜你喜欢
    • 2014-03-26
    • 1970-01-01
    • 2012-01-29
    • 2023-04-08
    • 1970-01-01
    • 2014-07-31
    • 2011-10-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多