PDFlib

高度なPDFアプリケーションの開発を支援する定番プログラムライブラリー Supported by インフォテック株式会社

PDFlib サンプル集(クックブック)

本サンプルプログラムは、PDF 文書生成ライブラリーの実装である PDFlib の基本的な機能を実際のプログラムで紹介したものです。

本サイトでダウンロードした PDFlib は、一部機能の制限を除き、評価版として無償でお使いいただけます。

PDFlib ブロックの基礎

PDFlib ブロックを含む PDF ページをインポートし、すべてのブロックに処理を行います。PDFlib ブロックは pCOS 経由で取得され、ブロック配置関数は出力ページ上にブロックを視覚できるようにします。実用例としては、ブロックに外部のデータソースから取得したデータを配置するような場合が考えられます。


/*
 * Block starter:
 * Import a PDF page containing Blocks and fill text and image
 * Blocks with some data. For each recipient of the simulated
 * mailing a separate page with personalized information is
 * generated.
 *
 * The Textflow Blocks are processed with variable text lengths in mind:
 * if a Block doesn't fully use its vertical space or requires excess
 * space, the next Block is moved up or down accordingly.
 *
 * Required software: PPS 10
 * Required data: Block PDF (template), images, font
 */

package com.pdflib.cookbook.pdflib.blocks;

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

public class starter_block {
    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 = "starter_block.pdf";
        String infile = "block_template.pdf";
        String title = "Starter_block";

        pdflib p = null;
        int exitcode = 0;
        
        class Block {               // Description of a single text or image Block
            Block(String blockname, String contents[]) {
                this.name = blockname;
                this.contents = contents;
            }
            String name;            // Block name
            String contents[];      // text Block: array with a string for each recipient
                                    // image Block: array with image file name for each recipient
        };
        
        final int number_of_recipients = 3;

        // Names and contents of text Blocks
        Block TextBlocks[] = {
            new Block("recipient",
                new String[] {          // address data for each recipient
                    "Mr Maurizio Moroni\nStrada Provinciale 124\nReggio Emilia",

                    "Ms Dominique Perrier\n25, Rue Lauriston\nParis",

                    "Mr Liu Wong\n55 Grizzly Peak Rd.\nButte"
                }),

            new Block("announcement",
                new String[] {          // greeting for each recipient
                    "Dear Paper Planes Fan,\n\n" +
                    "we are happy to present our <fillcolor=red>best price offer" +
                    "<fillcolor=black> especially for you:\n",
                    
                    "Bonjour,\n\n" +
                    "here comes your personal <fillcolor=red>best price offer:\n",
                    
                    "Dear Paper Folder,\n\n" +
                    "here's another exciting new paper plane especially for you. " +
                    "We can supply this <fillcolor=red>best price offer" +
                    "<fillcolor=black> only for a limited time and in limited quantity. " +
                    "Don't hesitate and order your new plane today!\n",
                }),
            
            new Block("specialoffers",
                new String[] {          // personalized offer for each recipient
                    "<fillcolor=red>Long Distance Glider<fillcolor=black>\n" +
                    "With this paper rocket you can send all your " +
                    "messages even when sitting in a hall or in the cinema pretty near " +
                    "the back.\n",
                    
                    "<fillcolor=red>Giant Wing<fillcolor=black>\n" +
                    "An unbelievable sailplane! It is amazingly robust and " +
                    "can even do aerobatics. But it is best suited to gliding.\n" +
                    "This paper arrow can be thrown with big swing. " +
                    "We launched it from the roof of a hotel. It stayed in the air a " +
                    "long time and covered a considerable distance.\n",
                    
                    "<fillcolor=red>Super Dart<fillcolor=black>\n" +
                    "The super dart can fly giant loops with a radius of 4 " +
                    "or 5 meters and cover very long distances. Its heavy cone point is " +
                    "slightly bowed upwards to get the lift required for loops.\n"
                }),            

            new Block("goodbye",
                new String[] {          // bye bye phrase for each recipient
                    "Visit us on our Web site at <fillcolor=blue>www.kraxi.com<fillcolor=black>!\n\n" +
                    "Yours sincerely,\nVictor Kraxi",
                
                    "Make sure to order quickly at <fillcolor=blue>www.kraxi.com<fillcolor=black> " +
                    "since we will soon run out of stock!\n\n" +
                    "Yours sincerely,\nVictor Kraxi",

                    "Make sure to order soon at <fillcolor=blue>www.kraxi.com<fillcolor=black>!\n\n" +
                    "Your friendly staff at Kraxi Systems Paper Planes",
                })
            };

        // Names and contents of image Block(s)
        Block ImageBlocks[] = {
            new Block("icon",
                new String[] {          // image file name for each recipient
                    "plane1.png",
                    "plane2.png",
                    "plane3.png"
                })
        };
        
        try {
            p = new pdflib();
            int no_of_input_pages, indoc;

            // 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 + "}}");

            if (p.begin_document(outfile,
                "destination={type=fitwindow} pagelayout=singlepage") == -1) {
                throw new Exception("Error: " + p.get_errmsg());
            }

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

            // Open the Block template with PDFlib Blocks
            indoc = p.open_pdi_document(infile, "");
            if (indoc == -1) {
                throw new Exception("Error: " + p.get_errmsg());
            }
            no_of_input_pages = (int) p.pcos_get_number(indoc, "length:pages");

            // Preload all pages of the Block template. We assume a small
            // number of input pages and a large number of generated output
            // pages. Therefore it makes sense to keep the input pages
            // open instead of opening them again for each recipient.
            // Add 1 because we use the 1-based page number as array index.
            
            int[] pagehandles = new int[no_of_input_pages+1]; 

            for (int pageno = 1; pageno <= no_of_input_pages; pageno++){
                // Open template page and prepare it for cloning the page size
                pagehandles[pageno] = p.open_pdi_page(indoc, pageno, "cloneboxes");

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

            // For each recipient: place template page(s) and fill Blocks
            for (int recipient = 0; recipient < number_of_recipients; recipient++) {

                // Process all pages of the template document
                for (int pageno = 1; pageno <= no_of_input_pages; pageno++) {
                    // Accumulated unused or excess space in Textflow Blocks:
                    // - if positive, the previous Blocks didn't use their space, so
                    //   we move up the Block
                    // - if negative, the previous Blocks used excess space, so
                    //   we move down the Block
                    double accumulated_offset_y = 0;

                    // Start the next output page. The page size will be
                    // replaced with the cloned size of the input page.
                    p.begin_page_ext(0, 0, "width=a4.width height=a4.height");

                    // Place the imported page on the output page, and clone all
                    // page boxes which are present in the input page; this will
                    // override the dummy size used in begin_page_ext().
                    p.fit_pdi_page(pagehandles[pageno], 0, 0, "cloneboxes");

                    // Process all Textflow Blocks
                    for (Block textblock: TextBlocks) {
                        // The Textflow Blocks in the template use "fitmethod=nofit"
                        // which means we allow the Textflow to overflow the Block.
                        String optlist = "";

                        // pCOS path for the current Block 
                        String blockpath = "pages[" + (pageno-1) + "]/blocks/" + textblock.name;

                        // Retrieve y coordinate of the Block's lower left corner
                        double lly = p.pcos_get_number(indoc, blockpath + "/Rect[1]");

                        // Adjust the vertical Block position by accumulated offset
                        // and make sure we don't fall off the bottom page edge.
                        // Similarly we could adjust the horizontal position.
                        double adjusted_lly = lly + accumulated_offset_y;
                        if (adjusted_lly < 0) {
                            throw new Exception("Error for recipient " + recipient + " (input page " + pageno + "): " +
                                "Too much text in Textflow Blocks");
                        }

                        // The "refpoint" option overrides the Blocks's original
                        // position. We use relative coordinates (suffix "r") to move
                        // the Block up or down if the previous Blocks didn't use up
                        // their area or exceeded the Block area.
                        optlist += " refpoint={ 0r " + accumulated_offset_y +"r }";

                        // Create a matchbox for the Block contents, using the Block name as matchbox name
                        optlist += " matchbox={name={" + textblock.name + "}}";
                        
                        // Fill text Block
                        if (p.fill_textblock(pagehandles[pageno], textblock.name,
                            textblock.contents[recipient], optlist) == -1) {
                            System.err.println("Warning: " + p.get_errmsg());
                        } else {
                        	// Calculate the height which wasn't used inside the Block
                        	// or was used in excess outside the Block. This will be used
                        	// for adjusting the position of the next Block.
                        	
                        	if ((int) p.info_matchbox(textblock.name, 1, "exists") == 1) {
	                            // We successfully filled a Textflow Block. Retrieve the bottom edge
	                            // of the matchbox which gives the vertical end position of the contents...
	                            double content_lly = p.info_matchbox(textblock.name, 1, "y1");		// (x1, y1) = lower left corner

	                            // ...and use the distance from the bottom edge of the Block as offset
	                            accumulated_offset_y += (content_lly - adjusted_lly);
                        	} else {
                        		// If the Block is empty, no corresponding matchbox exists.
                        		// We use the Block height as offset to skip the whole Block,
                        		// not taking into account possible space between the Blocks.
                                double ury = p.pcos_get_number(indoc, blockpath + "/Rect[3]");
                                accumulated_offset_y += (ury - lly);
                        	}
                        }
                    }

                    // Process all image Blocks
                    for (Block imageblock: ImageBlocks) {
                        int image = p.load_image("auto", imageblock.contents[recipient], "");
                        if (image == -1) {
                            throw new Exception("Error: " + p.get_errmsg());
                        }
    
                        // Fill image Block
                        if (p.fill_imageblock(pagehandles[pageno], imageblock.name, image, "") == -1)
                            System.err.println("Warning: " + p.get_errmsg());
                        p.close_image(image);
                    }

                    p.end_page_ext("");
                }
            }

            // Close the preloaded template pages
            for (int pageno = 1; pageno <= no_of_input_pages; pageno++) {
                p.close_pdi_page(pagehandles[pageno]);
            }
            p.close_pdi_document(indoc);

            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);
        }
    }
}
(Dec 11, 2012 - Jun 24, 2024)