java - iText 填写表格/复制页面到新文档

标签 java pdf itext7

我正在使用 iText 填充包含 AcroForm 的模板 PDF。 现在我想使用这个模板创建一个带有动态页面的新 PDF。 我的想法是填写模板 PDF,复制带有书面字段的页面并将其添加到新文件中。他们的主要问题是我们的客户想要自己设计模板。所以我不确定我是否尝试了正确的方法来解决这个问题。

所以我创建了这段代码,但现在不起作用,我收到错误com.itextpdf.io.IOException:未找到 PDF header 。

我的代码

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

最佳答案

第 1 部分 - PDF header 丢失

这似乎是由于您尝试在已读取的循环中重新读取 InputStream(并且根据 PdfReader 的配置关闭)而导致的。解决这个问题取决于所使用的输入流的具体类型 - 如果您想将其保留为简单的输入流(相对于更具体但功能更强大的输入流类型),那么您需要首先将流中的字节吸收到内存(例如 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 部分 - 其他问题

有几件事

  1. 请确保获取最近发布的 7.0.1 版本的 iText,因为它包含一些关于 rt/AcroForm 处理的修复
  2. 您可能可以对临时 PDF 使用 ByteArrayOutputStreams(而不是将它们写入文件) - 我将在下面的示例中使用这种方法
  3. PdfDocument/PdfPage 位于“内核”模块中,而 AcroForms 位于“表单”模块中(这意味着 PdfPage 故意不知道 AcroForms) - IPdfPageExtraCopier 是模块之间的桥梁。为了正确复制 AcroForms,您需要使用两个参数的 copyTo() 版本,传递 PdfPageFormCopier 的实例
  4. 字段名称​​必须在文档中是唯一的(“绝对”字段名称 - 我现在将跳过字段层次结构)。由于我们多次循环并添加模板中的字段,因此我们需要制定一种策略来重命名字段以确保唯一性(当前的 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());
            }
        }
    }
    

关于java - iText 填写表格/复制页面到新文档,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39638905/

相关文章:

javascript - 制作一个 anchor 标记,点击时触发打印操作

iTextsharp 7 许可证 key

java - 使用itext库在pdf文档中添加内联图像

java - 字节码注入(inject)在哪里可能有用?

java - 在 Spring Boot 应用程序上使用 Flyway 时如何在 H2 中加载初始数据?

java - 删除字符串中的一系列字符

java - 如何在 jooq 记录对象中设置当前数据库时间

php - 如何在php中显示pdf

pdf - PDF (XMP) 格式的每页信息规范

itext - 在 iText 7 中编写文档时如何获得垂直光标位置?