Java PDFBox如何克隆页面
我正在编写一个代码,它假设将一个数据插入表单,然后用该数据填充表单 我正在尝试创建一个包含给定数据的简单pdf多页文档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
我的问题是我的代码部分工作。我可以连续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;
}