Как использовать itext для заполнения (динамического XFA) PDF из данных в текстовом файле

У меня есть локальная форма PDF с определенным шаблоном, который никогда не меняется. Я идентифицировал форму как динамическую форму XFA (xml), так как наборы ключей не были возвращены. Я пытаюсь использовать itext для заполнения формы данными, содержащимися в файле .txt. Насколько я понимаю, мне нужно каким-то образом получить данные из текстового файла и правильно поместить их в файл .xml. файл, чтобы itext мог манипулировать исходным PDF-файлом, используя данный xml.

В качестве примера форма имеет следующий вид:

Пример

Пример кода, который я использую в Eclipse, успешно компилируется/запускается, но ему требуются данные в файле data.xml, чтобы заполнить пустую форму данными поля и вывести заполненную версию. Дело в том, что для моего реального проекта у меня нет файла data.xml для правильного заполнения формы. Необработанные данные поля находятся в файле .txt, где каждая строка содержит данные для разных полей в PDF.

ПРИМЕР: Ссылаясь на изображение выше, мой файл .txt выглядит следующим образом для полей до и включая поле с пометкой «ЧЕТЫРЕ»:

  • Джон
  • 15
  • Чернить
  • Хонда
  • Тойота
  • Форд
  • БМВ

Меня смущают две вещи:

1. Как извлечь XML-структуру исходного PDF-файла, чтобы знать формат, которого следует придерживаться при заполнении данных из файла .txt?

2. Как мне получить значения из текстового файла и правильно вставить их в структуру .xml?

Следующий код работает, но требует data.xml для заполнения «incomplete.pdf». Он использует код xfa.fillXfaForm(new FileInputStream(XML)); для ввода данных, но я застрял на том, как определить структуру для «XML» и как ее заполнить в первую очередь.

Любая помощь приветствуется, большое спасибо.

Код:

package sandbox;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;


import java.io.PrintStream;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.XfaForm;


public class FillXFA {

    public static final String SRC = "C:/Workspace/PDF/incomplete.pdf";
    public static final String XML = "C:/Workspace/PDF/data.xml";
    public static final String DEST = "C:/Workspace/PDF/completed.pdf";

    public static void main(String[] args) throws IOException, DocumentException {
        File file = new File(DEST);
        file.getParentFile().mkdirs();
        new FillXFA().manipulatePdf(SRC, DEST);
    }

    public void readXfa(String src, String dest)
            throws IOException, ParserConfigurationException, SAXException,
                TransformerFactoryConfigurationError, TransformerException {
            FileOutputStream os = new FileOutputStream(dest);
            PdfReader reader = new PdfReader(src);
            XfaForm xfa = new XfaForm(reader);
            Document doc = xfa.getDomDocument();
            Transformer tf = TransformerFactory.newInstance().newTransformer();
            tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            tf.setOutputProperty(OutputKeys.INDENT, "yes");
            tf.transform(new DOMSource(doc), new StreamResult(os));
            reader.close();
        }

    public void manipulatePdf(String src, String dest)
        throws IOException, DocumentException {
        PdfReader reader = new PdfReader(src);
        PdfStamper stamper = new PdfStamper(reader,
                new FileOutputStream(dest));
        AcroFields form = stamper.getAcroFields();
        XfaForm xfa = form.getXfa();
        xfa.fillXfaForm(new FileInputStream(XML));
        stamper.close();
        reader.close();
    }
}

person Mathomatic    schedule 12.10.2015    source источник
comment
Напишите код, который берет ваш текстовый файл и превращает его в файл XML, например data.xml.   -  person Kevin Brown    schedule 13.10.2015
comment
Я несколько сомневаюсь в этом подходе, потому что я ранее манипулировал файлом .xml... и он нормально импортировался в мой PDF, но когда некая компания попыталась загрузить его в свою систему, это не сработало. Это заставило меня поверить, что я каким-то образом нарушил структуру XML, возможно, с неуместным символом или чем-то еще. Я надеюсь, что itext позволит мне полностью придерживаться исходной структуры xml, сохраняя при этом правильное заполнение полей данных в PDF. Любые отзывы или источники о том, как продолжить, просто фантастические, спасибо.   -  person Mathomatic    schedule 13.10.2015
comment
Как видно из команды, она принимает один аргумент. XML. Вы должны убедиться, что это допустимый XML   -  person Kevin Brown    schedule 13.10.2015
comment
Спасибо, Кевин. Тогда мой вопрос: как мне убедиться, что это действительный XML? Как говорилось в моем предыдущем комментарии, я вручную манипулировал файлом XML и успешно импортировал его в форму PDF. Тем не менее, когда эта конкретная компания загрузила его в свою систему, он был признан сломанным. Есть ли способ проверить, не повреждена ли она или неправильная структура xml, несмотря на то, что она успешно импортирована вручную в PDF? Возможно, с помощью строки кода или онлайн-сервиса? Спасибо   -  person Mathomatic    schedule 13.10.2015


Ответы (1)


В XFA связь между полями формы и данными формы осуществляется с помощью концепции, называемой связывание данных. Поля могут иметь XPath-подобное выражение для выбора их значения из структуры данных XML. Это означает, что XML-данные должны быть соответствующим образом структурированы для работы с конкретной формой XFA, но эта структура не обязательно уникальна.

Простой пример: предположим, у вас есть форма XFA с одним текстовым полем. Это текстовое поле имеет привязку данных к любому элементу XML с именем тега «Имя». В этом случае ваш data.xml может быть просто:

<Name>Hurmle</Name>

Но это и бесконечное количество различных структур XML также будут работать:

<StackOverflow>
    <accounts>
        <account>
            <Name>Hurmle</Name>
        </account>
    </accounts>
</StackOverflow>

Метод readXfa в вашем примере кода будет работать для извлечения полного потока XML из формы XFA. Он состоит из разных частей. Наиболее актуальными являются:

  • шаблон: описывает логическую структуру формы, включая все поля и их привязку данных.
  • xfa:datasets: Holds information about the data. Consists of 2 parts.
    • dataDescription: A schema for the form data, optional. The data description grammar is defined in the XFA specification.
    • xfa:data: данные формы.

Один из способов определить, какая XML-структура будет работать, — посмотреть на привязку данных всех полей (см. шаблон). Таким образом, вы будете знать, откуда поля ожидают получить свои данные. Для нетривиальной формы это может быть сложно и/или много работы.

Если доступно в форме XFA, вы можете использовать описание данных. Это даст вам структуру данных и информацию, такую ​​​​как минимальное и максимальное появление элементов.

Наконец, вы можете просмотреть данные, которые уже находятся в форме (см. xfa:data). Имейте в виду, что эта XML-структура не обязательно является полной: пустые элементы могут быть опущены. Например, если форма имеет 2 поля, значения могут быть указаны как:

<SomeRoot>
    <Field1>Value1</Field1>
    <Field2></Field2>
</SomeRoot>

Но также:

<SomeRoot>
    <Field1>Value1</Field1>
</SomeRoot>

В первом случае вам будет проще разобраться в нужной структуре. Если xfa:data отсутствует или неполный, вы можете попробовать заполнить все поля формы вручную с помощью средства просмотра PDF с поддержкой XFA. При сохранении средство просмотра заполнит xfa:data в соответствии с описанием данных и привязкой данных.

Для справки: спецификация XFA

person rhens    schedule 13.10.2015
comment
Спасибо, rens, за подробный отзыв. Похоже, что стратегия, которую я могу использовать, заключается в том, чтобы полностью вручную заполнить форму XFA. Затем я могу экспортировать его в .xml и прочитать в текстовом редакторе, чтобы понять описание/привязку данных. Я не понимаю, как получить значения из моего .txt-файла в .xml-файл, чтобы не нарушать предопределенную структуру данных. Это произошло недавно, когда компания, связанная с моей работой, не смогла загрузить pdf, потому что я вручную изменил xml и, возможно, допустил небольшую синтаксическую ошибку. Я надеюсь, что itext позволит мне полностью придерживаться предопределенной структуры данных xml? - person Mathomatic; 13.10.2015
comment
Проверка правильности XML не связана с XFA или iText. Вы можете использовать стандартные инструменты XML для управления структурой XML. Взгляните на JAXP для обработки Java XML. DOM, вероятно, проще всего понять. replaceChild() и setNodeValue() можно использовать для добавления контента в документ XML. - person rhens; 13.10.2015