Сочетание XFA с PDFBox
Я хотел бы заполнить форму PDF с java-библиотекой PDFBox. Форма PDF создается с помощью Adobe Live Designer, поэтому она использует формат XFA.
Я пытаюсь найти ресурсы по заполнению форм XFA PDF с помощью PDFBox, но пока мне не повезло. Я видел, что в API доступен метод PDAcroForm.setXFA, но я не вижу, как его использовать.
Знаете ли вы, можно ли заполнить форму PDF с PDFBox? Если да, есть ли где-нибудь пример кода или учебник для достижения этого? Если нет, каковы лучшие альтернативы для достижения этой цели?
4 ответа
Это лучшее, что мне удалось сделать за то время, которое я уделил этой проблеме. Я сохраняю 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, иначе это не работает во всех средствах просмотра.
Вопрос конкретно определяет библиотеку 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);
Я не знаком с pdfbox, но вы можете сделать это с iText ( http://itextpdf.com/), как только вы получите доступ к XFA (XML) DOM.
Попробуйте это, и он объединит все 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);
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..