/* $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 */ 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 path_point_ops, String draw_options) throws PDFlibException { this.path_point_ops = path_point_ops; this.draw_path_options = draw_options; Iterator 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 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_cases = new ArrayList(); 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 ops = new ArrayList(); 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 ops = new ArrayList(); /* * 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 ops = new ArrayList(); /* * 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 ops = new ArrayList(); /* * 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 ops = new ArrayList(); /* * 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 ops = new ArrayList(); /* * 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 ops = new ArrayList(); 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 ops = new ArrayList(); /* * 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 ops = new ArrayList(); /* * 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_descs = new ArrayList(); /* * 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_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_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(); } }