BASKET
Search
PDFlib

table/table_invoice

Download Java Code        Switch to PHP Code      Show Output PDF

/* $Id: table_invoice.java,v 1.8 2013/03/28 07:50:06 stm Exp $

 * Table invoice:

 * Create an invoice using the table feature

 *

 * Create an invoice by importing a background PDF page with the company's

 * stationery header. Output the customer's address data as well as a table

 * listing all items ordered including their quantities and prices. If the table

 * spans several pages output a subtotal after each table instance. To retrieve

 * the last row having been output in a table instance use info_table() with the

 * "lastbodyrow" option. In the last table row, output the total at the end of

 * the table. Output some final text directly after the table. Use info_table()

 * with the "height" option to retrieve the exact end position of the table.

 *

 * Required software: PDFlib+PDI/PPS 9

 * Required data: PDF file

 */

package com.pdflib.cookbook.pdflib.table;


import java.math.BigDecimal;

import java.text.DateFormat;

import java.util.Date;

import java.util.Locale;


import com.pdflib.pdflib;

import com.pdflib.PDFlibException;


public class table_invoice

{

    public static void main (String argv[])

    {

    /* This is where the data files are. Adjust as necessary. */

    String searchpath = "../input";

    String outfile = "table_invoice.pdf";

    String title = "Table Invoice";

   

    pdflib p = null;


    String infile = "stationery.pdf";

   

    int row, col, tf=-1, tbl=-1;

    int i, y, stationery, itemno, page, regularfont, boldfont;

 

    String tf_opts;

 

    double sum = 0, total = 0, subtotal = 0, tabheight = 0;

    String result;

 

    final double pagewidth = 595, pageheight = 842;

    final double fontsize = 12;

    final double capheight = 8.5;

    final double rowheight = 16;

    final int margin = 4;

    final String leading = "120%";

    final int ystart = (int) pageheight - 170;

    final int yoffset = 15;

    final int ycontinued = 40;

    final int nfooters = 1, nheaders = 1;

   

    /* The table coordinates are fixed; only the height of the table may differ

     */

    final int llx = 55, urx = 505, lly = 80;

       

    /* The widths of the individual columns is fixed */

    final int maxcol = 5;

   

    final int c1 = 30, c2 = 200, c3 = 70, c4 = 70, c5 = 80;

   

    /* Get the current date */

    Date now = new Date();

    DateFormat fulldate =

        DateFormat.getDateInstance(DateFormat.LONG, Locale.US);

 

    /* Text to output after the table */

    final 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.";


    final String[][] items = {

    /*     Description,        Quantity, Price */

        { "Long Distance Glider; price includes volume discount of 20% for " +

          "more than 10 items ordered",

                                "11",    "15.96"},

        { "Turbo Flyer",        "5",     "39.95"},

        { "Giga Trash",         "1",     "179.95"},

        { "Bare Bone Kit",      "3",     "49.95"},

        { "Nitty Gritty",       "10",    "19.95"},

        { "Pretty Dark Flyer""1",     "74.95"},

        { "Free Gift",          "2",     "29.95"},

        { "Giant Wing",         "2",     "29.95"},

        { "Cone Head Rocket; price includes volume discount of 30% for " +

          "more than 20 items ordered",

                                "25",     "6.97"},

        { "Super Dart",         "2",      "29.95"},

        { "German Bi-Plane",    "6",      "9.95"},

        { "Turbo Glider; price includes volume discount of 20% for " +

          "more than 10 items ordered",

                                 "11",     "15.96"},

        { "Red Baron",           "5",      "39.95"},

        { "Mega Rocket",         "1",      "179.95"},

        { "Kit the Kat",         "3",      "49.95"},

        { "Red Wing",            "10",     "19.95"},

        { "Dark Rider",          "1",      "74.95"},

        { "Speedy Gift",         "2",      "29.95"},

        { "Giant Bingo",         "2",      "29.95"},

        { "Ready Rocket; price includes volume discount of 30% for " +

          "more than 20 items ordered",

                                 "25",     "6.97"},

    };

   

    final String [] address = {

        "John Q. Doe", "255 Customer Lane", "Suite B",

        "12345 User Town", "Everland"

    };


    /* Used to format the prices to a maximum of to fraction digits */

    BigDecimal value, roundedValue;

   

    try {

        p = new pdflib();


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


        /* This means we must check return values of load_font() etc. */

        p.set_option("errorpolicy=return");


        if (p.begin_document(outfile, "") == -1)

            throw new Exception("Error: " + p.get_errmsg());


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

        p.set_info("Title", title + " $Revision: 1.8 $");


        /* Open the PDF document */

        stationery = p.open_pdi_document(infile, "");

        if (stationery == -1)

            throw new Exception("Error: " + p.get_errmsg());

       

        /* Open the first page of the PDF */

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

        if (page == -1)

            throw new Exception("Error: " + p.get_errmsg());

     

        /* Load the bold and regular styles of a font */

        boldfont = p.load_font("Helvetica-Bold", "unicode", "");

        if (boldfont == -1)

            throw new Exception("Error: " + p.get_errmsg());

       

        regularfont = p.load_font("Helvetica", "unicode", "");

        if (regularfont == -1)

            throw new Exception("Error: " + p.get_errmsg());

       

        /* Start the output page */

        p.begin_page_ext(pagewidth, pageheight, "");


        /* Fit and close the imported PDF page */

        p.fit_pdi_page(page, 0, 0, "");

        p.close_pdi_page(page);


        /* Output the customer's address */

        y = ystart;

       

        p.setfont(regularfont, fontsize);

   

        for (i = 0; i < address.length; i++) {

            p.fit_textline(address[i], llx, y, "");

            y -= yoffset;

        }

       

        /* Print the header and the date */

        y -= 3 * yoffset;

       

        p.setfont(boldfont, fontsize);

       

        p.fit_textline("INVOICE", llx, y, "position {left top}");

        p.fit_textline(fulldate.format(now), urx, y, "position {right top}");

       

        y -= 3 * yoffset;

       

        /* ----------------------------------------------------

         * Add the first table row containing the heading cells

         * ----------------------------------------------------

         */

       

        /* Prepare the general option list for adding text line cells of the

         * table header:

         * Define a fixed row height, and the position of the text line to be on

         * the top left with a margin of 4, for example.

         * The text will be aligned on the top right or on the top left,

         * respectively.

         * For an exact vertical alignment of the text line and the Textflow

         * which will be added later note the following:

         * The height of an uppercase letter is exactly represented by the

         * capheight value of the font. For this reason use the capheight in the

         * font size specification. For example, a capheight of 8.5 will

         * approximately result in a font size of 12 points and (along with

         * "margin=4"), will sum up to an overall height of 16 points.

        */

        final String head_opts_right = "fittextline={position={right top} " +

            " font=" + boldfont + " fontsize={capheight=" + capheight + "}} " +

            " rowheight=" + rowheight + " margin=" + margin;

       

        final String head_opts_left = "fittextline={position={left top} " +

            " font=" + boldfont + " fontsize={capheight=" + capheight + "}} " +

            " rowheight=" + rowheight + " margin=" + margin;

             

        col = 1; row = 1;

             

        /* Add each heading cell with the option list defined above;

         * in addition, supply a fixed column width

         */

        tbl = p.add_table_cell(tbl, col++, row, "ITEM",

            head_opts_right + " colwidth=" + c1);

        if (tbl == -1)

            throw new Exception("Error adding cell: " + p.get_errmsg());

       

        tbl = p.add_table_cell(tbl, col++, row, "DESCRIPTION",

            head_opts_left + " colwidth=" + c1);

            if (tbl == -1)

                throw new Exception("Error adding cell: " + p.get_errmsg());

       

        tbl = p.add_table_cell(tbl, col++, row, "QUANTITY",

            head_opts_right + " colwidth=" + c3);

            if (tbl == -1)

                throw new Exception("Error adding cell: " + p.get_errmsg());

           

        tbl = p.add_table_cell(tbl, col++, row, "PRICE",

            head_opts_right + " colwidth=" + c4);

            if (tbl == -1)

                throw new Exception("Error adding cell: " + p.get_errmsg());

           

        tbl = p.add_table_cell(tbl, col++, row, "SUM",

                head_opts_right + " colwidth=" + c5);

            if (tbl == -1)

                throw new Exception("Error adding cell: " + p.get_errmsg());

           

        row++;

       

       

        /* -------------------------------------------

         * Add the body cells in subsequent table rows

         * -------------------------------------------

         */

       

        /* Prepare the general option list for adding text line cells of the

         * table body; it is similar to the option list defined for header cells

         * but the font is set to a regular font instead

         */

        final String body_opts = "fittextline={position={right top} " +

            " font=" + regularfont +

            " fontsize={capheight=" + capheight + "}} " +

            " rowheight=" + rowheight + " margin=" + margin;

       

        for (itemno = 1; itemno <= items.length; itemno++, row++) {

            col = 1;

           

            /* ---------------------------------------------------------------

             * Add the text line cell containing the Item in the first column,

             * with the options defined for table body cells above

             * ---------------------------------------------------------------

             */

            tbl = p.add_table_cell(tbl, col++, row, String.valueOf(itemno),

                body_opts);

            if (tbl == -1)

                throw new Exception("Error adding cell: " + p.get_errmsg());

           

           

            /* --------------------------------------------------------------

             * Add the Textflow cell containing the Description in the second

             * column

             * --------------------------------------------------------------

             */

                     

            /* Prepare the option list for adding the Textflow.

             * For an exact vertical alignment of the Textflow and the text

             * lines added as well note the following:

             * The height of an uppercase letter is exactly represented by the

             * capheight value of the font. For this reason use the capheight in

             * the font size specification. For example, a capheight of 8.5 will

             * approximately result in a font size of 12 points and (along with

             * "margin=4"), will sum up to an overall height of 16 points.

             */

            tf_opts = "font=" + regularfont +

                " fontsize={capheight=" + capheight + "} leading=" + leading;

           

            /* Prepare the option list for adding the Textflow cell

             *

             * The first line of the Textflow should be aligned with the

             * baseline of the text lines. At the same time, the text lines

             * should have the same distance from the top cell border as the

             * Textflow. To avoid any space from the top add the Textflow cell

             * using "fittextflow={firstlinedist=capheight}". Then add a margin

             * of 4 points, the same as for the text lines.

             */

            final String bodytf_opts = "fittextflow={firstlinedist=capheight}" +

                " colwidth=" + c2 + " margin=" + margin;

           

            /* Add the Textflow with the options defined above */

            tf = p.add_textflow(-1, items[itemno-1][0], tf_opts);

           

            if (tf == -1)

                throw new Exception("Error: " + p.get_errmsg());

               

            /* Add the Textflow table cell with the options defined above */

            tbl = p.add_table_cell(tbl, col++, row, "",

                bodytf_opts + " textflow=" + tf);

               

            if (tbl == -1)

                throw new Exception("Error adding cell: " + p.get_errmsg());

           

            tf = -1;

           

                       

            /* -----------------------------------------------------------

             * Add the text line cell containing the Quantity in the third

             * column, with the options defined for table body cells above

             * -----------------------------------------------------------

             */

            tbl = p.add_table_cell(tbl, col++, row, items[itemno-1][1],

                body_opts);

            if (tbl == -1)

                throw new Exception("Error adding cell: " + p.get_errmsg());

           

           

            /* -----------------------------------------------------------

             * Add the text line cell containing the Price in the third

             * column, with the options defined for table body cells above

             * -----------------------------------------------------------

             */

            tbl = p.add_table_cell(tbl, col++, row, items[itemno-1][2],

                body_opts);

            if (tbl == -1)

                throw new Exception("Error adding cell: " + p.get_errmsg());

           

           

            /* ---------------------------------------------------------------

             * Add the text line cell containing the sum with the options

             * defined for table body cells above. Format them to a maximum of

             * two fraction digits.

             * ---------------------------------------------------------------

             */

            sum = Double.valueOf(items[itemno-1][1]).doubleValue() *

                  Double.valueOf(items[itemno-1][2]).doubleValue();

           

            value = new BigDecimal(sum);

            roundedValue = value.setScale(2, BigDecimal.ROUND_HALF_UP);

                           

            tbl = p.add_table_cell(tbl, col, row,

                                   roundedValue.toString(), body_opts);

           

            if (tbl == -1)

                throw new Exception("Error adding cell: " + p.get_errmsg());

               

            /* Calculate the overall sum */

            total += sum;

        } /* for */

       

        /* Add an empty footer row containing a matchbox called "subtotal".

         * It will be filled with the subtotal or total later. The matchbox

         * starts in the column before last and spans two columns.

         */

        final String footer_opts =

            "rowheight=" + rowheight + " colspan=2 margin =" + margin +

            " matchbox={name=subtotal}";

       

        tbl = p.add_table_cell(tbl, maxcol-1, row, "", footer_opts + "");

                 

        if (tbl == -1)

            throw new Exception("Error adding cell: " + p.get_errmsg());

   

           

        /* ------------------------------------

         * Place the table on one or more pages

         * ------------------------------------

         */


        /* Loop until all of the table is placed; create new pages as long as

         * more table instances need to be placed

         */

        do {

            /* The first row is the header row which will be repeated on each

             * new page. The last row is the footer and will be repeated on each

             * new page. The header row is filled with a light blue, and the

             * footer row is filled with a light orange. Each odd row is filled

             * with a light gray.

             */

            final String fit_opts =

                "header=" + nheaders + " footer=" + nfooters +

                " fill={{area=rowodd fillcolor={gray 0.9}} " +

                "{area=header fillcolor={rgb 0.90 0.90 0.98}} " +

                "{area=footer fillcolor={rgb 0.98 0.92 0.84}}}";


            /* Place the table instance */

            result = p.fit_table(tbl, llx, lly, urx, y, fit_opts);

           

            /* An error occurred or the table's fitbox is too small to keep any

             * contents

             */

            if (result.equals("_error") || result.equals("_boxempty"))

                throw new Exception ("Couldn't place table : " +

                    p.get_errmsg());

           

            /* If all rows have been placed output the total in the matchbox

             * defined for the footer row. Since the matchbox cannot be supplied

             * directly to fit_textline(), we retrieve the matchbox coordinates

             * and fit the text accordingly.

             */

            if (!result.equals("_boxfull")) {

                /* Format the total to a maximum of two fraction digits */

                value = new BigDecimal(total);

                roundedValue = value.setScale(2, BigDecimal.ROUND_HALF_UP);

                String contents = "total:   " + roundedValue;

               

                /* Retrieve the coordinates of the third (upper right) corner of

                 * the "subtotal" matchbox. The parameter "1" indicates the

                 * first instance of the matchbox.

                 */

                double x3 = 0, y3 = 0;

                             

                if ((int) p.info_matchbox("subtotal", 1, "exists") == 1) {

                    x3 = p.info_matchbox("subtotal", 1, "x3");

                    y3 = p.info_matchbox("subtotal", 1, "y3");

                }

                else {

                    throw new Exception("Error: " + p.get_errmsg());

                }

               

                /* Start the text line at the corner coordinates retrieved

                 * (x2, y2) with a small margin. Right-align the text.

                 */

                p.setfont(boldfont, fontsize);

                p.fit_textline(contents, x3 - margin, y3 - margin,

                    "position={right top}");

            }

           

            /* Print the subtotal for all rows in the table instance on the

             * current page below the last table column before we place the

             * remaining rows on the next page

             */

            else if (result.equals("_boxfull")) {

                /* Get the last body row output in the table instance */

                double lastrow = p.info_table(tbl, "lastbodyrow");

               

                /* Calculate the subtotal */

                for (i = 0, subtotal = 0; i < lastrow - nfooters; i++) {

                    subtotal += Double.valueOf(items[i][1]).doubleValue() *

                        Double.valueOf(items[i][2]).doubleValue();

                }

               

                /* Output the subtotal in the matchbox defined for the footer

                 * row. Since the matchbox cannot be directly referenced we

                 * retrieve the matchbox coordinates and fit the text

                 *  accordingly.

                 */

               

                /* Format the subtotal to a maximum of two fraction digits*/

                value = new BigDecimal(subtotal);

                roundedValue = value.setScale(2, BigDecimal.ROUND_HALF_UP);

               

                String contents = "subtotal:   " + roundedValue;

               

                /* Retrieve the coordinates of the third (upper right) corner of

                 * the "subtotal" matchbox. The parameter "1" indicates the

                 * first instance of the matchbox.

                 */

                double x3 = 0, y3 = 0;

               

                if ((int) p.info_matchbox("subtotal", 1, "exists") == 1) {

                    x3 = p.info_matchbox("subtotal", 1, "x3");

                    y3 = p.info_matchbox("subtotal", 1, "y3");

                }

                else {

                    throw new Exception("Error: " + p.get_errmsg());

                }

               

                /* Start the text line at the corner coordinates retrieved in

                 * (x3, y3) with a small margin. Right-align the text.

                 */

                p.setfont(boldfont, fontsize);

                p.fit_textline(contents, x3 - margin, y3 - margin,

                    "position={right top}");

       

                /* Output the "Continued" remark */               

                p.setfont(regularfont, fontsize);

                p.fit_textline("-- Continued --", urx, ycontinued,

                    "position {right top}");

               

                p.end_page_ext("");

                p.begin_page_ext(pagewidth, pageheight, "");

                y = ystart;

            }

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

       

       

        /* -----------------------------------------------

         * Place the closing text directly after the table

         * -----------------------------------------------

         */

       

        /* Get the table height of the current table instance */

        tabheight = p.info_table(tbl, "height");

       

        y = y - (int) tabheight - yoffset;

               

        /* Add the closing Textflow to be placed after the table */

        tf_opts = "font=" + regularfont + " fontsize=" + fontsize +

            " leading=" + leading + " alignment=justify";

       

        tf = p.add_textflow(-1, closingtext, tf_opts);

        if (tf == -1)

            throw new Exception("Error: " + p.get_errmsg());

       

        /* Loop until all text has been fit which is indicated by the "_stop"

         * return value of fit_textflow()

         */

        do {

            /* Place the Textflow */

            result = p.fit_textflow(tf, llx, lly, urx, y, "");

           

            if (result.equals("_error"))

                throw new Exception ("Couldn't place table : " +

                    p.get_errmsg());

           

            if (result.equals("_boxfull") || result.equals("_boxempty")) {

                p.setfont(regularfont, fontsize);

                p.fit_textline("-- Continued --", urx, ycontinued,

                    "position {right top}");

               

                p.end_page_ext("");

                p.begin_page_ext(pagewidth, pageheight, "");

                y = ystart;

            }

        } while (!result.equals("_stop"));

       

        p.end_page_ext("");

       

        p.end_document("");

        p.close_pdi_document(stationery);


        } catch (PDFlibException e) {

            System.err.print("PDFlib exception occurred:\n");

            System.err.print("[" + e.get_errnum() + "] " + e.get_apiname() +

                ": " + e.get_errmsg() + "\n");

        } catch (Exception e) {

            System.err.println(e.toString());

        } finally {

            if (p != null) {

                p.delete();

            }

        }

    }

}