PDFlib Cookbook

cookbook

pdfa/invoice_pdfa2a

Combined PDF/UA-1 and PDF/A-2a invoice generation demo.

Download Java Code  Switch to PHP Code  Show Output 

/* invoice_pdfua1/invoice_pdfa2a
 *
 * Combined PDF/UA-1 and PDF/A-2a invoice generation demo
 *
 * Required software: PDFlib+PDI/PPS 10
 * (set run_sample_with_pdi=false below to run the sample code without PDI)
 * Required data: two fonts, raster image, Tagged PDF for imported logo
 */

package com.pdflib.cookbook.pdflib.pdfa;

import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

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

public class invoice_pdfa2a {
    final static boolean run_sample_with_pdi = true;

    final static double x_table = 55;
    final static double tablewidth = 475;

    final static double y_address = 682;
    final static double x_salesrep = 455;
    final static double y_invoice = 542;
    final static double y_invoice_p2 = 800;
    final static double bottom = 40;
    final static double imagesize = 90;

    final static double fontsize = 10;
    final static double fontsizesmall = 9;

    final static String fontname = "NotoSerif-Regular";

    final static String pagedimensions = "width=a4.width height=a4.height";

    final static String basefontoptions =
        "fontname=" + fontname + " fontsize=" + fontsize;

    final static String basetagoptions = "tag={tagname=P}";

    /**
     * Place company stationery
     * @throws Exception 
     */
    static void create_stationery(pdflib p) throws Exception {
        String sender = "Kraxi Systems, Inc. \u2022 17, " +
            "Aviation Road \u2022 Paperfield";
        String stationeryfontname = "NotoSerif-Regular";

        double y_company_logo = 748;

        String senderfull =
            "17, Aviation Road\n" +
            "Paperfield<nextline leading=50%><nextparagraph leading=120%>" +
            "Phone 7079 4301\n" +
            "Fax 7079 4302<nextline leading=50%><nextparagraph leading=120%>" +
            "info@kraxi.com\n" +
            "www.kraxi.com\n";

        String salesrepfilename = "sales_rep4.jpg";
        String salesrepname = "Lucy Irwin";
        String salesrepcaption = "Local sales rep:";

        /*
         * ----------------------------------- 
         * Print company logo and sender address
         * -----------------------------------
         */

        if (run_sample_with_pdi) {
            int page, stationery;
            String stationeryfilename = "kraxi_logo2.pdf";

            stationery = p.open_pdi_document(stationeryfilename, "");
            page = p.open_pdi_page(stationery, 1, "");

            /* Check whether the page can be placed in the current situation */
            if ((int) p.info_pdi_page(page, "fittingpossible", "") == 0) {
                throw new Exception("Cannot place Tagged PDF page: "
                    + p.get_errmsg());
            }

            p.fit_pdi_page(page, 0, y_company_logo,
                " boxsize={595 85} position={65 center}");

            p.close_pdi_page(page);
            p.close_pdi_document(stationery);
        }

        String optlist = basefontoptions + " fontsize=" + fontsizesmall
            + " fontname=" + stationeryfontname
            + " charref=true tag={tagname=Artifact}";
        p.fit_textline(sender, x_table, y_address + fontsize, optlist);

        /*
         * -----------------------------------
         * Print full company contact details
         * -----------------------------------
         */
        optlist = basefontoptions + " fontname=" + stationeryfontname
            + " fillcolor={gray 0.35} leading=125%";

        int tf = p.create_textflow(senderfull, optlist);

        p.fit_textflow(tf, x_salesrep, y_address,
            x_salesrep + 100, y_address + 150, basetagoptions + " verticalalign=bottom");

        /*
         * -----------------------------------
         * Place name and image of local sales rep.
         * Figure is followed by Caption per Best Practice Guide
         * -----------------------------------
         */
        int salesrepimage = p.load_image("auto", salesrepfilename, "");

        optlist = "boxsize={" + imagesize + " " + imagesize
            + "} fitmethod=meet tag={tagname=Figure Alt={Photo of sales rep.} Placement=Block}";
        p.fit_image(salesrepimage, x_salesrep, y_address - 3 * fontsizesmall
            - imagesize, optlist);
        p.close_image(salesrepimage);
        
        /* Open two structure elements with a single call */
        int id_Caption = p.begin_item("Caption", "tag={tagname=P}");

        optlist = basefontoptions + " fontsize=" + fontsizesmall;
        p.fit_textline(salesrepcaption, x_salesrep, y_address - fontsizesmall,
            optlist);
        p.fit_textline(salesrepname, x_salesrep, y_address - 2 * fontsizesmall,
            optlist);

        p.end_item(id_Caption);        
    }

    /**
     * Print receiver address
     * @throws PDFlibException 
     */
    static void create_address(pdflib p) throws PDFlibException {
        String address =
            "John Q. Doe\n" +
            "255 Customer Lane\n" +
            "Suite B\n" +
            "12345 User Town\n" +
            "Everland\n";
        int tf = p.create_textflow(address, basefontoptions + " leading=120% ");

        p.fit_textflow(tf, x_table, y_address,
            x_table + 200, y_address-100, basetagoptions);
    }

    /*
     * -----------------------------------
     * Print the header and date
     * -----------------------------------
     */
    static void create_table_header(pdflib p, int id_Part)
        throws PDFlibException {
        String invoiceheader = "INVOICE "
            + Calendar.getInstance().get(Calendar.YEAR) + "-10";

        /* Add a structured bookmark which references the Part element */
        String optlist = "item=" + id_Part;
        p.create_bookmark(invoiceheader, optlist);

        optlist = basefontoptions + " tag={tagname=H1 Title={" + invoiceheader
            + "}}";
        p.fit_textline(invoiceheader, x_table, y_invoice, optlist);

        String date = DateFormat.getDateInstance(DateFormat.LONG, Locale.US)
            .format(new Date());

        optlist = "position {100 0} " + basefontoptions + " " + "tag={tagname=P Title=date}";
        p.fit_textline(date, x_table + tablewidth, y_invoice, optlist);
    }

    public static void main(String argv[]) {

        /* This is where font/image/PDF input files live. 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 closingtext =
            "Terms of payment: 30 days net. "
            + "90 days warranty starting at the day of sale. "
            + "This warranty covers defects in workmanship only. "
            + "Kraxi Systems, Inc. will, at its option, repair or replace the "
            + "product under the warranty. This warranty is not transferable. "
            + "No returns or exchanges will be accepted for wet products.";

        class articledata {
            articledata(String name, double price, int quantity) {
                this.name = name;
                this.price = price;
                this.quantity = quantity;
            }

            String name;
            double price;
            int quantity;
        }

        articledata[] data = {
            new articledata("Super Kite", 20, 2),
            new articledata("Turbo Flyer", 40, 5),
            new articledata("Giga Trash", 180, 1),
            new articledata("Bare Bone Kit", 50, 3),
            new articledata("Nitty Gritty", 20, 10),
            new articledata("Pretty Dark Flyer", 75, 1),
            new articledata("Free Gift", 0, 1)
        };

        String[] headers = { 
            "ITEM", "DESCRIPTION", "QUANTITY", "PRICE (\u20AC)", "AMOUNT (\u20AC)" };

        String[] alignments = { "right", "left", "right", "right", "right" };

        final NumberFormat priceFormat = NumberFormat.getInstance(Locale.US);
        priceFormat.setMaximumFractionDigits(2);
        priceFormat.setMinimumFractionDigits(2);

        pdflib p = null;
        int exitcode = 0;

        try {
            int i;
            int row, col, tbl;
            String result;
            double total;
            String optlist;
            String buf;
            int tf;
            int id_Sect, id_Part;

            p = new pdflib();

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

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

            /*
             * This mean we don't have to check error return values, but will
             * get an exception in case of runtime problems.
             */
            p.set_option("errorpolicy=exception");

            /* "autospace" improves Reflow */
            p.set_option("autospace=true");

            p.begin_document("invoice_pdfa2a.pdf",
                "pdfua=PDF/UA-1 pdfa=PDF/A-2a lang=en "
                    + "tag={tagname=Document "
                    + "Title={Invoice for John Q. Doe - Kraxi Systems, Inc.}}");

            p.set_info("Creator", "tagged_invoice");
            p.set_info("Author", "Kraxi Systems, Inc.");
            p.set_info("Title",
                "Invoice for John Q. Doe - Kraxi Systems, Inc.");

            /* Use sRGB output intent so that we can use RGB color */
            p.load_iccprofile("sRGB", "usage=outputintent");

            /* Each Part element could contain a separate invoice */
            id_Part = p.begin_item("Part", "Title={Invoice " +
                Calendar.getInstance().get(Calendar.YEAR) + "-10}");

            /*
             * -----------------------------------
             * Create and place table with article list
             * -----------------------------------
             */
            /* ---------- Header row */
            row = 1;
            tbl = -1;

            for (col = 1; col <= headers.length; col++) {
                optlist = "fittextline={position={" + alignments[col - 1]
                    + " center} " + basefontoptions + " " 
                    + "} margin=2";
                tbl = p
                    .add_table_cell(tbl, col, row, headers[col - 1], optlist);
            }
            row++;

            /* ---------- Data rows: one for each article */
            total = 0;

            for (i = 0; i < data.length; i++) {
                double sum = data[i].price * data[i].quantity;
                col = 1;

                /* column 1: ITEM */
                buf = "" + (i + 1);
                optlist = "fittextline={position={" + alignments[col - 1]
                    + " center} " + basefontoptions + " " 
                    + "} margin=2";
                tbl = p.add_table_cell(tbl, col++, row, buf, optlist);

                /* column 2: DESCRIPTION */
                optlist = "fittextline={position={" + alignments[col - 1]
                    + " center} " + basefontoptions + " " 
                    + "} colwidth=50% margin=2";
                tbl = p.add_table_cell(tbl, col++, row, data[i].name, optlist);

                /* column 3: QUANTITY */
                buf = "" + data[i].quantity;
                optlist = "fittextline={position={" + alignments[col - 1]
                    + " center} " + basefontoptions + " " 
                    + "} margin=2";
                tbl = p.add_table_cell(tbl, col++, row, buf, optlist);

                /* column 4: PRICE */
                buf = priceFormat.format(data[i].price);
                optlist = "fittextline={position={" + alignments[col - 1]
                    + " center} " + basefontoptions + " " 
                    + "} margin=2";
                tbl = p.add_table_cell(tbl, col++, row, buf, optlist);

                /* column 5: AMOUNT */
                buf = priceFormat.format(sum);
                optlist = "fittextline={position={" + alignments[col - 1]
                    + " center} " + basefontoptions + " " 
                    + "} margin=2";
                tbl = p.add_table_cell(tbl, col++, row, buf, optlist);

                total += sum;
                row++;
            }

            /* ---------- Print total in the rightmost column */
            buf = priceFormat.format(total);
            optlist = "fittextline={position={"
                + alignments[headers.length - 1] + " center} "
                + basefontoptions + "} margin=2";
            tbl = p.add_table_cell(tbl, headers.length, row++, buf, optlist);


            /* ---------- Create invoice header on first page */
            p.begin_page_ext(0, 0, pagedimensions);

            id_Sect = p.begin_item("Sect", "Title={Sender stationery}");
            create_stationery(p);
            p.end_item(id_Sect);
            

            id_Sect = p.begin_item("Sect", "Title={Recipient address}");
            create_address(p);
            p.end_item(id_Sect);

            id_Sect = p.begin_item("Sect", "Title={Invoice body}");
            create_table_header(p, id_Part);

            /*
             * ---------- Place the table instance(s), creating pages as
             * required
             */
            double top = y_invoice - 3 * fontsize;
            do {
                /* Place the table on the page; Shade every other row. */
                optlist = "header=1 fill={{area=rowodd fillcolor={gray 0.9}}} "
                    + "tag={tagname=Table}";

                result = p.fit_table(tbl, x_table, bottom,
                    x_table + tablewidth, top, optlist);

                if (result.equals("_error")) {
                    throw new Exception("Couldn't place table: "
                        + p.get_errmsg());
                }
                else if (result.equals("_boxfull"))
                {
                    p.end_page_ext("");
                    p.begin_page_ext(0, 0, pagedimensions);
                    top = y_invoice_p2;
                }
            }
            while (result.equals("_boxfull"));

            /* Determine vertical start point for footer from top of last
             * placed table fragment.
             */
            double table_height = p.info_table(tbl, "height");
            top -= table_height + 2 * fontsize;

            p.delete_table(tbl, "");

            /* ---------- Footer with terms of payment */
            optlist = basefontoptions + " alignment=justify leading=120%";
            tf = p.create_textflow(closingtext, optlist);

            /* Loop until all of the text is placed; create new pages
             * as long as more text needs to be placed.
             */
            do
            {
                result = p.fit_textflow(tf, x_table, bottom,
                    x_table + tablewidth, top, "tag={tagname=P}");

                /* Start new page if more text is to be placed */
                if (result.equals("_boxfull") || result.equals("_nextpage"))
                {
                    p.end_page_ext("");
                    p.begin_page_ext(0, 0, pagedimensions);
                    top = y_invoice_p2;
                }
            } while (result.equals("_boxfull") || result.equals("_nextpage"));

            p.end_item(id_Sect);
            p.end_item(id_Part);

            p.end_page_ext("");

            p.end_document("");
        }
        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);
        }
    }
}