PDFlib Cookbook

cookbook

pdfa/zugferd2_add_xml_to_pdfa3b

ZUGFeRD 2: Add XML invoice to PDF.

Download Java Code  Switch to PHP Code  Show Output  Show Input (zugferd2-invoice.xml)  Show Input (ZUGFeRD2_invoice_noxml.pdf) 

/*
 * ZUGFeRD 2 Add XML Invoice to PDF:
 * Import the pages of a plain PDF/A invoice with PDI and add a
 * corresponding XML invoice to the output file. Note that the application
 * must take care that the imported PDF invoice and the XML invoice match.
 *
 * Required software: PDFlib+PDI/PPS 9
 * Required data: PDF/A input document, XML invoice file, XMP metadata
 */
package com.pdflib.cookbook.pdflib.pdfa;

import com.pdflib.pdflib;
import com.pdflib.PDFlibException;

public class zugferd2_add_xml_to_pdfa3b {
    public static void main(String argv[]) {
        /* This is where the data files are. Adjust as necessary. */
        String searchpath = "../input";

        /* By default annotations are also imported. In some cases this
         * requires the Noto fonts for creating annotation appearance streams.
         * We therefore set the searchpath to also point to the font directory.
         */
        String fontpath = "../resource/font";
        
        String outfile = "zugferd2_add_xml_to_pdfa3b.pdf";
        String infile = "ZUGFeRD2_invoice_noxml.pdf";
        String title = "ZUGFeRD 2 Add XML Invoice to PDF";
        String xmpfile = "ZUGFeRD2_extension_schema.xmp";
        String xmlinvoicefile = "zugferd2-invoice.xml";
        String attachmentname = "zugferd-invoice.xml";

        pdflib p = null;
        int exitcode = 0;

        try {
            p = new pdflib();

            p.set_option("searchpath={" + searchpath + "}");

            p.set_option("searchpath={" + fontpath + "}");

            /* This means we must check return values of load_font() etc. */
            p.set_option("errorpolicy=return");

            if (p.begin_document(outfile, "pdfa=PDF/A-3b metadata={filename={"
                + xmpfile + "}}") == -1)
                throw new Exception("Error: " + p.get_errmsg());

            /* Open the input PDF */
            int indoc = p.open_pdi_document(infile, "");
            if (indoc == -1) {
                throw new Exception("Error: " + p.get_errmsg());
            }
            
            /*
             * Clone PDF/A or PDF/X output intent
             */
            if (p.process_pdi(indoc, -1, "action=copyoutputintent") == -1) {
                throw new Exception("Error: " + p.get_errmsg());
            }

            p.set_info("Creator", "PDFlib Cookbook");
            p.set_info("Title", title);
            
            int endpage = (int) p.pcos_get_number(indoc, "length:pages");

            /* Loop over all pages of the input document */
            for (int pageno = 1; pageno <= endpage; pageno++) {
                int page = p.open_pdi_page(indoc, pageno, "");

                if (page == -1) {
                    throw new Exception("Error: " + p.get_errmsg());
                }
                
                /* Page size will be adjusted by fit_pdi_page() */
                p.begin_page_ext(0, 0, "width=a4.width height=a4.height");

                /*
                 * Place the imported page on the output page, and adjust the
                 * page size
                 */
                p.fit_pdi_page(page, 0, 0, "adjustpage");
                p.close_pdi_page(page);

                p.end_page_ext("");
            }
            
            /*
             * Clone XMP metadata of input document if present. In order to
             * be able to merge the XMP from the input document with the
             * XMP needed according to ZUGFeRD for identifying the
             * XML invoice, the XMP from the input document is supplied
             * to the "metadata" option of PDF_end_document(), while the
             * ZUGFeRD XMP was supplied to the "metadata" option of
             * PDF_begin_document().
             */
            String pfv_xmpfile = "/xmp/document.xmp";
            if (!p.pcos_get_string(indoc, "type:/Root/Metadata")
                                                    .equals("stream")) {
                throw new Exception(
                    "Error: XMP metadata missing in input document");
            }
            
            byte[] xmp = p.pcos_get_stream(indoc, "", "/Root/Metadata");
            p.create_pvf(pfv_xmpfile, xmp, "");
            String xmp_optlist = "metadata={filename=" + pfv_xmpfile + "}";
            
            p.close_pdi_document(indoc);

            /*
             * Load the XML file for the invoice, and associate it with the
             * document with relationship "Alternative". Also mark it as an
             * attachment that can be retrieved with Acrobat's normal attachment
             * dialog.
             * The name of the attachment is explicitly specified, as the
             * name of the disk file differs from the attachment name
             * required by ZUGFeRD.
             */
            int xml_invoice = p.load_asset("Attachment", xmlinvoicefile,
                "mimetype=text/xml "
                    + "description={Rechnungsdaten im ZUGFeRD-XML-Format} "
                    + "relationship=Alternative documentattachment=true "
                    + "name=" + attachmentname);

            if (xml_invoice == -1) {
                throw new Exception("Error: " + p.get_errmsg());
            }
            
            p.end_document("associatedfiles={" + xml_invoice + "} "
                            + xmp_optlist);
            
            p.delete_pvf(pfv_xmpfile);
        }
        catch (PDFlibException e) {
            System.err.println("PDFlib exception occurred:");
            System.err.println("[" + e.get_errnum() + "] " + e.get_apiname() +
                ": " + e.get_errmsg());
            exitcode = 1;
        }
        catch (Exception e) {
            System.err.println(e);
            exitcode = 1;
        }
        finally {
            if (p != null) {
                p.delete();
            }
            System.exit(exitcode);
        }
    }
}