简体   繁体   中英

Editing a XFA PDF with iText (Editing only a field within a node)

Ok so I'm having to programmatically fill out an XFA PDF using C#. I've been able to successfully extract the XML structure of the PDF. However, I'm running into issues using the AcroFields.Xfa.FillXfaForm(sourceXML) calls.

Essentially what is happening is this: I am taking the ENTIRE XML tree, editing the fields within the XML and then attempting to edit the form fields with the new XML. I end up with a PDF stripped of all AcroForm fields, without the new input added. HOWEVER when I parse this edited PDF and extract the XML tree I see that my edits have been preserved.

The security settings for this particular XFA PDF allow form fields to be edited however I am being forced to use PdfReader.unethicalreading = true; with my current set up (which is why I believe the form fields are being stripped out). I believe that the XFA PDF is taking my XML edits as a full on edit to the format of the document itself.

Here is my code so far:

namespace ConsoleApplication2 { class Program {

    static void Main(string[] args)
    {
        System.IO.StreamWriter file = new System.IO.StreamWriter(@"E:\XMLOutPut\outPutTest.xml");
        file.WriteLine(ReadFileNames());
        file.Close();

        using (FileStream existingPdf = new FileStream(@"E:\ORIGINAL.pdf", FileMode.Open))
        {
            using (PdfReader pdfReader = new PdfReader(existingPdf))
            {
                using (FileStream sourceXML = new FileStream(@"E:\XMLOutPut\outPutTest.xml", FileMode.Open))
                {
                    using (FileStream targetPdf = new FileStream(@"E:\ORIGINAL.pdf", FileMode.Open))
                    {
                        PdfReader.unethicalreading = true;
                        PdfStamper stamper = new PdfStamper(pdfReader, targetPdf,'\0', true);
                        stamper.AcroFields.Xfa.FillXfaForm(sourceXML);
                        stamper.Close();
                    }
                }
            }
        }
    }

    public static string ReadFileNames()
    {

        string SRC = @"E:\ORIGINAL.pdf";
        using (PdfReader reader = new PdfReader(SRC))
        {
            return ReadXFA(reader);
        }
    }

    public static string ReadXFA(PdfReader reader)
    {
        XfaForm xfa = new XfaForm(reader);
        XmlDocument document = xfa.DomDocument;
        reader.Close();

        if (!string.IsNullOrEmpty(document.DocumentElement.NamespaceURI))
        {
            document.DocumentElement.SetAttribute("xmlns", "");
            XmlDocument newDoc = new XmlDocument();
            newDoc.LoadXml(document.OuterXml);
            document = newDoc;
        }

        var sb = new StringBuilder(4000);
        var Xsettings = new XmlWriterSettings() { Indent = true };
        using (var wrtier = XmlWriter.Create(sb, Xsettings))
        {
            document.WriteTo(wrtier);
        }
        return sb.ToString();
    }
}

}

I am starting to believe that I have to somehow iterate through the XML and pull out however many fields I'd like to edit and do it that way??

Any help would be greatly appreciated.

Kind regards.

This is currently not possible using iText. You'll need to extract the XFA from the file (you can use iText to do this) and then traverse the XFA structure to make the edits, which you'll have to do with another tool, and then re-insert the XFA into the PDF, which can be done using iText.

    static void Main(string[] args)
    {
        using (FileStream existingPdf = new FileStream(SRC, FileMode.Open))
        using (PdfReader pdfReader = new PdfReader(existingPdf))
        using (FileStream targetPdf = new FileStream(Target, FileMode.Create))
        {
            PdfReader.unethicalreading = true;
            using (PdfStamper stamper = new PdfStamper(pdfReader, targetPdf, '\0', true))
            {
                XfaForm form = new XfaForm(pdfReader);
                XDocument xdoc = form.DomDocument.ToXDocument();
                var nodeElements = from nodeElement in xdoc.Descendants("form1").Descendants("A1")
                                   select nodeElement;
                foreach (XElement singleNodeElement in nodeElements)
                {
                    if (singleNodeElement.Name == "A1")
                    {
                        singleNodeElement.Value = "LOLGG";
                    }
                }
                XmlDocument xmlDoc = xdoc.ToXmlDocument();
                XmlNamespaceManager namespaces = new XmlNamespaceManager(xmlDoc.NameTable);
                namespaces.AddNamespace("xfa", "http://www.xfa.org/schema/xfa-data/1.0/");
                XmlNode baseNode = xmlDoc.SelectSingleNode("//xfa:datasets", namespaces);
                stamper.AcroFields.Xfa.FillXfaForm(baseNode);
            }
        }

    }
}
public static class DocumentExtensions
{
    public static XmlDocument ToXmlDocument(this XDocument xDocument)
    {
        var xmlDocument = new XmlDocument();
        using (var xmlReader = xDocument.CreateReader())
        {
            xmlDocument.Load(xmlReader);
        }
        return xmlDocument;
    }

    public static XDocument ToXDocument(this XmlDocument xmlDocument)
    {
        using (var nodeReader = new XmlNodeReader(xmlDocument))
        {
            nodeReader.MoveToContent();
            return XDocument.Load(nodeReader);
        }
    }
}

Alrighty folks so it is possible to do this with iText in addition to Linq and Xml.Linq, as the code example shows above.

In order to make this possible we had to take an XMLDocument and convert it to an XDocument and then use Linq to traverse the nodes. Once we were able to get the correct nodes we had to add a namespace to correctly identify the prefix. We then had to transfer the XDoc format back to an XMLDoc format in order to use the FillXfaForm from iText.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM