Java PDFBox如何克隆页面

Java PDFBox如何克隆页面,java,pdfbox,Java,Pdfbox,我正在编写一个代码,它假设将一个数据插入表单,然后用该数据填充表单 我正在尝试创建一个包含给定数据的简单pdf多页文档 我的问题是我的代码部分工作。我可以连续6次运行代码并生成多页文档,但在第7次尝试时,我会遇到如下错误: java.io.IOException: COSStream has been closed and cannot be read. Perhaps its enclosing PDDocument has been closed? at org.apache.pdf

我正在编写一个代码,它假设将一个数据插入表单,然后用该数据填充表单

我正在尝试创建一个包含给定数据的简单pdf多页文档


我的问题是我的代码部分工作。我可以连续6次运行代码并生成多页文档,但在第7次尝试时,我会遇到如下错误:

java.io.IOException: COSStream has been closed and cannot be read. Perhaps its enclosing PDDocument has been closed?
    at org.apache.pdfbox.cos.COSStream.checkClosed(COSStream.java:77)
    at org.apache.pdfbox.cos.COSStream.createOutputStream(COSStream.java:198)
    at org.apache.pdfbox.cos.COSStream.createOutputStream(COSStream.java:186)
    at org.apache.pdfbox.pdmodel.interactive.form.AppearanceGeneratorHelper.writeToStream(AppearanceGeneratorHelper.java:641)
    at org.apache.pdfbox.pdmodel.interactive.form.AppearanceGeneratorHelper.setAppearanceContent(AppearanceGeneratorHelper.java:303)
    at org.apache.pdfbox.pdmodel.interactive.form.AppearanceGeneratorHelper.setAppearanceValue(AppearanceGeneratorHelper.java:170)
    at org.apache.pdfbox.pdmodel.interactive.form.PDTextField.constructAppearances(PDTextField.java:263)
    at org.apache.pdfbox.pdmodel.interactive.form.PDTerminalField.applyChange(PDTerminalField.java:228)
    at org.apache.pdfbox.pdmodel.interactive.form.PDTextField.setValue(PDTextField.java:218)
    at app.components.FieldPopulator.insertData(FieldPopulator.java:153)
    at app.components.FieldPopulator.populateGoodsFields(FieldPopulator.java:69)
    at app.Main.populateData(Main.java:55)
    at app.Main.main(Main.java:25)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:143)
Error: COSStream has been closed and cannot be read. Perhaps its enclosing PDDocument has been closed?Document has been createdjava.io.IOException: COSStream has been closed and cannot be read. Perhaps its enclosing PDDocument has been closed?
    at org.apache.pdfbox.cos.COSStream.checkClosed(COSStream.java:77)
    at org.apache.pdfbox.cos.COSStream.createRawInputStream(COSStream.java:125)
    at org.apache.pdfbox.pdfwriter.COSWriter.visitFromStream(COSWriter.java:1200)
    at org.apache.pdfbox.cos.COSStream.accept(COSStream.java:383)
    at org.apache.pdfbox.cos.COSObject.accept(COSObject.java:158)
    at org.apache.pdfbox.pdfwriter.COSWriter.doWriteObject(COSWriter.java:522)
    at org.apache.pdfbox.pdfwriter.COSWriter.doWriteObjects(COSWriter.java:460)
    at org.apache.pdfbox.pdfwriter.COSWriter.doWriteBody(COSWriter.java:444)
    at org.apache.pdfbox.pdfwriter.COSWriter.visitFromDocument(COSWriter.java:1096)
    at org.apache.pdfbox.cos.COSDocument.accept(COSDocument.java:419)
    at org.apache.pdfbox.pdfwriter.COSWriter.write(COSWriter.java:1367)
    at org.apache.pdfbox.pdfwriter.COSWriter.write(COSWriter.java:1254)
    at org.apache.pdfbox.pdmodel.PDDocument.save(PDDocument.java:1232)
    at app.Main.saveDocument(Main.java:62)
    at app.Main.main(Main.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:143)
我想知道为什么这个错误不断出现,或者为什么代码有时有效,有时无效

下面是主类(我删除了代码中不相关的部分):

现在,我认为问题在于我在
build()
方法中处理
PDField
s及其注释的方式,但我无法确定问题的根源

请帮我解决这个问题,我已经为此挣扎了一段时间,我只想继续我的项目,因为我知道这个项目的这部分工作非常完美

提前谢谢

更新:

好的,问题的主要原因是
DocumentBuilder
类中的
build()
方法。我将尝试澄清一下我的代码:

首先,我创建了一个新文档,它将作为结果文档

接下来,我使用我的Evalutor类计算出顺序需要多少页面 以填充数据。在
计算器中
我执行某些计算。需要注意的是,我使用了
FormMetaHolder
对象,它的任务是提供有关所有表单字段的元数据,例如字段字体、字体大小、, 字段宽度和字段名。field font变量包含对的引用 我从表单资源中获取的实际PDFont对象

接下来,我循环页面数,对于每个迭代,我执行以下操作:

.1。从模板文件创建一个新的
PDDocument
,并将其分配给
templateDoc

.2。从该文档中获取acro表单。 .2.1. 如果是第一次迭代,则指定默认值 acro表单的资源到
res
变量

.3。重命名要包含不同值的相同字段。因为PDFBox中的多行功能充满了bug,所以我将相同大小的某些字段划分为10个较小的字段。每个像这样的小字段都有编号。例如,如果这个大字段被称为“UN”,那么 所有较小的字段将被称为UN1、UN2、, UN3

现在,如果我有很多数据要插入,我希望能够复制模板文档,唯一的例外是适当地重命名那些较小的字段。例如,如果我需要2页来存储 由用户提供,我需要20个较小的字段,每个页面10个。 我需要重命名所有UNs字段,如下所示: UN1-UN20

.4。在我相应地重命名所有较小的字段之后,我尝试为我的父字段列表找到新的注释。我这样做的原因是,除了“较小”的字段外,我还有不变的静态字段 跨越页面

.5。找到所有正确的小部件后,我将当前页面添加到结果文档中,并关闭用于实例化
templateDoc
对象的
InputStream

for循环结束后,我创建了一个新的
PDAcroForm
对象,并将结果文档作为参数。我将其字段设置为
parentFields
并设置默认资源 成为
res
对象


最后,我将文档acro表单设置为新创建的acro表单。

“我可以连续6次运行代码并生成多页文档,但在第7次尝试时,我会遇到这样的错误”请澄清。同一文档组合是7次还是不同文档组合?是运行整个程序7次,还是只运行部分?(主要部分是静态的)为什么生成PDDocument()对象只是为了覆盖它们?我至少看过两次。这不是问题的原因,但会使代码更难阅读。我相信你的IDE会标记这一点。我的猜测是它与关闭templateDoc有关,但我看不到您在哪里关闭它。@Tilmahauser“我的猜测是它与关闭templateDoc有关,但我看不到您在哪里关闭它。”-我的印象也是这样。这些实例超出了范围,所以垃圾收集最终会处理它们。这使得效果看起来是伪随机的。David,您是否尝试过使用
PDDocument.importPage()
导入相关页面?方法注释解释了使用
PDDocument.addPage()
简单添加页面的区别,并且还包含一个警告。@tilmahausher我所说的6次是指代码可以连续尝试几次,但在下一次尝试时可能突然失败。我使用的是一个模板。我之所以生成templateDoc,是因为我想收集唯一字段并将它们添加到parentFields列表中。我添加了一些关于我想做什么的描述do@mkl我尝试了
importPage()
方法,但没有成功。我添加了一些关于我要做什么的描述。请看一看!
package app;

import app.components.*;
import org.apache.pdfbox.pdmodel.PDDocument;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Created by David on 11/01/2017.
 */
public class Main {

    private static FormMetaHolder metaHolder = new FormMetaHolder();

    private static HashMap<String, Object> properties = new HashMap<>();
    private static List<GoodObject> objects = new ArrayList();
    private static PDDocument document = new PDDocument();

    public static void main(String[] args) throws IOException {
        initiateData(); // This method generates sample data. I removed it for simplicity
        initiatePdf();
        populateData();
        saveDocument();

        System.out.print("Document has been created");
    }

    public static void initiatePdf() {
        DocumentBuilder builder = new DocumentBuilder();
        builder.setObjectsData(objects);

        document = builder.build();
    }

    public static void populateData() throws IOException {
        FieldPopulator populator = new FieldPopulator(document.getDocumentCatalog().getAcroForm(), metaHolder);

        populator.numberPages();
        populator.populateStaticFields(properties);
        populator.populateGoodsFields(objects);
    }

    public static void saveDocument() {
        try {
            FileOutputStream out = new FileOutputStream(Constants.Paths.RESULT_FILE);

            document.save(out);
            document.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public PDDocument build() {
        PDDocument document = new PDDocument();
        try {
            int numPages = evaulator.getNumOfPages(objectsToInsert);

            // A FieldRenamer for renaming the DG fields sequentially
            FieldRenamer renamer = new FieldRenamer();

            PDResources res = new PDResources();

            // First one here is a list of the unique fields of the document
            List<PDField> parentFields = new ArrayList<>();

            for (int i = 1; i <= numPages; i++) {
                try {
                    InputStream stream = Main.class.getClassLoader().getResourceAsStream(Constants.Paths.EMPTY_DOC);
                    templateDoc = new PDDocument().load(stream);

                    PDDocumentCatalog catalog = templateDoc.getDocumentCatalog();
                    PDAcroForm acroForm = catalog.getAcroForm();

                    if (i == 1)
                        res = acroForm.getDefaultResources();

                    // Renaming the PDFields
                    renamer.setAcroForm(acroForm);
                    renamer.renameFields(i * 10 + 1 - 10);

                    PDPage page = catalog.getPages().get(0);

                    // Iterating over the acro form to find new fields that are not
                    // In the main collection of fields - the parentFields object
                    // If a field is already inside the parentFields list, add new
                    // PDAnnotationWidget to that field, for visual representation
                    // In the new page
                    List<PDAnnotation> widgets = new ArrayList<>();
                    for (PDField field : acroForm.getFields()) {
                        boolean flag = true;
                        for (PDField parentField : parentFields) {
                            String currentFieldName = field.getFullyQualifiedName();
                            String parentFieldName = parentField.getFullyQualifiedName();

                            if (currentFieldName.equals(parentFieldName)) {
                                flag = false;

                                PDAnnotationWidget existWidget = parentField.getWidgets().get(0);
                                PDAnnotationWidget widget = new PDAnnotationWidget();

                                widget.setPage(page);
                                widget.getCOSObject().setItem(COSName.PARENT, parentField);
                                widget.setRectangle(existWidget.getRectangle());

                                parentField.getWidgets().add(widget);
                                widgets.add(widget);
                                page.getAnnotations().add(widget);
                                break;
                            }
                        }

                        if (flag) {
                            parentFields.add(field);
                        }
                    }

                    document.addPage(page);

                    stream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    System.out.print("Error: could not load the template file");
                }
            }

            PDAcroForm acroForm = new PDAcroForm(document);

            acroForm.setFields(parentFields);
            acroForm.setDefaultResources(res);

            document.getDocumentCatalog().setAcroForm(acroForm);
        } catch (NullPointerException e) {
            e.printStackTrace();
            System.out.print("Error: no data has been set to insert");
        }

        return document;
    }