iText 填写表格/复制页面到新文档
iText Fill Form / Copy Page to new Document
我正在使用 iText
填写包含 AcroForm
的模板 PDF。
现在我想使用这个模板创建一个带有动态页面的新 PDF。
我的想法是填充模板 PDF,复制带有写入字段的页面并将其添加到新文件中。他们的主要问题是我们的客户想自己设计模板。所以我不确定我是否尝试了正确的方法来解决这个问题。
所以我创建了这段代码,但现在无法正常工作,我收到错误 com.itextpdf.io.IOException: PDF header not found.
我的代码
x = 1;
try (PdfDocument finalDoc = new PdfDocument(new PdfWriter("C:\Users\...Final.pdf"))) {
for (HashMap<String, String> map : testValues) {
String path1 = "C:\Users\.....Temp.pdf"
InputStream template = templateValues.get("Template");
PdfWriter writer = new PdfWriter(path1);
try (PdfDocument pdfDoc = new PdfDocument(new PdfReader(template), writer)) {
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
for (HashMap.Entry<String, String> map2 : map.entrySet()) {
if (form.getField(map2.getKey()) != null) {
Map<String, PdfFormField> fields = form.getFormFields();
fields.get(map2.getKey()).setValue(map2.getValue());
}
}
} catch (IOException | PdfException ex) {
System.err.println("Ex2: " + ex.getMessage());
}
if (x != 0 && (x % 5) == 0) {
try (PdfDocument tempDoc = new PdfDocument(new PdfReader(path1))) {
PdfPage page = tempDoc.getFirstPage();
finalDoc.addPage(page.copyTo(finalDoc));
} catch (IOException | PdfException ex) {
System.err.println("Ex3: " + ex.getMessage());
}
}
x++;
}
} catch (IOException | PdfException ex) {
System.err.println("Ex: " + ex.getMessage());
}
添加完内容后关闭 PdfDocuments。
第 1 部分 - PDF Header 丢失
这似乎是由于您试图 re-read 一个 InputStream w/in 一个已经被读取的循环(并且,根据 PdfReader 的配置,关闭)。解决这个问题取决于所使用的特定类型的 InputStream - 如果您想将其保留为简单的 InputStream(相对于更具体但功能更强大的 InputStream 类型),那么您需要首先将字节从流中提取出来内存(例如 ByteArrayOutputStream),然后根据这些字节创建您的 PDFReader。
即
ByteArrayOutputStream templateBuffer = new ByteArrayOutputStream();
while ((int c = template.read()) > 0) templateBuffer.write(c);
for (/* your loop */) {
...
PdfDocument filledInAcroFormTemplate = new PdfDocument(new PdfReader(new ByteArrayInputStream(templateBuffer.toByteArray())), new PdfWriter(tmp))
...
第 2 部分 - 其他问题
两件事
- 确保获取最近发布的 iText 7.0.1 版本,因为它包含一些修复/AcroForm 处理
- 您可以将 ByteArrayOutputStreams 用于临时 PDF(与将它们写入文件相比)——我将在下面的示例中使用这种方法
- PdfDocument/PdfPage 在 "kernel" 模块中,而 AcroForms 在 "form" 模块中(意味着 PdfPage 故意不知道 AcroForms)——IPdfPageExtraCopier 是模块之间的桥梁。为了正确复制 AcroForms,您需要使用 two-arg copyTo() 版本,传递 PdfPageFormCopier
的实例
字段名称 必须 在文档中是唯一的("absolute" 字段名称 - 我现在将跳过字段层次结构)。由于我们多次循环并添加模板中的字段,我们需要想出一个策略来重命名字段以确保唯一性(当前的 API 在这方面实际上有点笨拙)
File acroFormTemplate = new File("someTemplate.pdf");
Map<String, String> someMapOfFieldToValues = new HashMap<>();
try (
PdfDocument finalOutput = new PdfDocument(new PdfWriter(new FileOutputStream(new File("finalOutput.pdf")));
) {
for (/* some looping condition */int x = 0; x < 5; x++) {
// for each iteration of the loop, create a temporary in-memory
// PDF to handle form field edits.
ByteArrayOutputStream tmp = new ByteArrayOutputStream();
try (
PdfDocument filledInAcroFormTemplate = new PdfDocument(new PdfReader(new FileInputStream(acroFormTemplate)), new PdfWriter(tmp));
) {
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(filledInAcroFormTemplate, true);
for (PdfFormField field : acroForm.getFormFields().values()) {
if (someMapOfFieldToValues.containsKey(field.getFieldName())) {
field.setValue(someMapOfFieldToValues.get(field.getFieldName()));
}
}
// NOTE that because we're adding the template multiple times
// we need to adopt a field renaming strategy to ensure field
// uniqueness in the final document. For demonstration's sake
// we'll just rename them prefixed w/ our loop counter
List<String> fieldNames = new ArrayList<>();
fieldNames.addAll(acroForm.getFormFields().keySet()); // avoid ConfurrentModification
for (String fieldName : fieldNames) {
acroForm.renameField(fieldName, x+"_"+fieldName);
}
}
// the temp PDF needs to be "closed" for all the PDF finalization
// magic to happen...so open up new read-only version to act as
// the source for the merging from our in-memory bucket-o-bytes
try (
PdfDocument readOnlyFilledInAcroFormTemplate = new PdfDocument(new PdfReader(new ByteArrayInputStream(tmp.toByteArray())));
) {
// although PdfPage.copyTo will probably work for simple pages, PdfDocument.copyPagesTo
// is a more comprehensive copy (wider support for copying Outlines and Tagged content)
// so it's more suitable for general page-copy use. Also, since we're copying AcroForm
// content, we need to use the PdfPageFormCopier
readOnlyFilledInAcroFormTemplate.copyPagesTo(1, 1, finalOutput, new PdfPageFormCopier());
}
}
}
我正在使用 iText
填写包含 AcroForm
的模板 PDF。
现在我想使用这个模板创建一个带有动态页面的新 PDF。
我的想法是填充模板 PDF,复制带有写入字段的页面并将其添加到新文件中。他们的主要问题是我们的客户想自己设计模板。所以我不确定我是否尝试了正确的方法来解决这个问题。
所以我创建了这段代码,但现在无法正常工作,我收到错误 com.itextpdf.io.IOException: PDF header not found.
我的代码
x = 1;
try (PdfDocument finalDoc = new PdfDocument(new PdfWriter("C:\Users\...Final.pdf"))) {
for (HashMap<String, String> map : testValues) {
String path1 = "C:\Users\.....Temp.pdf"
InputStream template = templateValues.get("Template");
PdfWriter writer = new PdfWriter(path1);
try (PdfDocument pdfDoc = new PdfDocument(new PdfReader(template), writer)) {
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
for (HashMap.Entry<String, String> map2 : map.entrySet()) {
if (form.getField(map2.getKey()) != null) {
Map<String, PdfFormField> fields = form.getFormFields();
fields.get(map2.getKey()).setValue(map2.getValue());
}
}
} catch (IOException | PdfException ex) {
System.err.println("Ex2: " + ex.getMessage());
}
if (x != 0 && (x % 5) == 0) {
try (PdfDocument tempDoc = new PdfDocument(new PdfReader(path1))) {
PdfPage page = tempDoc.getFirstPage();
finalDoc.addPage(page.copyTo(finalDoc));
} catch (IOException | PdfException ex) {
System.err.println("Ex3: " + ex.getMessage());
}
}
x++;
}
} catch (IOException | PdfException ex) {
System.err.println("Ex: " + ex.getMessage());
}
添加完内容后关闭 PdfDocuments。
第 1 部分 - PDF Header 丢失
这似乎是由于您试图 re-read 一个 InputStream w/in 一个已经被读取的循环(并且,根据 PdfReader 的配置,关闭)。解决这个问题取决于所使用的特定类型的 InputStream - 如果您想将其保留为简单的 InputStream(相对于更具体但功能更强大的 InputStream 类型),那么您需要首先将字节从流中提取出来内存(例如 ByteArrayOutputStream),然后根据这些字节创建您的 PDFReader。
即
ByteArrayOutputStream templateBuffer = new ByteArrayOutputStream();
while ((int c = template.read()) > 0) templateBuffer.write(c);
for (/* your loop */) {
...
PdfDocument filledInAcroFormTemplate = new PdfDocument(new PdfReader(new ByteArrayInputStream(templateBuffer.toByteArray())), new PdfWriter(tmp))
...
第 2 部分 - 其他问题
两件事
- 确保获取最近发布的 iText 7.0.1 版本,因为它包含一些修复/AcroForm 处理
- 您可以将 ByteArrayOutputStreams 用于临时 PDF(与将它们写入文件相比)——我将在下面的示例中使用这种方法
- PdfDocument/PdfPage 在 "kernel" 模块中,而 AcroForms 在 "form" 模块中(意味着 PdfPage 故意不知道 AcroForms)——IPdfPageExtraCopier 是模块之间的桥梁。为了正确复制 AcroForms,您需要使用 two-arg copyTo() 版本,传递 PdfPageFormCopier 的实例
字段名称 必须 在文档中是唯一的("absolute" 字段名称 - 我现在将跳过字段层次结构)。由于我们多次循环并添加模板中的字段,我们需要想出一个策略来重命名字段以确保唯一性(当前的 API 在这方面实际上有点笨拙)
File acroFormTemplate = new File("someTemplate.pdf"); Map<String, String> someMapOfFieldToValues = new HashMap<>(); try ( PdfDocument finalOutput = new PdfDocument(new PdfWriter(new FileOutputStream(new File("finalOutput.pdf"))); ) { for (/* some looping condition */int x = 0; x < 5; x++) { // for each iteration of the loop, create a temporary in-memory // PDF to handle form field edits. ByteArrayOutputStream tmp = new ByteArrayOutputStream(); try ( PdfDocument filledInAcroFormTemplate = new PdfDocument(new PdfReader(new FileInputStream(acroFormTemplate)), new PdfWriter(tmp)); ) { PdfAcroForm acroForm = PdfAcroForm.getAcroForm(filledInAcroFormTemplate, true); for (PdfFormField field : acroForm.getFormFields().values()) { if (someMapOfFieldToValues.containsKey(field.getFieldName())) { field.setValue(someMapOfFieldToValues.get(field.getFieldName())); } } // NOTE that because we're adding the template multiple times // we need to adopt a field renaming strategy to ensure field // uniqueness in the final document. For demonstration's sake // we'll just rename them prefixed w/ our loop counter List<String> fieldNames = new ArrayList<>(); fieldNames.addAll(acroForm.getFormFields().keySet()); // avoid ConfurrentModification for (String fieldName : fieldNames) { acroForm.renameField(fieldName, x+"_"+fieldName); } } // the temp PDF needs to be "closed" for all the PDF finalization // magic to happen...so open up new read-only version to act as // the source for the merging from our in-memory bucket-o-bytes try ( PdfDocument readOnlyFilledInAcroFormTemplate = new PdfDocument(new PdfReader(new ByteArrayInputStream(tmp.toByteArray()))); ) { // although PdfPage.copyTo will probably work for simple pages, PdfDocument.copyPagesTo // is a more comprehensive copy (wider support for copying Outlines and Tagged content) // so it's more suitable for general page-copy use. Also, since we're copying AcroForm // content, we need to use the PdfPageFormCopier readOnlyFilledInAcroFormTemplate.copyPagesTo(1, 1, finalOutput, new PdfPageFormCopier()); } } }