Reputation: 75
My Pdf contains an XFA form with an empty field. I tried filling it by manipulating the XML then applying SetXfa() with ITextSharp, but the PDF does not change. Here is my method :
static void EditFieldXFA(string path, string key, string value)
{
iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(path);
using (MemoryStream ms = new MemoryStream())
{
PdfStamper stamper = new PdfStamper(reader, ms);
AcroFields form = stamper.AcroFields;
XfaForm xfa = form.Xfa;
var a = xfa.DatasetsNode.InnerXml;
if (a.Contains(key))
{
// The empty XML looks like <key />, and a filled XML looks like <key>value</key>, so we edit the field
string keyClose = key.Replace(" /", "").Replace("<", "</");
var newXml = a.Replace(key, key.Replace(" /", "") + value + keyClose);
XmlDocument doc = new XmlDocument();
doc.LoadXml(newXml);
// xfa.FillXfaForm(XmlReader.Create(new StringReader(newXml)));
xfa.DomDocument = doc;
xfa.Changed = true;
XfaForm.SetXfa(xfa, stamper.Reader, stamper.Writer);
stamper.Close();
reader.Close();
return;
}
else
{
Console.Write("Error : No " + key + " field detected. Please check that it is empty. Press enter to exit the process.");
Console.Read();
Environment.Exit(2);
}
}
}
The XML is correctly modified, I checked. Is this the correct way ? Do you have any idea what to try next ? Thank you for your help !
XML file : https://drive.google.com/file/d/1EbZJM6TjZ5tXQ0Dxa6IeUDz3QgJuxsBi/view?usp=sharing Please let me know if there's any issue with the download, and open it in Acrobat to see the content.
Upvotes: 0
Views: 1216
Reputation: 95963
The stream your PdfStamper
writes to is the MemoryStream ms
PdfStamper stamper = new PdfStamper(reader, ms);
and you completely ignore its contents after closing the stamper, i.e. finishing the stamping
stamper.Close();
reader.Close();
return;
Thus, your changes are lost.
Instead you can return the manipulated PDF as byte array to the caller, simply declare the method return type as byte[]
static byte[] EditFieldXFA(string path, string key, string value)
and return the memory stream contents
stamper.Close();
reader.Close();
return ms.ToArray();
Whenever you want to manipulate a XML file, don't do it manually, use a XML API, e.g. DOM; every once in a while a situation pops up in which building XML manually appears simpler but usually that appearance is deceptive, you eventually get into trouble for doing so.
In particular in the case at hand you already have a XmlNode
: xfa.DatasetsNode
. You can simply set all its children with a given name like this:
static byte[] EditFieldXFAImproved(string path, string xpath, string value)
{
iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(path);
using (MemoryStream ms = new MemoryStream())
{
PdfStamper stamper = new PdfStamper(reader, ms);
AcroFields form = stamper.AcroFields;
XfaForm xfa = form.Xfa;
XmlNode a = xfa.DatasetsNode;
XmlNodeList hits = a.SelectNodes(xpath);
foreach(XmlNode hit in hits)
{
if (hit.NodeType == XmlNodeType.Element)
{
hit.InnerText = value;
}
}
xfa.Changed = true;
stamper.Close();
reader.Close();
return ms.ToArray();
}
}
For example
byte[] result = EditFieldXFAImproved(@"53116504000001 (3).pdf", "//Grund", "Gruuuuuuund");
File.WriteAllBytes(@"53116504000001 (3)-filled.pdf", result);
sets the "Ablehnungsgrund/Hinweis" field value to "Gruuuuuuund".
Beware, the second parameter has been renamed from key
to xpath
because now it is not expected to contain an empty XML element "<key />"
anymore but instead an xpath "//key"
.
Was ist denn das, was da plötzlich so schnell auf mich zukommt? So sehr, sehr schnell. So riesig und so flach und so rund. Das braucht einen riesigen Namen … wie …. Grund!
Upvotes: 1