Combining XFA with PDFBox

后端 未结 5 1639
面向向阳花
面向向阳花 2020-12-14 14:09

I would like to fill a PDF form with the PDFBox java library. The PDF form is created with Adobe Live Designer, so it uses the XFA format.

I try to find resources ab

5条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-14 14:18

    This it the best I was able to manage in the time I was allocated on the problem. I get the pdf saved (in Life Cycle) as optimized (I'm not the one doing the pdf). This is the PDF openning part, XML duplication and then saving:

        PDDocument document = PDDocument.load(fileInputStream);
        fileInputStream.close();
        document.setAllSecurityToBeRemoved(true);
    
        Map values = new HashMap();
        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();
        }
    

    then the methods who set values for fields. I set both the XFA and the AcroForm:

    public void setXFAFields(Node pNode, Map 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 values) throws IOException {
    
        @SuppressWarnings("unchecked")
        List fields = pdfDocument.getDocumentCatalog().getAcroForm().getFields();
        for (PDField pdField : fields) {
            setFields(pdField, values);
        }
    }
    
    private void setFields(PDField field, Map values) throws IOException {
        List 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));
            }
        }
    }
    

    This work, but not for all "kind" of PDF life Cycle produce, some got a warning message about "extended fonction" not enabled anymore but still work. The optimize version is the only one I found who don't prompt message when openned after being filled.

    I fill the XFA and the Acroform otherwise it don't work in all viewer.

提交回复
热议问题