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(""); } /* This is where the data files are. Adjust as necessary. */ $searchpath = dirname(__FILE__,3)."/input"; $title = "Clipping Path Variants"; $pageheight = 852; $pagewidth = 595; 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("", "") == - 1) throw new Exception("Error: " . $p->get_errmsg()); $p->set_info("Creator", "PDFlib Cookbook"); $p->set_info("Title", $title); $font = $p->load_font("NotoSerif-Regular", "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; ?>