PDFlib Cookbook

cookbook

blocks/pdfvt1_with_blocks

Create a large number of invoices in a single PDF and make use of PDF/VT-1 features.

Download Java Code  Switch to PHP Code  Show Output  Show Input (stationery_pdfx4p.pdf) 

package com.pdflib.cookbook.pdflib.blocks;

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

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

/**
 * Create PDF/VT-1 with Blocks
 * 
 * Create a large number of invoices in a single PDF and make use of
 * the following PDF/VT-1 features:
 * - create a document part (DPart) hierarchy
 * - assign PDF/VT scope attributes to images and imported PDF pages
 * - add document part metadata (DPM) to the DPart root node and all page nodes
 * 
 * This topic is similar to starter_pdfvt1, but it uses PDFlib Blocks to
 * place various text and image items on the page.
 *
 * Required software: PPS 10
 * Required data: PDF background, fonts, several raster images
 */
public class pdfvt1_with_blocks {

    final static Random random = new Random();

    static class articledata_s {
        articledata_s(String name, double price) {
            this.name = name;
            this.price = price;
        }

        String name;
        double price;
    };

    static class addressdata_s {
        addressdata_s(String firstname, String lastname, String flat,
            String street, String city) {
            this.firstname = firstname;
            this.lastname = lastname;
            this.flat = flat;
            this.street = street;
            this.city = city;
        }

        String firstname;
        String lastname;
        String flat;
        String street;
        String city;
    };

    static final int MATRIXROWS = 32;
    static final int MATRIXDATASIZE = 4 * MATRIXROWS;

    public static void main(String argv[]) {
        int MAXRECORD = 100;
        int i, stationery, page;
        int record;
        int barcodeimage;
        String stationeryfilename = "stationery_pdfx4p.pdf";
        String fontname = "NotoSerif-Regular";

        /*
         * 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 title = "Create PDF/VT-1 with Blocks";
        String outfile = "pdfvt1_with_blocks.pdf";
        double left = 55;
        double right = 530;
        double bottom = 822;

        double fontsize = 11;
        String buf;
        String optlist;
        String fontoptions;

        String closingtext = "Terms of payment: <save fillcolor={cmyk 0 1 1 0}>30 days net<restore>. "
            + "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.";

        articledata_s articledata[] = { new articledata_s("Super Kite", 20),
            new articledata_s("Turbo Flyer", 40),
            new articledata_s("Giga Trash", 180),
            new articledata_s("Bare Bone Kit", 50),
            new articledata_s("Nitty Gritty", 20),
            new articledata_s("Pretty Dark Flyer", 75),
            new articledata_s("Large Hadron Glider", 85),
            new articledata_s("Flying Bat", 25),
            new articledata_s("Simple Dimple", 40),
            new articledata_s("Mega Sail", 95),
            new articledata_s("Tiny Tin", 25),
            new articledata_s("Monster Duck", 275),
            new articledata_s("Free Gift", 0) };

        addressdata_s addressdata[] = {
            new addressdata_s("Edith", "Poulard", "Suite C", "Main Street",
                "New York"),
            new addressdata_s("Max", "Huber", "", "Lipton Avenue",
                "Albuquerque"),
            new addressdata_s("Herbert", "Pakard", "App. 29", "Easel",
                "Duckberg"),
            new addressdata_s("Charles", "Fever", "Office 3", "Scenic Drive",
                "Los Angeles"),
            new addressdata_s("D.", "Milliband", "", "Old Harbour", "Westland"),
            new addressdata_s("Lizzy", "Tin", "Workshop", "Ford", "Detroit"),
            new addressdata_s("Patrick", "Black", "Backside",
                "Woolworth Street", "Clover") };

        String[] salesrepnames = { "Charles Ragner", "Hugo Baldwin",
            "Katie Blomock", "Ernie Bastel", "Lucy Irwin", "Bob Montagnier",
            "Chuck Hope", "Pierre Richard" };

        int salesrepimage[] = new int[salesrepnames.length];

        String[] headers = { "ITEM", "DESCRIPTION", "QUANTITY", "PRICE",
            "AMOUNT" };

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

        int dpm = 0, cip4_root, cip4_metadata;
        int exitcode = 0;

        double leading = fontsize + 2;
        
        pdflib p = null;

        try {
            p = new pdflib();

            if (p.begin_document(outfile,
                "pdfx=PDF/X-4 pdfvt=PDF/VT-1 usestransparency=false "
                    + "nodenamelist={root recipient} recordlevel=1") == -1) {
                throw new Exception("Error: " + p.get_errmsg());
            }

            /*
             * Set errorpolicy to return, this means we must check return values
             * of load_font() etc. Set the search path for fonts and images etc.
             */
            p.set_option("errorpolicy=return SearchPath={" + searchpath + "}");

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

            p.set_info("Creator", "PDFlib Cookbook");
            p.set_info("Title", title);

            fontoptions = "fontname=" + fontname + " fontsize=" + fontsize;

            /* Define output intent profile */
            if (p.load_iccprofile("ISOcoated_v2_eci.icc",
                "usage=outputintent") == -1) {
                System.err.println("Error: " + p.get_errmsg());
                System.err.println("See www.pdflib.com for ICC profiles.");
                p.delete();
                System.exit(2);
            }

            /*
             * Load company stationery as background (used on first page for
             * each recipient)
             */
            stationery = p.open_pdi_document(stationeryfilename, "");
            if (stationery == -1) {
                throw new Exception("Error: " + p.get_errmsg());
            }

            page = p.open_pdi_page(stationery, 1,
                "pdfvt={scope=global environment={Kraxi Systems}}");
            if (page == -1) {
                throw new Exception("Error: " + p.get_errmsg());
            }

            /*
             * Preload images of all local sales reps (used on first page
             * for each recipient). To get encapsulated image XObjects, the
             * renderingintent option is used.
             */
            for (i = 0; i < salesrepnames.length; i++) {
                String salesrepfilename = "sales_rep" + i + ".jpg";
                salesrepimage[i] = p.load_image("auto", salesrepfilename,
                    "pdfvt={scope=file} renderingintent=Perceptual");

                if (salesrepimage[i] == -1) {
                    throw new Exception("Error: " + p.get_errmsg());
                }
            }

            final int ARTICLECOUNT = articledata.length;
            final int ADDRESSCOUNT = addressdata.length;
            final int COLUMNCOUNT = headers.length;
            final int SALESREPS = salesrepnames.length;

            /*
             * Construct DPM metadata for the DPart root node
             */
            cip4_metadata = p.poca_new("containertype=dict usage=dpm");
            p.poca_insert(cip4_metadata,
                "type=string key=CIP4_Conformance value=base");
            p.poca_insert(cip4_metadata,
                "type=string key=CIP4_Creator value=pdfvt1_with_blocks");
            p.poca_insert(cip4_metadata,
                "type=string key=CIP4_JobID value={Kraxi Systems invoice}");

            optlist = "containertype=dict usage=dpm "
                + "type=dict key=CIP4_Metadata value=" + cip4_metadata;
            cip4_root = p.poca_new(optlist);

            optlist = "containertype=dict usage=dpm "
                + "type=dict key=CIP4_Root value=" + cip4_root;
            dpm = p.poca_new(optlist);

            /* Create root node in the DPart hierarchy and add DPM metadata */
            optlist = "dpm=" + dpm;
            p.begin_dpart(optlist);

            p.poca_delete(dpm, "recursive=true");

            DecimalFormat zip_code_format = new DecimalFormat("00000");

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

            for (record = 0; record < MAXRECORD; record++) {
                byte datamatrix[] = new byte[MATRIXDATASIZE];
                int cip4_recipient, cip4_contact, cip4_person;
                String firstname, lastname, result;
                int pagecount = 0;
                int item;

                firstname = addressdata[get_random(ADDRESSCOUNT)].firstname;
                lastname = addressdata[get_random(ADDRESSCOUNT)].lastname;

                /*
                 * Construct DPM metadata for the next DPart node
                 * (i.e. the page)
                 */
                dpm = p.poca_new("containertype=dict usage=dpm");
                cip4_root = p.poca_new("containertype=dict usage=dpm");
                cip4_recipient = p.poca_new("containertype=dict usage=dpm");
                cip4_contact = p.poca_new("containertype=dict usage=dpm");
                cip4_person = p.poca_new("containertype=dict usage=dpm");

                optlist = "type=dict key=CIP4_Root value=" + cip4_root;
                p.poca_insert(dpm, optlist);

                optlist = "type=dict key=CIP4_Recipient value="
                    + cip4_recipient;
                p.poca_insert(cip4_root, optlist);

                optlist = "type=string key=CIP4_UniqueID value={ID_" + record
                    + "}";
                p.poca_insert(cip4_recipient, optlist);

                optlist = "type=dict key=CIP4_Contact value=" + cip4_contact;
                p.poca_insert(cip4_recipient, optlist);

                optlist = "type=dict key=CIP4_Person value=" + cip4_person;
                p.poca_insert(cip4_contact, optlist);

                optlist = "type=string key=CIP4_Firstname value={" + firstname
                    + "}";
                p.poca_insert(cip4_person, optlist);

                optlist = "type=string key=CIP4_Lastname value={" + lastname
                    + "}";
                p.poca_insert(cip4_person, optlist);

                /*
                 * Create a new node in the document part hierarchy and add DPM
                 * metadata
                 */
                optlist = "dpm=" + dpm;
                p.begin_dpart(optlist);

                p.poca_delete(dpm, "recursive=true");

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

                for (col = 1; col <= COLUMNCOUNT; col++) {
                    optlist = "fittextline={position={" + alignments[col - 1]
                        + " center} " + fontoptions + "} margin=2";
                    tbl = p.add_table_cell(tbl, col, row, headers[col - 1],
                        optlist);
                    if (tbl == -1) {
                        throw new Exception("Error: " + p.get_errmsg());
                    }

                }
                row++;

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

                /*
                 * Print variable-length article list
                 */
                for (i = 0, item = 0; i < ARTICLECOUNT; i++) {
                    int quantity = get_random(9) + 1;
                    double sum;

                    if (item > 0 && get_random(100) > 50)
                        continue;

                    col = 1;

                    item++;
                    sum = articledata[i].price * quantity;

                    /* column 1: ITEM */
                    buf = "" + item;
                    optlist = "fittextline={position={" + alignments[col - 1]
                        + " center} " + fontoptions + "} colwidth=5% margin=2";
                    tbl = p.add_table_cell(tbl, col++, row, buf, optlist);
                    if (tbl == -1) {
                        throw new Exception("Error: " + p.get_errmsg());
                    }

                    /* column 2: DESCRIPTION */
                    optlist = "fittextline={position={" + alignments[col - 1]
                        + " center} " + fontoptions + "} colwidth=50% margin=2";
                    tbl = p.add_table_cell(tbl, col++, row, articledata[i].name,
                        optlist);
                    if (tbl == -1) {
                        throw new Exception("Error: " + p.get_errmsg());
                    }

                    /* column 3: QUANTITY */
                    buf = "" + quantity;
                    optlist = "fittextline={position={" + alignments[col - 1]
                        + " center} " + fontoptions + "} margin=2";
                    tbl = p.add_table_cell(tbl, col++, row, buf, optlist);
                    if (tbl == -1) {
                        throw new Exception("Error: " + p.get_errmsg());
                    }

                    /* column 4: PRICE */
                    buf = priceFormat.format(articledata[i].price);
                    optlist = "fittextline={position={" + alignments[col - 1]
                        + " center} " + fontoptions + "} margin=2";
                    tbl = p.add_table_cell(tbl, col++, row, buf, optlist);
                    if (tbl == -1) {
                        throw new Exception("Error: " + p.get_errmsg());
                    }

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

                    total += sum;
                    row++;
                }

                /* Print total in the rightmost column */
                buf = priceFormat.format(total);
                optlist = "fittextline={position={"
                    + alignments[COLUMNCOUNT - 1] + " center} " + fontoptions
                    + "} margin=2";
                tbl = p.add_table_cell(tbl, COLUMNCOUNT, row++, buf, optlist);
                if (tbl == -1) {
                    throw new Exception("Error: " + p.get_errmsg());
                }

                /* Footer row with terms of payment */
                optlist = fontoptions + " alignment=justify leading=120%";
                int tf = p.create_textflow(closingtext, optlist);
                if (tf == -1) {
                    throw new Exception("Error: " + p.get_errmsg());
                }

                optlist = "rowheight=1 margin=2 margintop=" + 2 * fontsize
                    + " textflow=" + tf + " colspan=" + COLUMNCOUNT;
                tbl = p.add_table_cell(tbl, 1, row++, "", optlist);
                if (tbl == -1) {
                    throw new Exception("Error: " + p.get_errmsg());
                }

                /* Place the table instance(s), creating pages as required */
                do {
                    double top = 100; // estimate value

                    p.begin_page_ext(0, 0,
                        "topdown=true width=a4.width height=a4.height");

                    if (++pagecount == 1) {
                        /*
                         * Place company stationery as background on first page
                         * for each recipient
                         */
                        p.fit_pdi_page(page, 0, 842, "");

                        /*
                         * Fill Blocks with name and image of local sales rep on
                         * first page for each recipient
                         */

                        optlist = "";
                        buf = "Local sales rep:" + "\n"
                            + salesrepnames[record % SALESREPS];
                        if (p.fill_textblock(page, "salesrepname", buf,
                            optlist) == -1)
                            System.err.println("Warning: " + p.get_errmsg());

                        if (p.fill_imageblock(page, "salesrepimage",
                            salesrepimage[record % SALESREPS], "") == -1)
                            System.err.println("Warning: " + p.get_errmsg());

                        /*
                         * Fill Text Blocks with recipient's address
                         */
                        optlist = "";

                        buf = firstname + " " + lastname;
                        if (p.fill_textblock(page, "name", buf, optlist) == -1)
                            System.err.println("Warning: " + p.get_errmsg());

                        buf = addressdata[get_random(ADDRESSCOUNT)].flat;
                        if (p.fill_textblock(page, "flat", buf, optlist) == -1)
                            System.err.println("Warning: " + p.get_errmsg());

                        buf = get_random(999) + " "
                            + addressdata[get_random(ADDRESSCOUNT)].street;
                        if (p.fill_textblock(page, "street", buf,
                            optlist) == -1)
                            System.err.println("Warning: " + p.get_errmsg());

                        buf = zip_code_format.format(get_random(99999)) + " "
                            + addressdata[get_random(ADDRESSCOUNT)].city;
                        if (p.fill_textblock(page, "city", buf, optlist) == -1)
                            System.err.println("Warning: " + p.get_errmsg());

                        /*
                         * Fill image Block with barcode image for each
                         * recipient. To get encapsulated image XObjects the
                         * renderingintent option is used.
                         */
                        create_datamatrix(datamatrix, record);
                        p.create_pvf("barcode", datamatrix, "");

                        barcodeimage = p.load_image("raw", "barcode",
                            "bpc=1 components=1 width=32 height=32 invert "
                                + "pdfvt={scope=singleuse} "
                                + "renderingintent=Saturation");
                        if (barcodeimage == -1) {
                            throw new Exception("Error: " + p.get_errmsg());
                        }

                        if (p.fill_imageblock(page, "barcode", barcodeimage,
                            "") == -1)
                            System.err.println("Warning: " + p.get_errmsg());

                        p.close_image(barcodeimage);
                        p.delete_pvf("barcode");

                        /*
                         * Fill Text Blocks with header and date
                         */
                        optlist = "";
                        buf = "INVOICE "
                            + Calendar.getInstance().get(Calendar.YEAR) + "-"
                            + (record + 1);
                        if (p.fill_textblock(page, "invoice_number", buf,
                            optlist) == -1)
                            System.err.println("Warning: " + p.get_errmsg());

                        buf = DateFormat
                            .getDateInstance(DateFormat.LONG, Locale.US)
                            .format(new Date());
                        if (p.fill_textblock(page, "date", buf, optlist) == -1)
                            System.err.println("Warning: " + p.get_errmsg());

                        /*
                         * Retrieve y coordinate of the lowest Block
                         * "invoice_number" so that we know where subsequent
                         * data can be placed.
                         */
                        top = p.pcos_get_number(stationery,
                            "pages[0]/blocks/invoice_number/Rect[1]");

                        // adjust for topdown coordinate system
                        top = 842 - top + 2 * leading;
                    }
                    else {
                        top = 50;
                    }

                    /*
                     * Place the table on the page. Shade every other row,
                     * except the footer row.
                     */
                    result = p.fit_table(tbl, left, bottom, right, top,
                        "header=1 "
                            + "fill={{area=rowodd fillcolor={gray 0.9}} "
                            + "{area=rowlast fillcolor={gray 1}}} "
                            + "rowheightdefault=auto colwidthdefault=auto");

                    if (result.equals("_error")) {
                        throw new Exception(
                            "Couldn't place table: " + p.get_errmsg());
                    }

                    p.end_page_ext("");
                }
                while (result.equals("_boxfull"));

                p.delete_table(tbl, "");

                /* Close node in the document part hierarchy */
                p.end_dpart("");
            }

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

            for (i = 0; i < salesrepnames.length; i++) {
                p.close_image(salesrepimage[i]);
            }

            /* Close root node in the document part hierarchy */
            p.end_dpart("");

            p.end_document("");
        }
        catch (PDFlibException e) {
            System.err.println(
                "PDFlib exception occurred in pdfvt1_with_blocks sample:");
            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);
        }
    }

    /**
     * Get a pseudo random number between 0 and n-1.
     * For internal reasons we use our own simplistic random number generator.
     */
    static long seed = 0x1234;
    static int get_random(int n)
    {
        seed = ((seed * 0xDEECE66D) + 11) & 0x7FFFFFFF;
        return (int) (seed % n);
    } 
    /**
     * Simulate a datamatrix barcode
     */
    static void create_datamatrix(byte datamatrix[], int record) {
        int i;

        for (i = 0; i < MATRIXROWS; i++) {
            datamatrix[4 * i + 0] = (byte) ((0xA3 + 1 * record + 17 * i) % 0xFF);
            datamatrix[4 * i + 1] = (byte) ((0xA2 + 3 * record + 11 * i) % 0xFF);
            datamatrix[4 * i + 2] = (byte) ((0xA0 + 5 * record + 7 * i) % 0xFF);
            datamatrix[4 * i + 3] = (byte) ((0x71 + 7 * record + 9 * i) % 0xFF);
        }
        for (i = 0; i < MATRIXROWS; i++) {
            datamatrix[4 * i + 0] |= 0x80;
            datamatrix[4 * i + 2] |= 0x80;
            if ((i % 2) != 0)
                datamatrix[4 * i + 3] |= 0x01;
            else
                datamatrix[4 * i + 3] &= 0xFE;
        }
        for (i = 0; i < 4; i++) {
            datamatrix[4 * (MATRIXROWS / 2 - 1) + i] = (byte) 0xFF;
            datamatrix[4 * (MATRIXROWS - 1) + i] = (byte) 0xFF;
        }
    }
}