将XFA与PDFBox结合使用

Out*_*und 5 java forms pdf pdf-form pdfbox

我想用PDFBox java库填写PDF表单.PDF表单是使用Adobe Live Designer创建的,因此它使用XFA格式.

我试图找到有关使用PDFBox填充XFA PDF表单的资源,但到目前为止我没有任何运气.我看到API中有一个PDAcroForm.setXFA方法,但我看不到如何使用它.

您知道是否可以用PDFBox填写PDF表格?如果是,是否有任何代码示例或教程来实现这一目标?如果不是,实现这一目标的最佳替代方案是什么?

小智 5

该问题专门识别主题中的PDFBox库; 您不需要iText,可以使用PDFBox 1.8中提供的PDXFA对象完成XFA操作.

非常感谢Maruan Sahyoun在PDFBox + XFA上所做的出色工作.

此代码仅在删除PDDocument上的所有安全性时有效.
它还假设PDXFA中的COS对象是COSStream.下面简单的示例读取xml流并将其写回PDF.

 PDDocument doc = PDDocument.load("filename");
 doc.setAllSecurityToBeRemoved(true);

 PDDocumentCatalog docCatalog = doc.getDocumentCatalog();
 PDAcroForm form = docCatalog.getAcroForm();

 PDXFA xfa = form.getXFA();
 COSBase cos = xfa.getCOSObject();
 COSStream coss = (COSStream) cos;
 InputStream cosin = coss.getUnfilteredStream();
 Document document = documentBuilder.parse(cosin);

 COSStream cosout = new COSStream(new RandomAccessBuffer());
 OutputStream out = cosout.createUnfilteredStream();

 TransformerFactory tFactory = TransformerFactory.newInstance();
 Transformer transformer = tFactory.newTransformer();
 DOMSource source = new DOMSource(xmlDoc);
 StreamResult result = new StreamResult(out);
 transformer.transform(source, result);

 PDXFA xfaout = new PDXFA(cosout);
 form.setXFA(xfaout);
Run Code Online (Sandbox Code Playgroud)


Rem*_*rin 5

这是我在分配问题时所能做的最好的事情。我将pdf保存为(优化)(在生命周期中)(我不是做pdf的人)。这是PDF的打开部分,XML复制然后保存:

    PDDocument document = PDDocument.load(fileInputStream);
    fileInputStream.close();
    document.setAllSecurityToBeRemoved(true);

    Map<String, String> values = new HashMap<String, String>();
    values.put("variable_name", "value");


    setFields(document, values); // see code below

    PDAcroForm form = document.getDocumentCatalog().getAcroForm();
    Document documentXML = form.getXFA().getDocument();

    NodeList dataElements = documentXML.getElementsByTagName("xfa:data");
    if (dataElements != null) {
        for (int i = 0; i < dataElements.getLength(); i++) {
            setXFAFields(dataElements.item(i), values);
        }
    }

    COSStream cosout = new COSStream(new RandomAccessBuffer());

    TransformerFactory.newInstance().newTransformer()
            .transform(new DOMSource(documentXML), new StreamResult(cosout.createUnfilteredStream()));

    form.setXFA(new PDXFA(cosout));

    FileOutputStream fios = new FileOutputStream(new File(docOut + ".pdf"));
    document.save(fios);
    document.close();
    try {
        fios.flush();
    } finally {
        fios.close();
    }
Run Code Online (Sandbox Code Playgroud)

然后是为字段设置值的方法。我同时设置了XFA和AcroForm:

public void setXFAFields(Node pNode, Map<String, String> values) throws IOException {
    if (values.containsKey(pNode.getNodeName())) {
        pNode.setTextContent(values.get(pNode.getNodeName()));
    } else {
        NodeList childNodes = pNode.getChildNodes();
        if (childNodes != null) {
            for (int i = 0; i < childNodes.getLength(); i++) {
                setXFAFields(childNodes.item(i), values);
            }
        }
    }
}

public void setFields(PDDocument pdfDocument, Map<String, String> values) throws IOException {

    @SuppressWarnings("unchecked")
    List<PDField> fields = pdfDocument.getDocumentCatalog().getAcroForm().getFields();
    for (PDField pdField : fields) {
        setFields(pdField, values);
    }
}

private void setFields(PDField field, Map<String, String> values) throws IOException {
    List<COSObjectable> kids = field.getKids();
    if (kids != null) {
        for (COSObjectable pdfObj : kids) {
            if (pdfObj instanceof PDField) {
                setFields((PDField) pdfObj, values);
            }
        }
    } else {
        // remove the [0] from the name to match values in our map
        String partialName = field.getPartialName().replaceAll("\\[\\d\\]", "");
        if (!(field instanceof PDSignatureField) && values.containsKey(partialName)) {
            field.setValue(values.get(partialName));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这项工作,但并非针对所有PDF生命周期的“种类”产生,有些人收到了有关“扩展功能”的警告消息,该消息已不再启用,但仍然有效。优化版本是我发现的唯一一个在填充后打开时不提示消息的版本。

我填写了XFA和Acroform,否则它不能在所有查看器中正常工作。


小智 1

我不熟悉 pdfbox,但一旦您可以访问 XFA (XML) DOM,您就可以使用 iText ( http://itextpdf.com/ ) 执行此操作。