Available Now: Explore our latest release with enhanced accessibility and powerful IDP features
By Apryse | 2019 Jan 10
4 min
Tags
tutorial
sdk
pdf/a
conversion
The ZUGFeRD standard makes the sharing of payment information between entities large and small a lot simpler. However, building a program to automatically generate ZUGFeRD-compliant invoices is a little more involved. This post shows you how you can create valid ZUGFeRD files via Windows, Mac, or Linux apps by leveraging Apryse’s full-featured SDK.
Learn more about document solutions for the finance sector.
Start by downloading a trial of Apryse's SDK, which includes the features you will need, such as adding file attachments to a PDF, converting to PDF/A format, and PDF/A verification; as well as offerings for digital signature creation, redaction and much more.
ZUGFeRD invoices bring together two parts:
When generating the human-readable invoice, you may wish to work in a higher level format like DOCX, XAML, or HTML before converting to PDF, which makes direct editing and structuring of the file more involved.
Apryse's SDK won’t restrict you to working in PDF since it is able to convert 20+ file formats via pdftron.PDF.Convert.ToPDF
, with samples available in C#, C++, Java, Obj-C, PHP, Python, Ruby, and VB.
The first step after creating your XML file is to embed the XML within the PDF invoice on a “one-to-one” basis. That means each ZUGFeRD invoice should consist of a single XML representation paired with a matching PDF representation. You can later add as many non-invoice file attachments as you wish.
This post provides sample code in C#—with the full sample available for download here. Note that this sample assumes you have a file named ZUGFeRD-invoice.xml
in the input_path TestFiles
. (It will not generate an XML for you.)
Below is the section from the sample that reads the PDF document and an XML invoice from your chosen inputs, embeds the XML invoice into the PDF as an attachment, and saves the result as a new, in-memory PDF file (stored in the byte array). Importantly, when embedding the XML, the code attaches the XML to the PDF as a whole, rather than to a section of the document.
byte[] doc_bytes_invoice_added = null;
using (PDFDoc in_doc = new PDFDoc(input_path + "fish.pdf"))
{
in_doc.InitSecurityHandler();
NameTree files = NameTree.Create(in_doc, "EmbeddedFiles");
FileSpec fs = FileSpec.Create(in_doc, input_path + "ZUGFeRD-invoice.xml", true);
byte[] file1_name = System.Text.Encoding.UTF8.GetBytes("ZUGFeRD-invoice.xml");
files.Put(file1_name, fs.GetSDFObj());
fs.GetSDFObj().PutText("Desc", "ZUGFeRD Rechnung");
fs.GetSDFObj().PutName("AFRelationship", "Alternative");
fs.GetSDFObj().PutText("F", "ZUGFeRD-invoice.xml");
fs.GetSDFObj().PutText("UF", "ZUGFeRD-invoice.xml");
Obj collection = in_doc.GetRoot().FindObj("Collection");
if (collection == null) collection = in_doc.GetRoot().PutDict("Collection");
collection.PutName("View", "T");
doc_bytes_invoice_added = in_doc.Save(SDFDoc.SaveOptions.e_incremental);
}
Next, convert to PDF/A-3 to ensure there are no objects (e.g., non-embedded fonts) in your invoice that could prevent a PDF reader from correctly displaying the content. The PDF/A-3 standard is mandatory for ZUGFeRD invoices, as only PDF/A-3 permits you to embed non-PDF/A file types.
For the following sample, we’ve also chosen to convert to PDF/A conformance level b. This is generally a good path to follow; selecting level b will make it easier for invoices to pass auto-conversion. You can always change the syntax to convert to another conformance level (i.e., level a or level u) if you are restricted by a policy or want to enhance your invoices’ accessibility features, such as ensuring text that can be reliably searched and copied. Just know that this may entail doing extra scripting to mark PDF content for logical structure and read order, and to add character mappings to Unicode if your PDFs do not contain this information already.
byte[] doc_bytes_pdfa = null;
using (PDFACompliance pdf_a = new PDFACompliance(true, doc_bytes_invoice_added, null, PDFACompliance.Conformance.e_Level3B, null, 10, false))
{
doc_bytes_pdfa = pdf_a.SaveAs(false);
}
Finally, add XMP metadata to describe the embedded invoice as a ZUGFeRD invoice by executing the following code. Afterwards, it will save to disk a ZUGFeRD-compliant PDF.
using (PDFDoc in_doc = new PDFDoc(doc_bytes_pdfa, doc_bytes_pdfa.Length))
{
in_doc.InitSecurityHandler();
Obj xmp_stm = in_doc.GetRoot().FindObj("Metadata");
Filter stm = xmp_stm.GetDecodedStream();
FilterReader reader = new FilterReader(stm);
byte[] buf = new byte[stm.Size()];
int nb = reader.Read(buf);
String xmp_str = System.Text.ASCIIEncoding.ASCII.GetString(buf);
String zugferd_xmp = @"<rdf:Description xmlns:zf=""urn:ferd:pdfa:CrossIndustryDocument:invoice:1p0#"" rdf:about="""">
<zf:ConformanceLevel>BASIC</zf:ConformanceLevel>
<zf:DocumentFileName>ZUGFeRD-invoice.xml</zf:DocumentFileName>
<zf:DocumentType>INVOICE</zf:DocumentType>
<zf:Version>1.0</zf:Version>
</rdf:Description>
<rdf:Description xmlns:pdfaExtension=""http://www.aiim.org/pdfa/ns/extension/"" xmlns:pdfaSchema=""http://www.aiim.org/pdfa/ns/schema#"" xmlns:pdfaProperty=""http://www.aiim.org/pdfa/ns/property#"" rdf:about="""">
<pdfaExtension:schemas>
<rdf:Bag>
<rdf:li rdf:parseType=""Resource"">
<pdfaSchema:schema>ZUGFeRD PDFA Extension Schema</pdfaSchema:schema>
<pdfaSchema:namespaceURI>urn:ferd:pdfa:CrossIndustryDocument:invoice:1p0#</pdfaSchema:namespaceURI>
<pdfaSchema:prefix>zf</pdfaSchema:prefix>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType=""Resource"">
<pdfaProperty:name>DocumentFileName</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>name of the embedded XML invoice file</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType=""Resource"">
<pdfaProperty:name>DocumentType</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>INVOICE</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType=""Resource"">
<pdfaProperty:name>Version</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>The actual version of the ZUGFeRD XML schema</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType=""Resource"">
<pdfaProperty:name>ConformanceLevel</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>The conformance level of the embedded ZUGFeRD data</pdfaProperty:description>
</rdf:li>
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>
</rdf:Bag>
</pdfaExtension:schemas>
</rdf:Description>";
xmp_str = xmp_str.Insert(xmp_str.IndexOf("<rdf:Description "), zugferd_xmp);
xmp_stm.SetStreamData(System.Text.Encoding.ASCII.GetBytes(xmp_str));
in_doc.Save(output_path + "zugferd_invoice.pdf", SDFDoc.SaveOptions.e_linearized);
}
That’s it! You should now have a working prototype able to automatically generate ZUGFeRD-valid invoices using Apryse's SDK.
Later, you may want to add other invoicing capabilities to your solution: check out Apryse’s full-featured SDK library with a free trial.
If you have any questions about Apryse’s PDF SDK, please feel free to get in touch!
Tags
tutorial
sdk
pdf/a
conversion
Apryse
Related Products
Share this post
PRODUCTS
Enterprise
Small Business
Popular Content