dns_nx
dns_nx

Reputation: 3933

iText - PDF/A - Set XMP Metadata for ZUGFeRD invoice

I've got an issue with handling XMP Metadata when generating a ZUGFeRD invoice in PDF/A 3b. First I have to say, I have never been in touch with XMP Metadata before, so this is really a very new topic for me. I'm using version 8.0.5 of iText package, .NET Framework 4.7.2 with C#.

This is my current code to set the XMP Metadata for the PDF/A document, but this code does actually not work, because I get all the time errors, that

iText.Kernel.XMP.XMPException: 'Schema namespace URI and prefix mismatch'

For example on this line of code:

xmpMeta.SetStructField(extensionNamespace, schemaArrayItemPath, schemaNamespace, "schema", "Factur-X PDF/A Extension Schema");

I already have a working example, where the code is not running into exceptions, but when I validate the PDF/A document with veraPDF, it tells me a lot of errors, that the PDF/A is not compliant. Is there no example available to set valid XMP Metadata for ZUGFeRD invoices?

This is my current code:

byte[] xmpBytes = pdfaDoc.GetXmpMetadata();
XMPMeta xmpMeta;
if (xmpBytes != null && xmpBytes.Length > 0)
{
    xmpMeta = XMPMetaFactory.ParseFromBuffer(xmpBytes);
}
else
{
    xmpMeta = XMPMetaFactory.Create();
}
XMPMetaFactory.GetSchemaRegistry().RegisterNamespace("http://www.aiim.org/pdfa/ns/extension/", "pdfaExtension");
XMPMetaFactory.GetSchemaRegistry().RegisterNamespace("http://www.aiim.org/pdfa/ns/schema#", "pdfaSchema");
XMPMetaFactory.GetSchemaRegistry().RegisterNamespace("http://www.aiim.org/pdfa/ns/property#", "pdfaProperty");
XMPMetaFactory.GetSchemaRegistry().RegisterNamespace("urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#", "fx");

PropertyOptions arrayOptions = new PropertyOptions(PropertyOptions.ARRAY | PropertyOptions.ARRAY_ORDERED);
PropertyOptions structOptions = new PropertyOptions(PropertyOptions.STRUCT);

string extensionNamespace = "http://www.aiim.org/pdfa/ns/extension/";
string schemaNamespace = "http://www.aiim.org/pdfa/ns/schema#";
string propertyNamespace = "http://www.aiim.org/pdfa/ns/property#";
string zugferdNamespace = "urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#";

xmpMeta.AppendArrayItem(extensionNamespace, "schemas", arrayOptions, null, null);

string schemaArrayItemPath = extensionNamespace + "schemas[1]";

xmpMeta.SetStructField(extensionNamespace, schemaArrayItemPath, schemaNamespace, "schema", "Factur-X PDF/A Extension Schema");
xmpMeta.SetStructField(extensionNamespace, schemaArrayItemPath, schemaNamespace, "namespaceURI", zugferdNamespace);
xmpMeta.SetStructField(extensionNamespace, schemaArrayItemPath, schemaNamespace, "prefix", "fx");

xmpMeta.SetStructField(extensionNamespace, schemaArrayItemPath, schemaNamespace, "property", null, arrayOptions);

string propertyArrayItemPath = schemaArrayItemPath + "/property[1]";

xmpMeta.AppendArrayItem(propertyNamespace, schemaArrayItemPath + "/property", structOptions, null, null);
xmpMeta.SetStructField(propertyNamespace, propertyArrayItemPath, propertyNamespace, "name", "ConformanceLevel");
xmpMeta.SetStructField(propertyNamespace, propertyArrayItemPath, propertyNamespace, "valueType", "Text");
xmpMeta.SetStructField(propertyNamespace, propertyArrayItemPath, propertyNamespace, "category", "external");
xmpMeta.SetStructField(propertyNamespace, propertyArrayItemPath, propertyNamespace, "description", "Conformance level of the invoice.");

xmpMeta.AppendArrayItem(propertyNamespace, schemaArrayItemPath + "/property", structOptions, null, null);
string secondPropertyPath = schemaArrayItemPath + "/property[2]";
xmpMeta.SetStructField(propertyNamespace, secondPropertyPath, propertyNamespace, "name", "DocumentType");
xmpMeta.SetStructField(propertyNamespace, secondPropertyPath, propertyNamespace, "valueType", "Text");
xmpMeta.SetStructField(propertyNamespace, secondPropertyPath, propertyNamespace, "category", "external");
xmpMeta.SetStructField(propertyNamespace, secondPropertyPath, propertyNamespace, "description", "Type of the document.");

Upvotes: 1

Views: 323

Answers (2)

dns_nx
dns_nx

Reputation: 3933

I solved it now by simply adding the XMP metadata in XML format with this code:

string xmlMetadata = @"<?xml version='1.0'?>
                        <rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
                            <rdf:Description 
                                rdf:about=''
                                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#'
                            >
                                <pdfaExtension:schemas>
                                    <rdf:Bag>
                                        <rdf:li rdf:parseType='Resource'>
                                            <pdfaSchema:schema>ZUGFeRD PDFA Extension Schema</pdfaSchema:schema>
                                            <pdfaSchema:namespaceURI>urn:ferd:pdfa:invoice:rc#</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 data</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 ZUGFeRD data</pdfaProperty:description>
                                                    </rdf:li>
                                                </rdf:Seq>
                                            </pdfaSchema:property>
                                        </rdf:li>
                                    </rdf:Bag>
                                </pdfaExtension:schemas>
                            </rdf:Description>
                        </rdf:RDF>";


XMPMeta xmpMeta = XMPMetaFactory.ParseFromString(xmlMetadata);

pdfaDoc.SetXmpMetadata(xmpMeta);

Upvotes: 0

LionelRichTea
LionelRichTea

Reputation: 66

Disclaimer: I am an Apryse employee. We have recently published an article on creating ZUGFeRD invoices with iText, which may be helpful to you. Included is a link to the C# version of the Java code used in the article. This example targets the BASIC profile, but can easily be adapted to other profiles in the current (2.3) ZUGFeRD specification.

Regarding your code, the XMP exceptions are possibly due to the namespaces not being correctly registered. As for the PDF/A conformance errors, PDF/A compliance requires its own XMP metadata, so it may be you did not include this correctly?

If you still have issues, please update the question with your complete code, and an example PDF so we can investigate further.

Upvotes: 2

Related Questions