PDFlib Cookbook

cookbook
package com.pdflib.cookbook.pdflib.graphics;

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

/**
 * $Id: clipping.java,v 1.7 2014/01/08 15:07:15 stm Exp $
 * 
 * Demonstrate the effect of the different PDF clipping rules.
 * 
 * Demonstrate how to perform "inverse" clipping, e.g. create a rectangle on a
 * page that is exempted from drawing.
 *
 * Required software: PDFlib/PDFlib+PDI/PPS 9
 * Required data: none
 */
public class clipping {

    /**
     * Create a path in the shape of a pentagram to have a shape that intersects
     * itself.
     * 
     * @param p
     *            the pdflib object
     * @param length
     *            the length of a vertex of the pentagram
     * 
     * @return path handle
     * 
     * @throws PDFlibException
     */
    private static int pentagram_path(pdflib p, double length)
        throws PDFlibException {

        /* The angle for which to turn at each point to form the star */
        final double angle = 144;

        /*
         * Initial angle so the pentagram is constructed with the first point
         * at the top.
         */
        final double initial_angle = -3 * angle / 4;

        /* 
         * Construct the pentagram with polar coordinates. The path will
         * be closed automatically when using it as a clipping path.
         */
        int i;
        int path = -1;
        for (i = 0; i < 4; i += 1) {
            path = p.add_path_point(path, length, initial_angle + angle * i,
                "line", "polar=true relative=true");
        }

        return path;
    }

    /**
     * Create a path that consists of two nested circles that are either
     * oriented in the same direction or in opposite directions.
     * 
     * @param p
     *            the pdflib object
     * @param outer_radius
     *            radius of outer circle
     * @param inner_radius
     *            radius of inner circle
     * @param same_direction
     *            whether to orient the circles in the same direction or in
     *            opposite directions
     * @return path handle
     * 
     * @throws PDFlibException
     */
    private static int nested_circles_path(pdflib p, double outer_radius,
        double inner_radius, boolean same_direction) throws PDFlibException {

        /*
         * The circle is constructed of two half-circles. Type "elliptical" is
         * used insted of type "circle" or "circular" because it allows to use
         * the "clockwise" option to specify the direction.
         * 
         * Outer circle is always oriented clockwise
         */
        String outer_direction = "clockwise=true";
        int path = p.add_path_point(-1, -outer_radius, 0, "move", "");
        p.add_path_point(path, outer_radius, 0, "elliptical", "radius="
            + outer_radius + " " + outer_direction);
        p.add_path_point(path, -outer_radius, 0, "elliptical", "radius="
            + outer_radius + " " + outer_direction);

        /*
         * Inner circle is oriented in the same or opposite direction, depending
         * on "same_direction" parameter.
         */
        String inner_direction = "clockwise="
            + (same_direction ? "true" : "false");
        p.add_path_point(path, -inner_radius, 0, "move", "");
        p.add_path_point(path, inner_radius, 0, "elliptical", "radius="
            + inner_radius + " " + inner_direction);
        p.add_path_point(path, -inner_radius, 0, "elliptical", "radius="
            + inner_radius + " " + inner_direction);

        return path;
    }

    /**
     * Draw the clipped shapes into the specified box while using the specified
     * clip rule.
     * 
     * @param p
     *            the pdflib object
     * @param cliprule
     *            the clip rule to use
     * @param font
     *            font for texts
     * @param ypos
     *            vertical position of box on page
     * @param boxheight
     *            height of box
     * @param boxwidth
     *            width of box
     * @param unit
     *            unit to distribute the three shapes across the page
     *            horizontally
     * @param penta
     *            pentagram path
     * @param nested_circles_same_dir
     *            path containing nested circles oriented in same direction
     * @param nested_circles_different_dir
     *            path containing nested circles oriented in opposite direction
     * 
     * @throws PDFlibException
     */
    private static void clipped_shapes(pdflib p, String cliprule, int font,
        double ypos, double boxwidth, double boxheight, double unit, int penta,
        int nested_circles_same_dir, int nested_circles_different_dir)
        throws PDFlibException {

        /* Save graphics state before translation */
        p.save();

        /* Position box vertically on page */
        p.translate(0, ypos);

        /* Step that is incremented to position the shapes horizontally */
        double step = unit;

        /* Dimensions of text box for displaying clip rule */
        double textbox_height = boxheight / 3.0;
        double text_lly = boxheight * 2.0 / 3.0;

        p.fit_textline("cliprule=" + cliprule, 0, text_lly,
            "position=center font=" + font + " fontsize=20 " + "boxsize={"
                + boxwidth + " " + textbox_height + "}");

        /* Dimensions of caption for each shape */
        double caption_lly = boxheight / 2.0;
        double caption_fontsize = 10;
        double caption_height = 3 * caption_fontsize;

        /* Caption for pentagram */
        String optlist = "alignment=center font=" + font + " fontsize="
            + caption_fontsize;
        int tf = p.create_textflow("Self-intersecting pentagram", optlist);
        p.fit_textflow(tf, step, caption_lly, step + unit, caption_lly
            + caption_height, "");

        /* Save graphics state before setting the clipping path */
        p.save();

        /* Use pentagram path as clipping path with specified clip rule */
        p.draw_path(penta, step + unit / 2, 2 * unit, "clip cliprule="
            + cliprule);

        /*
         * Fill a rectangle that has the size of the box with blue, affected by
         * the clipping path.
         */
        p.set_graphics_option("fillcolor=blue");
        p.rect(0, 0, boxwidth, boxheight);
        p.fill();

        /* Restore graphics state without clipping path */
        p.restore();

        p.delete_textflow(tf);

        step += 2 * unit;

        tf = p.create_textflow("Nested circles, same direction", optlist);
        p.fit_textflow(tf, step, caption_lly, step + unit, caption_lly
            + caption_height, "");

        /*
         * Same steps as above to demonstrate the effect of nested circles with
         * same orientation to set the clipping path with the specified clip
         * rule.
         */
        p.save();
        p.draw_path(nested_circles_same_dir, step + unit / 2, 1.5 * unit,
            "clip cliprule=" + cliprule);
        p.set_graphics_option("fillcolor=blue");
        p.rect(0, 0, boxwidth, boxheight);
        p.fill();
        p.restore();

        p.delete_textflow(tf);

        step += 2 * unit;

        tf = p.create_textflow("Nested circles, different direction", optlist);
        p.fit_textflow(tf, step, caption_lly, step + unit, caption_lly
            + caption_height, "");

        /*
         * Same steps as above to demonstrate the effect of nested circles with
         * opposite orientation to set the clipping path with the specified clip
         * rule.
         */
        p.save();
        p.draw_path(nested_circles_different_dir, step + unit / 2, 1.5 * unit,
            "clip cliprule=" + cliprule);
        p.set_graphics_option("fillcolor=blue");
        p.rect(0, 0, boxwidth, boxheight);
        p.fill();
        p.restore();

        p.delete_textflow(tf);

        /* Restore graphics state before translate */
        p.restore();
    }

    /**
     * Demonstrate the effects of the "Nonzero Winding Number" and "Even-Odd"
     * clipping rules.
     * 
     * @param p
     *            the pdflib object
     * @param font
     *            font handle for texts
     * @param pagewidth
     *            width of page
     * @param pageheight
     *            height of page
     * 
     * @throws PDFlibException
     */
    private static void clippingrules(pdflib p, int font, double pagewidth,
        double pageheight) throws PDFlibException {

        /*
         * Divide the page horizontally into seven parts to place the three
         * shapes evenly spaced.
         */
        final double unit = pagewidth / 7;

        /* Path for pentagram */
        int penta = pentagram_path(p, unit);

        /* Path for nested circles oriented in same direction */
        int nested_circles_same_dir = nested_circles_path(p, unit / 2,
            unit / 4, true);

        /* Path for nested circles oriented in opposite direction */
        int nested_circles_different_dir = nested_circles_path(p, unit / 2,
            unit / 4, false);

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

        /* Define dimension of box that covers half the page vertically */
        double boxheight = pageheight / 2;
        double boxwidth = pagewidth;

        /* Y position of upper box on page */
        double ypos = pageheight / 2;

        /* Use paths for clipping with "Nonzero Winding Number Rule" */
        clipped_shapes(p, "winding", font, ypos, boxwidth, boxheight, unit,
            penta, nested_circles_same_dir, nested_circles_different_dir);

        /* Y position of lower box on page */
        ypos = 0;

        /* Use paths for clipping with "Even-Odd Rule" */
        clipped_shapes(p, "evenodd", font, ypos, boxwidth, boxheight, unit,
            penta, nested_circles_same_dir, nested_circles_different_dir);

        /* Clean up path handles */
        p.delete_path(penta);
        p.delete_path(nested_circles_same_dir);
        p.delete_path(nested_circles_different_dir);

        p.end_page_ext("");
    }

    /**
     * Demonstrate "inverse" clipping: Define rectangles where the interior
     * is not painted when performing graphics operations.
     * 
     * @param p
     *            the pdflib object
     * @param font
     *            font handle for texts
     * @param pagewidth
     *            width of page
     * @param pageheight
     *            height of page
     * 
     * @throws PDFlibException
     */
    private static void inverse_clipping(pdflib p, int font, double pagewidth,
        double pageheight) throws PDFlibException {
        
        p.begin_page_ext(pagewidth, pageheight, "");
        
        /* Define position and dimensions of "inverse" clip rectangle */
        double clip_llx = pagewidth / 4;
        double clip_lly = pageheight / 4;
        double clip_width = pagewidth / 2;
        double clip_height = pageheight / 4;
        
        /* Create explanation */
        String optlist = "alignment=center font=" + font + " fontsize=20";
        int tf = p.create_textflow(
            "This rectangle is exempted from drawing by clipping", optlist);
        
        /*
         * Fit the explanation in the rectangle that later will be exempted
         * from drawing by clipping.
         */
        p.fit_textflow(tf, clip_llx, clip_lly,
            clip_llx + clip_width, clip_lly + clip_height,
            "matchbox={margin=10} verticalalign=center");
        
        p.delete_textflow(tf);
        
        /*
         * We want to exempt the inner rectangle from drawing, when the
         * nested rectangles are drawn in the same direction.
         */
        p.set_graphics_option("cliprule=evenodd");
        
        /* 
         * To exempt the inner of a rectangle from painting on the page,
         * first the whole page must be set as a clipping rectangle.
         */
        p.rect(0, 0, pagewidth, pageheight);
        
        /*
         * Now define the smaller rectangle that shall be exempted from
         * drawing.
         */
        p.rect(clip_llx, clip_lly, clip_width, clip_height);
        
        /* Use the nested rectangles as clipping path */
        p.clip();
        
        /* Create a textflow that shall be used to fill the page */
        final String text =
            "Lorem ipsum dolor sit amet, consectetur adipisicing elit, " +
            "sed do eiusmod tempor incididunt ut labore et dolore magna " +
            "aliqua. Ut enim ad minim veniam, quis nostrud exercitation " +
            "ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
            "Duis aute irure dolor in reprehenderit in voluptate velit " +
            "esse cillum dolore eu fugiat nulla pariatur. Excepteur sint " +
            "occaecat cupidatat non proident, sunt in culpa qui officia " +
            "deserunt mollit anim id est laborum. ";
        optlist = "font=" + font +
                    " fontsize=7 alignment=justify fillcolor=blue";
        tf = -1;
        int i;
        for (i = 0; i < 60; i += 1)
        {
            tf = p.add_textflow(tf, text, optlist);
        }
        
        /* Fill whole page with text while clipping is in effect */
        p.fit_textflow(tf, 0, 0, pagewidth, pageheight, "");
        
        p.delete_textflow(tf);
        
        p.end_page_ext("");
    }
    
    public static void main(String argv[]) {

        final String outfile = "clipping.pdf";
        final String title = "Clipping Path Variants";

        final double pageheight = 852;
        final double pagewidth = 595;

        pdflib p = null;

        try {
            p = new pdflib();
            
            /* 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.7 $");
            
            int font = p.load_font("Helvetica", "unicode", "");
            if (font == -1)
                throw new Exception("Error: " + p.get_apiname() + ": "
                        + p.get_errmsg());
            
            clippingrules(p, font, pagewidth, pageheight);
            
            inverse_clipping(p, font, pagewidth, pageheight);

            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();
            }
        }
    }
}