PDFlib Cookbook

cookbook

path_objects/clipping

Demonstrate the effect of different PDF clipping rules.

Download PHP Code  Switch to Java Code  Show Output 

<?php
/**
 *
 * 
 * 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
 */

/**
 * Create a path in the shape of a pentagram to have a shape that intersects
 * itself.
 */
function pentagram_path($p, $length) {
    
    /* The angle for which to turn at each po$to form the star */
    $angle = 144;
    
    /*
     * Initial angle so the pentagram is constructed with the first point at the
     * top.
     */
    $initial_angle = - 3 * $angle / 4;
    
    /*
     * Construct the pentagram with polar coordinates. The path will be closed
     * automatically when using it as a clipping path.
     */
    $i;
    $path = 0;
    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.
 */
function nested_circles_path($p, $outer_radius, $inner_radius, $same_direction) {
    
    /*
     * 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
     */
    $outer_direction = "clockwise=true";
    $path = $p->add_path_point(0, - $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.
     */
    $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.
 */
function clipped_shapes($p, $cliprule, $font, $ypos, $boxwidth, $boxheight, 
                $unit, $penta, $nested_circles_same_dir, 
                $nested_circles_different_dir) {
    
    /* 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 */
    $step = $unit;
    
    /* Dimensions of text box for displaying clip rule */
    $textbox_height = $boxheight / 3.0;
    $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 */
    $caption_lly = $boxheight / 2.0;
    $caption_fontsize = 10;
    $caption_height = 3 * $caption_fontsize;
    
    /* Caption for pentagram */
    $optlist = "alignment=center font=" . $font . " fontsize=" .
                     $caption_fontsize;
    $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.
 */
function clippingrules($p, $font, $pagewidth, $pageheight) {
    
    /*
     * Divide the page horizontally into seven parts to place the three shapes
     * evenly spaced.
     */
    $unit = $pagewidth / 7;
    
    /* Path for pentagram */
    $penta = pentagram_path($p, $unit);
    
    /* Path for nested circles oriented in same direction */
    $nested_circles_same_dir = nested_circles_path($p, $unit / 2, $unit / 4, 
                    true);
    
    /* Path for nested circles oriented in opposite direction */
    $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 */
    $boxheight = $pageheight / 2;
    $boxwidth = $pagewidth;
    
    /* Y position of upper box on page */
    $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.
 */
function inverse_clipping($p, $font, $pagewidth, $pageheight) {
    $p->begin_page_ext($pagewidth, $pageheight, "");
    
    /* Define position and dimensions of "inverse" clip rectangle */
    $clip_llx = $pagewidth / 4;
    $clip_lly = $pageheight / 4;
    $clip_width = $pagewidth / 2;
    $clip_height = $pageheight / 4;
    
    /* Create explanation */
    $optlist = "alignment=center font=" . $font . " fontsize=20";
    $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 */
    $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 = 0;
    $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("");
}

$title = "Clipping Path Variants";

$pageheight = 852;
$pagewidth = 595;

try {
    $p = new PDFlib();
    
    /* This means we must check return values of load_font() etc. */
    $p->set_option("errorpolicy=return");
    $p->set_option("stringformat=utf8");
    
    if ($p->begin_document("", "") == - 1)
        throw new Exception("Error: " . $p->get_errmsg());
    
    $p->set_info("Creator", "PDFlib Cookbook");
    $p->set_info("Title", $title);
    
    $font = $p->load_font("Helvetica", "unicode", "");
    if ($font == 0)
        throw new Exception("Error: " . $p->get_errmsg());
    
    clippingrules($p, $font, $pagewidth, $pageheight);
    
    inverse_clipping($p, $font, $pagewidth, $pageheight);
    
    $p->end_document("");
    
    $buf = $p->get_buffer();
    $len = strlen($buf);
    
    header("Content-type: application/pdf");
    header("Content-Length: $len");
    header("Content-Disposition: inline; filename=clipping.pdf");
    print $buf;
}
catch (PDFlibException $e) {
    echo("PDFlib exception occurred:\n"
            . "[" . $e->get_errnum() . "] "
            . $e->get_apiname() . ": " . $e->get_errmsg() . "\n");
    exit(1);
}
catch (Throwable $e) {
    echo("PHP exception occurred: " . $e->getMessage() . "\n");
    exit(1);
}
$p = 0;
?>