Skip to main content

Embedding files and adding custom XMP with PDF/A-3b

As of 2025, electronic invoicing based on Zugferd will be mandatory in Germany which means that some of our customers will have to embed different file types in a PDF/A-3. Additionally, sometimes, when writing files with any of the PDF/A presets or PDFVersion parameter set, Mako will drop XMP properties if they cannot be parsed correctly.

This C# example demonstrates how to create a PDF/A-3b file with a custom .xml file attachment and custom XMP metadata that is preserved in the PDF output. It uses the XmpCore library to generate the custom XMP, with a minor modification to ensure compatibility, and subsequently merged with the mandatory PDF/A-3b metadata. The example makes use of the setIgnoreXmp(false) feature introduced in Mako 8, which ensures that the custom XMP is preserved in the final output.

Custom XMP C# Example
C#
/* --------------------------------------------------------------------------------
 *  <copyright file="Program.cs" company="Global Graphics Software Ltd">
 *    Copyright (c) 2025 Global Graphics Software Ltd. All rights reserved.
 *  </copyright>
 *  <summary>
 *    This example is provided on an "as is" basis and without warranty of any kind.
 *    Global Graphics Software Ltd. does not warrant or make any representations
 *    regarding the use or results of use of this example.
 *  </summary>
 * ---------------------------------------------------------------------------------
 */

using JawsMako;
using XmpCore;
using XmpCore.Impl;
using XmpCore.Options;

namespace XMPissue;


internal class Program
{
    static int Main(string[] args)
    {
        try
        {
            var testFilepath = "..\\..\\..\\..\\TestFiles\\";
            Directory.CreateDirectory(testFilepath);
            using var mako = IJawsMako.create();
            IJawsMako.enableAllFeatures(mako);
            using var factory = mako.getFactory();
            var assembly = IDocumentAssembly.create(mako);
            var document = IDocument.create(mako);
            var page = IPage.create(mako);
            var content = IDOMFixedPage.create(factory, 210 / 25.4 * 96, 297 / 25.4 * 96); // XPS units
            page.setContent(content);
            document.appendPage(page);
            assembly.appendDocument(document);

            // Create ZugFERD XMP
            var xmp = XmpMetaFactory.Create();
            XmpMetaFactory.SchemaRegistry.RegisterNamespace(@"urn:ferd:pdfa:invoice:rc#", "zf");
            var namespaces = XmpMetaFactory.SchemaRegistry.Namespaces;
            xmp.SetProperty(@"urn:ferd:pdfa:invoice:rc#", "DocumentType", "INVOICE");
            var options = new SerializeOptions();
            options.OmitPacketWrapper = true;
            options.UseCanonicalFormat = true;
            var newXmp = XmpSerializerHelper.SerializeToString((XmpMeta)xmp, options);
            newXmp = newXmp[1..]; //remove leading question mark
            
            // Prepare XMP packet for Mako
            var pair = mako.getTempStore().createTemporaryReaderWriterPair();
            var newXmpStreamReader = pair.inputStream;
            var newXmpStreamWriter = pair.outputStream;
            newXmpStreamWriter.open();
            newXmpStreamWriter.completeWrite(newXmp);
            newXmpStreamReader.close();
            assembly.setXmpPacket(newXmpStreamReader);

            // Add attachment
            IRAInputStream fileStream = IInputStream.createFromFile(mako, testFilepath + "factur-x.xml");
            if (fileStream is not null && fileStream.open())
            { 
                var metadata = new IFileSpecAsEmbeddedData.Data();
                metadata.fileName = "faktur-x.xml";
                metadata.description = "ZUGFeRD-invoice";
                metadata.mimeType = "text/xml";
                metadata.modificationDate = DateTime.Now.ToString();
                metadata.fileSize = (ulong)fileStream.IRAStream_length();
                metadata.fileDataStream = fileStream;
                IFileSpecAsEmbeddedData embeddedFile = IFileSpecAsEmbeddedData.createInstance(mako.getFactory(), metadata);
                if (embeddedFile is not null)
                    document.addEmbeddedStream(embeddedFile);
            }

            var output = IPDFOutput.create(mako);
            output.setPreset("PDF/A-3b");
            output.setIgnoreXmp(false);
            output.setValidateXmp(false);

            output.writeAssembly(assembly, testFilepath + "zugferd.pdf");
        }
        catch (MakoException e)
        {
            Console.WriteLine($"Exception thrown: {e.m_errorCode}: {e.m_msg}");
        }
        catch (Exception e)
        {
            Console.WriteLine($"Exception thrown: {e}");
        }

        return 0;
    }
}

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.