当前位置: 首页 > 知识库问答 >
问题:

PDFBox在多页单据中插入数据

白君之
2023-03-14

我正在尝试创建一个简单的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)
    }

首先有两件事:metaholder实例保存有关acro表单中所有字段的信息。信息为:字段名称、字段小部件宽度、字段字体和字体大小

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

结果是,在第一页中,字段“shipper”的值为“david”;在第二页中,字段“shipper”为空。

这里有一个图像。第一页:

 // 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个答案

郏佐
2023-03-14

你想要的是有相同领域的sereval视觉表示。这是通过为这样一个字段设置几个注释小部件来实现的。

在PDF中,当一个字段只有一个注释小部件时,它们共享一个公共字典。当它有几个时,注释小部件就在字段的子列表中。

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

在新的CreateMultiWidgetsForm.java示例中有一个这样的示例。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();
    }
}
 类似资料:
  • 我正在使用PDFbox下载PDF。我想在中间添加一些新页面。 此代码在PDF结尾处插入新页面。如何在另一个位置插入页面?

  • 嗨,我是拉威尔的新手,我在将数据插入数据库和抛出错误时遇到了麻烦 msgstr"Countable():参数必须是实现可数的数组或对象" 我想在数据库中添加所有注册员工的出勤信息 控制器 叶片输出: 模型类员工出席扩展模型{// } 模型2 命名空间App; 使用Illumb\Database\Elount\Model; 类employee_data扩展模型{//protected$fillabe

  • 在一个带桶的表格中进行这种多次插入,这样行吗?是否影响台面的铲斗?

  • 问题内容: 我正在寻找一种刷新页面时停止在数据库中插入或发送数据的方法。 这是我的代码: user_details_page.php Confirm_page.php 因此,每次我刷新确认page.php时,都会将数据发送到数据库。如何停止呢? 问题答案: 将用户带到新页面: 通过这样做,刷新的用户将得到刷新,这意味着它将不会重复执行两次插入操作。 最好的建议 :如果没有,请检查用户是否首先存在!

  • 我想我在这段代码中遇到了一点问题:当我试图在数据库中插入值时,我遇到了一个错误。

  • 问题内容: 我试图在 Flask-SQLAlchemy中 建立多对多关系,但似乎我不知道如何填写 “多对多标识符数据库” 。您能帮我了解我在做什么错以及应该怎么看吗? 然后是我的标识符数据库: 到目前为止,当我尝试将数据插入数据库时​​,看起来像这样。 现在我的问题是,由于我真的无法创建“ student_identifier”对象,如何将其添加到多对多数据库?如果可以的话,它可能看起来像这样: