Объединение XFA с PDFBox

Я хотел бы заполнить форму PDF с помощью java-библиотеки PDFBox. Форма PDF создается с помощью Adobe Live Designer, поэтому в ней используется формат XFA.

Я пытаюсь найти ресурсы о заполнении форм XFA PDF с помощью PDFBox, но пока мне не повезло. Я видел, что в API доступен метод PDAcroForm.setXFA, но я не понимаю, как его использовать.

Знаете ли вы, можно ли заполнить PDF-форму с помощью PDFBox? Если да, есть ли где-нибудь пример кода или учебник для этого? Если нет, то каковы наилучшие альтернативы для достижения этой цели?


person OutOfBound    schedule 10.05.2012    source источник


Ответы (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();
    }

затем методы, которые устанавливают значения для полей. Я установил 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));
        }
    }
}

Это работает, но не для всех «видов» жизненного цикла PDF, некоторые получили предупреждающее сообщение о «расширенной функции», которая больше не включена, но все еще работает. Оптимизированная версия - единственная, которую я нашел, которая не выводит сообщение при открытии после заполнения.

Заполняю XFA и Acroform иначе не работает во всех вьюверах.

person Remi Morin    schedule 28.11.2014

В вопросе конкретно указывается библиотека PDFBox в теме; вам не нужен iText, манипуляции с XFA можно выполнить с помощью объекта PDXFA, доступного в PDFBox 1.8.

Большое спасибо Маруану Сахьюну за его прекрасную работу над PDFBox + XFA.

Этот код работает, только если вы удалили всю защиту PDDocument.
Он также предполагает, что объект COS в PDXFA является 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);
person JammyDodger    schedule 16.01.2014
comment
Я использовал приведенный выше код и немного обеспокоен тем, что полученный файл довольно большой. Файл PDF начинается с 647kb pdf. Новый pdf 14000кб. Кто-нибудь знает, как можно уменьшить размер создаваемого нового файла. Можно ли установить какой-либо тип сжатия при обратной записи в файл PDF? - person chamalabey; 04.11.2014
comment
Код JammyDodger показывает только, как получить доступ к XML и как вернуть его обратно. Не показывайте, как находить поля, как устанавливать значения... Насколько я вижу, DocumentBuilder - это получение javax.xml.parsers.DocumentBuilder с javax.xml.parsers.DocumentBuilderFactory. неопределенная переменная xmlDoc является документом (мое предположение) - person Remi Morin; 27.11.2014
comment
@JammyDodger У меня похожая проблема. Я хочу прочитать содержимое файла XFA с помощью PDFBox. Я не могу понять, как это сделать. Не могли бы вы добавить код для этого или хотя бы объяснить приведенный выше код. - person Mayank; 03.06.2016

Я не знаком с pdfbox, но вы можете сделать это с iText (http://itextpdf.com/), как только вы получить доступ к XFA (XML) DOM.

person Tim Lowes    schedule 16.05.2012
comment
я наконец выбрал itext. - person OutOfBound; 18.10.2013

Попробуйте это, и он объединит все pdf без xfa и с XFA (это только при использовании PDBox)


PDAcroForm form = document.getDocumentCatalog().getAcroForm();
 if(form != null) {
      document.setAllSecurityToBeRemoved(true);
      form.flatten();
      if(form.hasXFA()) {
         form.setXFA(null);
      }
 }                       
 merge.appendDocument(anyPDFDoc, document);
person user3230501    schedule 08.09.2020

AcroForm предназначен для PDF со статическими полями. Если в PDF есть формы xfa, вы можете использовать itext (Java) или itextsharp (.net) для заполнения ваших данных. Единственная проблема с формами XFA заключается в том, что их нельзя сгладить с помощью itext. Единственный способ сгладить, который я нашел, - это использовать bullzip или аналогичный создатель pdf, чтобы открыть этот xfa pdf, созданный с помощью itext, и передать его через bullzip, который выплюнет версию pdf. Надеюсь, это даст вам некоторые идеи.

Ниже приведен примерный пример того, как заполняется xfa.

XfaForm xfa = pdfFormFields.Xfa;
dynamic bytes = Encoding.UTF8.GetBytes("<?xml version=\"1.0\" encoding=\"UTF-8\"?> <form1> <staticform>" + "\r\n<barcode>" + barcode + "</barcode></staticform> <flowForm><Extra>" + Extra + "</Extra></flowForm> </form1>");
MemoryStream ms = new MemoryStream(bytes);
pdfStamper.AcroFields.Xfa.FillXfaForm(ms);

теперь вы можете использовать созданный вами xfa pdf и распечатать через bullzip

const string Printer_Name = "Bullzip PDF Printer";

                    PdfSettings pdfSettings = new PdfSettings();
                    pdfSettings.PrinterName = Printer_Name;
                    pdfSettings.SetValue("Output", flatten_pdf);
                    pdfSettings.SetValue("ShowPDF", "no");
                    pdfSettings.SetValue("ShowSettings", "never");
                    pdfSettings.SetValue("ShowSaveAS", "never");
                    pdfSettings.SetValue("ShowProgress", "no");
                    pdfSettings.SetValue("ShowProgressFinished", "no");
                    pdfSettings.SetValue("ConfirmOverwrite", "no");
                    pdfSettings.WriteSettings(PdfSettingsFileType.RunOnce);
                    PdfUtil.PrintFile(xfa_pdffile, Printer_Name);

выходной файл будет сглаженным pdf..

person BipAdh    schedule 06.12.2012
comment
Ну, нам всегда нужен код. Так что, если вы можете предоставить может, предоставить его. - person j0k; 06.12.2012
comment
Этот вопрос помечен как Java. Bullzip недоступен для Java! - person filip-fku; 02.07.2013