PDFlib Cookbook

cookbook

graphics/path_objects

Download Java Code     Switch to PHP Code      Show Output PDF

/* $Id: path_objects.java,v 1.15 2013/02/26 11:10:24 stm Exp $
 * 
 * Use path objects:
 * Create various shapes with path objects
 * 
 * Build a table that shows the shapes together with the corresponding 
 * add_path_point() and draw_point() calls.
 * 
 * Required software: PDFlib/PDFlib+PDI/PPS 9
 * Required data: none
 */
package com.pdflib.cookbook.pdflib.graphics;

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

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

public class path_objects {

    /**
     * Margin to add around the path in a table cell
     */
    private static final double PATH_BOX_MARGIN = 0.1;
    
    /**
     * Interface for the different use cases.
     */
    interface use_case {
        path_desc create_example_path_desc(pdflib p) throws PDFlibException;
        String use_case_description();
    }
    
    /**
     * Class for describing path construction operations.
     */
    class add_path_point_op {
        add_path_point_op(double x, double y, String keyword, String options) {
            this.x = x;
            this.y = y;
            this.keyword = keyword;
            this.options = options;
        }
        double x, y;
        String keyword;
        String options;
    }
    
    /**
     * Class that encapsulates a path handle and the operations that it
     * created.
     */
    class path_desc {
        path_desc(pdflib p, List<add_path_point_op> path_point_ops, String draw_options)
        		throws PDFlibException {
            this.path_point_ops = path_point_ops;
            this.draw_path_options = draw_options;
            Iterator<add_path_point_op> path_desc_iterator = path_point_ops.iterator();
            
            NumberFormat form = NumberFormat.getInstance(Locale.US);
            form.setMinimumFractionDigits(0);
            form.setMaximumFractionDigits(2);
            
            final String tf_options =
            	"fontname=Helvetica encoding=unicode fontsize=10";

            path = -1;
            textflow = -1;
            while (path_desc_iterator.hasNext()) {
                add_path_point_op op = path_desc_iterator.next();
                path = p.add_path_point(path, op.x, op.y, op.keyword, 
                                        op.options);
                
                final String op_text = "add_path_point(path, "
                    + form.format(op.x) + ", " + form.format(op.y) + ", \"" 
                    + op.keyword + "\", \"" + op.options + "\")\n";
		textflow = p.add_textflow(textflow, op_text, tf_options);
            }
            textflow = p.add_textflow(textflow, 
    			"draw_path(path, x, y, \"" + draw_options + "\")\n",
    			tf_options);
        }
        
        /**
         * List of path point operations
         */
        List<add_path_point_op> path_point_ops;
        
        /**
         * PDFlib path handle
         */
        int path;
        
        /**
         * PDFlib textflow handle
         */
        int textflow;
        
        /**
         * Option list for the draw_path() operation
         */
        String draw_path_options;
    }
    
    /**
     * Execute the example.
     */
    public void run() {
        /* This is where the data files are. Adjust as necessary. */
        final String searchpath = "../input";
        final String outfile = "path_objects.pdf";
        final String title = "Path Objects";
        pdflib p = null;
        
        /* The page dimensions */
        final double a4_width = 595, a4_height = 842;
        
        /* Parameters for placing the table containing the path examples */
        final double margin = 50;
        final double tbl_llx = margin, tbl_lly = margin, 
            tbl_urx = a4_width - margin, tbl_ury = a4_height - margin;

        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_apiname() + ": "
                        + p.get_errmsg());

            p.set_info("Creator", "PDFlib Cookbook");
            p.set_info("Title", title + " $Revision: 1.15 $");

            final int font = p.load_font("Helvetica", "unicode", "");
            if (font == -1)
                throw new Exception("Error: " + p.get_apiname() + ": "
                        + p.get_errmsg());

            int tbl = -1;
            
            List<use_case> use_cases = new ArrayList<use_case>();
            
            use_cases.add(new use_case() {
                public String use_case_description() {
                    return "Circle";
                }

                public path_desc create_example_path_desc(pdflib p) 
                        throws PDFlibException {
                	final double radius = 50;
                	final double x = 50, y = 50;
                	
                    List<add_path_point_op> ops = new ArrayList<add_path_point_op>();
                    ops.add(new add_path_point_op(x - radius, y, "move", ""));
                    ops.add(new add_path_point_op(x + radius, y, "control", ""));
                    ops.add(new add_path_point_op(x - radius, y, "circular", ""));
                    
                    return new path_desc(p, ops, "stroke");
                }
            });
            
            use_cases.add(new use_case() {
                public String use_case_description() {
                    return "Rectangle";
                }

                public path_desc create_example_path_desc(pdflib p) 
                        throws PDFlibException {
                    final double rect_height = 50, rect_width = 100;
                    
                    List<add_path_point_op> ops = new ArrayList<add_path_point_op>();
                    
                    /*
                     * Build the rectangle. It implicitly starts at (0, 0).
                     */
                    ops.add(new add_path_point_op(0, rect_height, 
                                                "line", ""));
                    ops.add(new add_path_point_op(rect_width, 
                                                rect_height, "line", ""));
                    ops.add(new add_path_point_op(rect_width, 0, 
                                                "line", ""));
                    
                    return new path_desc(p, ops, "stroke close");
                }
            });
            
            
            use_cases.add(new use_case() {
                public String use_case_description() {
                    return "Rectangle With Inbound Rounded Corners";
                }

                public path_desc create_example_path_desc(pdflib p) 
                        throws PDFlibException {
                    final double rect_height = 50, rect_width = 100;
                    final double line_width = 2;
                    final double round_radius = -5;
                    
                    List<add_path_point_op> ops = new ArrayList<add_path_point_op>();
                    
                    /*
                     * Build the rectangle. It implicitly starts at (0, 0).
                     */
                    ops.add(new add_path_point_op(0, rect_height, 
                                                "line", ""));
                    ops.add(new add_path_point_op(rect_width, 
                                                rect_height, "line", ""));
                    ops.add(new add_path_point_op(rect_width, 0, 
                                                "line", ""));
                    
                    return new path_desc(p, ops, 
                    		"stroke close"
                    		+ " round=" + round_radius
                    		+ " linewidth=" + line_width);
                }
            });
            
            use_cases.add(new use_case() {
                public String use_case_description() {
                    return "Triangle";
                }

                public path_desc create_example_path_desc(pdflib p) 
                        throws PDFlibException {
                    final double edge_length = 100;
                    final double height =
                        edge_length * Math.sin(Math.toRadians(60));
                    
                    List<add_path_point_op> ops = new ArrayList<add_path_point_op>();
                    
                    /*
                     * Build the rectangle. It implicitly starts at (0, 0).
                     */
                    ops.add(new add_path_point_op(edge_length / 2, 
                                                height, "line", ""));
                    ops.add(new add_path_point_op(edge_length, 0,
                                                "line", ""));
                    
                    return new path_desc(p, ops, "stroke close");
                }
            });
            
            use_cases.add(new use_case() {
                public String use_case_description() {
                    return "Triangle With Rounded Corners";
                }

                public path_desc create_example_path_desc(pdflib p) 
                        throws PDFlibException {
                    final double edge_length = 100;
                    final double height =
                        edge_length * Math.sin(Math.toRadians(60));
                    final double line_width = 10;
                    final double round_radius = 10;
                    
                    List<add_path_point_op> ops = new ArrayList<add_path_point_op>();
                    
                    /*
                     * Build the rectangle. It implicitly starts at (0, 0).
                     */
                    ops.add(new add_path_point_op(edge_length / 2, 
                                                height, "line", ""));
                    ops.add(new add_path_point_op(edge_length, 0,
                                                "line", ""));
                    
                    return new path_desc(p, ops, 
                		"stroke close strokecolor=red"
                		+ " round=" + round_radius 
                		+ " linewidth=" + line_width);
                }
            });
            
            use_cases.add(new use_case() {
                public String use_case_description() {
                    return "B\u00e9zier Segment With Four Control Points";
                }

                public path_desc create_example_path_desc(pdflib p) 
                        throws PDFlibException {
                    List<add_path_point_op> ops = new ArrayList<add_path_point_op>();
                    
                    /*
                     * Specify the control points. The first control point 
                     * (0, 0) is implicit.
                     */
                    ops.add(new add_path_point_op(100, 50, "control", ""));
                    ops.add(new add_path_point_op(100, 100, "control", ""));
                    ops.add(new add_path_point_op(0, 100, "curve", ""));
                    
                    return new path_desc(p, ops, "stroke");
                }
            });
            
            use_cases.add(new use_case() {
                public String use_case_description() {
                    return "Two B\u00e9zier Segments Joined Automatically";
                }

                public path_desc create_example_path_desc(pdflib p) 
                        throws PDFlibException {
                    List<add_path_point_op> ops = new ArrayList<add_path_point_op>();
                    final double y_ctrl_delta = 10;
                    final double x_ctrl_width = 100;
                    
                    /*
                     * Specify the control points. The first control point 
                     * (0, 0) is implicit.
                     */
                    ops.add(new add_path_point_op(
                    		x_ctrl_width, 1 * y_ctrl_delta, "control", ""));
                    ops.add(new add_path_point_op(
                    		x_ctrl_width, 2 * y_ctrl_delta, "control", ""));
                    ops.add(new add_path_point_op(
                    		x_ctrl_width / 2, 3 * y_ctrl_delta, "curve", ""));
                    ops.add(new add_path_point_op(
                    		0, 4 * y_ctrl_delta, "control", ""));
                    ops.add(new add_path_point_op(
                    		0, 5 * y_ctrl_delta, "control", ""));
                    ops.add(new add_path_point_op(
                    		x_ctrl_width, 6 * y_ctrl_delta, "curve", ""));
                    
                    return new path_desc(p, ops, "stroke");
                }
            });            
            
            use_cases.add(new use_case() {
                public String use_case_description() {
                    return "Circular Arc Segment with Cartesian Coordinates";
                }

                public path_desc create_example_path_desc(pdflib p) 
                        throws PDFlibException {
                    final double radius = 100;
                    final double angle = Math.toRadians(45);
                    final double control_1_x = radius * Math.cos(angle);
                    final double control_1_y = radius * Math.sin(angle);
                    final double control_2_x = radius * Math.cos(angle / 2);
                    final double control_2_y = radius * Math.sin(angle / 2);
                    final double control_3_x = radius * Math.cos(0);
                    final double control_3_y = radius * Math.sin(0);
                    
                    List<add_path_point_op> ops = new ArrayList<add_path_point_op>();
                    
                    /*
                     * Build the arc segment. It implicitly starts at (0, 0).
                     */
                    ops.add(new add_path_point_op(control_1_x,
                    				control_1_y, "line", ""));
                    ops.add(new add_path_point_op(control_2_x, 
                    				control_2_y, "control", ""));
                    ops.add(new add_path_point_op(control_3_x, 
    						control_3_y, "circular", ""));
                    ops.add(new add_path_point_op(0, 0, "line", ""));
                    
                    return new path_desc(p, ops, "stroke fill");
                }
            });

            use_cases.add(new use_case() {
                public String use_case_description() {
                    return "Circular Arc Segment with Polar Coordinates";
                }

                /* Arc segments can be expressed much easier with
                 * Polar Coordinates.
                 */
                public path_desc create_example_path_desc(pdflib p) 
                        throws PDFlibException {
                    final double radius = 100;
                    final double angle = 45;
                    
                    List<add_path_point_op> ops = new ArrayList<add_path_point_op>();
                    
                    /*
                     * Build the arc segment. It implicitly starts at (0, 0).
                     */
                    ops.add(new add_path_point_op(radius, angle, "line", 
                                                    "polar=true"));
                    ops.add(new add_path_point_op(radius, angle / 2, "control", 
                                                    "polar=true"));
                    ops.add(new add_path_point_op(radius, 0, "circular",
                                                    "polar=true"));
                    ops.add(new add_path_point_op(0, 0, "line", "polar=true"));
                    
                    return new path_desc(p, ops, "stroke fill");
                }
            });

            
            /*
             * List of path descriptions lists.
             */
            List<path_desc> path_descs = new ArrayList<path_desc>();
            
            /*
             * Loop over the list of use cases, and for each create a table
             * cell with a descriptive header, and fit the path into the next
             * cell.
             */
            Iterator<use_case> use_case_iterator = use_cases.iterator();
            for (int i = 0; use_case_iterator.hasNext(); i += 1) {
                final int row = i * 2 + 1;
                
                final use_case c = use_case_iterator.next();
                
                final String row_group = "row_" + i;
                
                final String optlist = 
                    "fittextline={position=center font=" + font 
                    + " fontsize=14} colspan=2 rowjoingroup=" + row_group;

                tbl = p.add_table_cell(tbl, 1, row, c.use_case_description(),
                        optlist);
                if (tbl == -1)
                    throw new Exception("Error: " + p.get_apiname() + ": "
                            + p.get_errmsg());
                
                final path_desc desc = c.create_example_path_desc(p);
                
                /*
                 * Remember path description for later deletion of path and
                 * textflow handles.
                 */
                path_descs.add(desc);
                
                /*
                 * Determine the height of the path object, and add a margin
                 * of 10% at top and bottom to calculate the height of the cell.
                 */
                double row_height = p.info_path(desc.path, "height", "")
                                            * (1 + 2 * PATH_BOX_MARGIN);
                
                /*
                 * Add a cell with the textflow that describe the operations
                 * that created the path.
                 */
                p.add_table_cell(tbl, 1, row + 1, "", 
                            "margin=5% textflow=" + desc.textflow 
                            + " colwidth=65% rowjoingroup=" + row_group);
                
                /*
                 * Add the path to a table cell.
                 */
                p.add_table_cell(tbl, 2, row + 1, "", 
                        "path=" + desc.path 
                        + " fitpath={" + desc.draw_path_options 
                        + " fitmethod=nofit}"
                        + " rowheight=" + row_height
                        + " colwidth=35%"
                        + " rowjoingroup=" + row_group);
            }
            
            /*
             * Place the table.
             */
            String result;
            do {
                p.begin_page_ext(a4_width, a4_height, "");

                /*
                 * Options for the per-example header line
                 */
                final String optlist = 
                    "fill={{area=rowodd fillcolor={gray 0.9}}} "
                        + "stroke={{line=other}} ";

                /* Place the table instance */
                result = p.fit_table(tbl, tbl_llx, tbl_lly, tbl_urx, tbl_ury,
                                    optlist);

                if (result.equals("_error"))
                    throw new Exception("Couldn't place table : "
                            + p.get_errmsg());
                
                p.end_page_ext("");
            }
            while (result.equals("_boxfull"));
            
            /*
             * Delete the open handles.
             */
            Iterator<path_desc> path_descs_iterator = path_descs.iterator();
            while (path_descs_iterator.hasNext()) {
                path_desc desc = path_descs_iterator.next();
                
                p.delete_path(desc.path);
                p.delete_textflow(desc.textflow);
            }
            
            p.end_document("");
        }
        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.getMessage());
        }
        finally {
            if (p != null) {
                p.delete();
            }
        }
    }
    
    public static void main(String[] args) {
        new path_objects().run();
    }
}