{"id":1735,"date":"2011-04-11T10:47:26","date_gmt":"2011-04-11T16:47:26","guid":{"rendered":"http:\/\/bililite.nfshost.com\/blog\/?p=1735"},"modified":"2012-07-18T15:22:11","modified_gmt":"2012-07-18T21:22:11","slug":"paths-vector-graphics-and-php-images-in-fpdf","status":"publish","type":"post","link":"https:\/\/bililite.com\/blog\/2011\/04\/11\/paths-vector-graphics-and-php-images-in-fpdf\/","title":{"rendered":"Paths, Vector Graphics and PHP images in FPDF"},"content":{"rendered":"<p>Looking at <a href=\"http:\/\/fpdf.org\">FPDF<\/a> and at my <a href=\"\/blog\/2011\/03\/18\/creating-pdfs-with-php\/\">PDF tutorial<\/a>, it is clear that there are a few things that PDF's can do that aren't part of the API of FPDF. However, FPDF is easily extensible to include everything I might find useful, so I put together a package of those routines. \r\n<p><a href=\"\/blog\/blogfiles\/pdf\/pdftest.php?which=GraphicsPDF&amp;output=source\">See the code<\/a>.<\/p>\r\n<p><a href=\"\/blog\/blogfiles\/pdf\/graphicsTest.php\">See the sample output<\/a>.<\/p>\r\n<p><a href=\"\/blog\/blogfiles\/pdf\/pdftest.php?which=graphicsTest&amp;output=source\">See the code that produced the sample output<\/a>.<\/p>\r\n<!--more-->\r\n<h3>Path Drawing<\/h3>\r\n<p>Create a \"path\" by moving a virtual stylus around the page, then draw it or fill it in. The main advantage of using the path drawing routines rather than <a href=\"http:\/\/www.fpdf.org\/en\/doc\/line.htm\"><code class=\"language-php\">$pdf->Line<\/code><\/a> is that PDF creates nice line joins at the angles, rather than just overlaying the lines: <img decoding=\"async\" loading=\"lazy\" alt=\"Triange formed with path commands\" src=\"\/blog\/blogfiles\/pdf\/cooltriangle.png\" title=\"Path-drawn Triangle\" class=\"alignnone\" width=\"79\" height=\"141\" \/> as opposed to <img decoding=\"async\" loading=\"lazy\" alt=\"Triangle formed from individual lines\" src=\"\/blog\/blogfiles\/pdf\/uncooltriangle.png\" title=\"Individual Lines\" class=\"alignnone\" width=\"79\" height=\"115\" \/>.<\/p>\r\n<dl>\r\n<dt><code>MoveTo ($x, $y)<\/code><\/dt>\r\n<dd>Moves the stylus to <code>($x, $y)<\/code> (in user units) without drawing the path from the previous point. <code>$x<\/code> and <code>$y<\/code> must be numbers. Paths must start with a <code>MoveTo<\/code> to set the original stylus location or the result is undefined.<\/dd>\r\n<dt><code>LineTo ($x, $y)<\/code><\/dt>\r\n<dd>Draw a line from the current stylus location to <code>($x, $y)<\/code>, which becomes the new stylus location. Note that this only creates the line in the path; it does not actually draw the line on the page.<\/dd>\r\n<dt><code>CurveTo ($x1, $y1, $x2, $y2, $x3, $y3)<\/code><\/dt>\r\n<dd>Draw a <a href=\"http:\/\/en.wikipedia.org\/wiki\/B%C3%A9zier_curve\">cubic B\u00e9zier curve<\/a> from the current location to <code>($x3, $y3)<\/code>, using <code>($x1, $y1)<\/code> and <code>($x2, $y2)<\/code> as control points. See <a href=\"http:\/\/processingjs.nihongoresources.com\/bezierinfo\/\">Mike \"Pomax\" Kamermans<\/a>'s site for more information on drawing with B\u00e9zier curves.<\/dd>\r\n<dt><code>ClosePath()<\/code><\/dt>\r\n<dd>Draw a line from the current location to the last <code>MoveTo<\/code> point (if not the same) and mark the path as closed so the first and last lines join nicely.<\/dd>\r\n<dt><code>DrawPath($style='D')<\/code><\/dt>\r\n<dd>Actually draw the path on the page. <code>$style<\/code> can be:\r\n  <ul>\r\n  <li><code>'D'<\/code> (or an empty string) to draw the path with the current <a href=\"http:\/\/www.fpdf.org\/en\/doc\/setdrawcolor.htm\">drawing color<\/a>.<\/li>\r\n  <li><code>'F'<\/code> to fill the path with the current <a href=\"http:\/\/www.fpdf.org\/en\/doc\/setfillcolor.htm\">fill color<\/a>.<\/li>\r\n  <li><code>'DF'<\/code> or <code>'FD'<\/code> to do both.<\/li>\r\n  <\/ul>\r\n<\/dl>\r\n<h3>Drawing<\/h3>\r\n<dl>\r\n<dt><code>Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='')<\/code><\/dt>\r\n<dd>Enhanced <a href=\"http:\/\/www.fpdf.org\/en\/doc\/image.htm\">Image<\/a> to allow the use of <a href=\"http:\/\/us.php.net\/manual\/en\/book.image.php\">PHP GD images<\/a> directly, including the alpha channel (using the code from <a href=\"http:\/\/www.fpdf.org\/en\/script\/script83.php\">Valentin Schmidt<\/a>). Uses temporary PNG files, so requires write-access to the current directory.<\/dd>\r\n<dd>For example,\r\n<pre><code class=\"language-php\">$im = imagecreate(110, 20);\r\n$background_color = imagecolorallocate($im, 0, 0, 0);\r\n$text_color = imagecolorallocate($im, 233, 14, 91);\r\nimagestring($im, 1, 5, 5,  \"A Simple Text String\", $text_color);\r\n$pdf->Image($im);<\/code><\/pre>\r\nwill insert the image <img decoding=\"async\" loading=\"lazy\" alt=\"Image of text\" src=\"http:\/\/us.php.net\/manual\/en\/images\/21009b70229598c6a80eef8b45bf282b-imagecreate.png\" title=\"A Simple Text String\" class=\"alignnone\" width=\"110\" height=\"25\" \/> onto the page.<\/dd>\r\n<dd>Also enhanced to allow the use of vector-based images; see <code>OpenImage<\/code> and <code>CloseImage<\/code> below.<\/dd>\r\n<dt><code>Text($x, $y, $txt, $theta=0)<\/code><\/dt>\r\n<dd>Enhanced <a href=\"http:\/\/www.fpdf.org\/en\/doc\/text.htm\"><code>Text<\/code><\/a> to allow rotation of the baseline by <code>$theta<\/code> radians (counter clockwise).<\/dd>\r\n<dt><code>Rect($x, $y, $w, $h, $style='', $theta=0)<\/code><\/dt>\r\n<dd>Enhanced <a href=\"http:\/\/www.fpdf.org\/en\/doc\/rect.htm\"><code>Rect<\/code><\/a> to allow rotation of the rectangle by <code>$theta<\/code> radians (counter clockwise) around the upper left corner.<\/dd>\r\n<dt><code>PlaceImage($file, $x, $y, $w=0, $h=0, $theta=0, $type='')<\/code><\/dt>\r\n<dd>Similar to <a href=\"http:\/\/www.fpdf.org\/en\/doc\/image.htm\"><code>Image<\/code><\/a> but puts the image with <code>($x, $y)<\/code> at the <em>center<\/em> rather than the upper left of the image, and rotates the image by <code>$theta<\/code> radians (counter clockwise) around the center. Unlike <code>Image<\/code>, does not allow <code>$x=NULL<\/code> to place the image in the text flow, and does not create links.<\/dd>\r\n<dt><code>Arc($x, $y, $style='', $a, $b=NULL, $theta=0, $lambda1=0, $lambda2=0)<\/code><\/dt>\r\n<dd>Draw an arc or wedge of an ellipse. <code>($x, $y)<\/code> is the center of the ellipse, <code>$a<\/code> is the semimajor axis (half the width) and <code>$b<\/code> is the semiminor axis (half the height). If <code>$b===NULL<\/code>, then <code>$b<\/code> is set to <code>$a<\/code> (a circle with radius <code>$a<\/code>). The ellipse is rotated <code>$theta<\/code> radians counter clockwise from the horizontal. The arc goes from <code>$lambda1<\/code> radians from the major axis (not the horizontal) to <code>$lambda2<\/code> radians; if they are equal then the whole ellipse is drawn. <code>$style<\/code> is the same as for <a href=\"http:\/\/www.fpdf.org\/en\/doc\/rect.htm\">Rect<\/a>: <code>'D'<\/code> to draw the arc, <code>'F'<\/code> to fill in the wedge, <code>'DF'<\/code> or <code>'FD'<\/code> to do both.<\/dd>\r\n<dd>The calculations to estimate circles with B\u00e9zier curves comes from <a href=\"http:\/\/www.spaceroots.org\/documents\/ellipse\/\">L. Maisonobe<\/a>.<\/dd>\r\n<dt><code>Polygon($x, $y, $n, $style='', $a, $b=NULL, $skip=1, $theta=0, $lambda=0)<\/code><\/dt>\r\n<dd>Draw a regular polygon with <code>$n<\/code> sides, inscribed in the ellipse that would be drawn with <code>Arc($x, $y, '', $a, $b, $theta)<\/code>. The first vertex is placed at <code>$lambda<\/code> to the major axis. <code>$skip<\/code> determines the shape of the polygon: if <code>$skip==1<\/code>, draw a regular, convex polygon. If <code>$skip&gt;1<\/code>, draw a star by drawing the lines from each vertex <code><em>i<\/em><\/code> to <code><em>i<\/em>+$skip<\/code>. The higher <code>$skip<\/code> is, the tighter the star (up to <code>$n\/2<\/code>).<\/dd>\r\n<dd>So <code class=\"language-php\">$pdf->Polygon(1, 1, 5, 'D', 10, 10, 1)<\/code> draws a pentagon with a radius of 10, and <code class=\"language-php\">$pdf->Polygon(1, 1, 5, 'D', 10, 10, 2)<\/code> draws a five-pointed star with a radius of 10.<\/dd>\r\n<dt><code>Cloverleaf($x, $y, $n, $style='', $a, $b=NULL, $skip=1, $theta=0, $lambda=0)<\/code><\/dt>\r\n<dd>Same as <code>Polygon<\/code>, but draws curves with loops at the vertices.<\/dd>\r\n<\/dl>\r\n<h3>Transparent Drawing<\/h3>\r\n<p>In addition to allowing alpha-channel transparency in images with the enhanced <code>Image<\/code>, you can use transparent colors for drawing, filling and text. The code is based on that of <a href=\"http:\/\/www.fpdf.org\/en\/script\/script74.php\">Martin Hall-May<\/a>.<\/p>\r\n<dl>\r\n<dt><code>SetDrawColor($r, $g=null, $b=null, $alpha=0)<\/code><\/dt>\r\n<dd>Enhanced <a href=\"http:\/\/www.fpdf.org\/en\/doc\/setdrawcolor.htm\">SetDrawColor<\/a> to allow alpha channel transparency. <code>$alpha==0<\/code> is opaque and <code>$alpha==127<\/code> is fully transparent. Note that this is backward from CSS opacity, which uses 0 for transparent and 1 for opaque.<\/dd>\r\n<dt><code>SetFillColor($r, $g=null, $b=null, $alpha=0)<\/code><\/dt>\r\n<dd>Enhanced <a href=\"http:\/\/www.fpdf.org\/en\/doc\/setfillcolor.htm\">SetFillColor<\/a> to allow alpha channel transparency. <code>$alpha==0<\/code> is opaque and <code>$alpha==127<\/code> is fully transparent.<\/dd>\r\n<dt><code>SetTextColor($r, $g=null, $b=null, $alpha=0)<\/code><\/dt>\r\n<dd>Enhanced <a href=\"http:\/\/www.fpdf.org\/en\/doc\/settextcolor.htm\">SetTextColor<\/a> to allow alpha channel transparency. <code>$alpha==0<\/code> is opaque and <code>$alpha==127<\/code> is fully transparent.<\/dd>\r\n<\/dl>\r\n<h3>Vector-based images<\/h3>\r\n<p>In addition to bitmapped images like JPEG or PNG images (or GD images), PDF supports vector graphics similar to SVG (a class to allow using SVG directly would be useful; I may get the time). Create a new image with <code>OpenImage<\/code>, then any of the drawing commands will draw into the image being created rather than onto the page. Use <code>Image<\/code> or <code>PlaceImage<\/code> to put the image onto the page.<p>\r\n<p>For example,<\/p>\r\n<pre><code class=\"language-php\">$pdf = new GraphicsPDF('P', 'in', 'letter'); \/\/ use inches for user units\r\n$pdf->OpenImage('star', 2, 2); \/\/ create a 2 x 2 image\r\n$pdf->SetFillColor (0, 0, 255);\r\n$pdf->Rect(0, 0, 2, 2, 'F'); \/\/ fill it with blue\r\n$pdf->SetFillColor (255);\r\n$pdf->Polygon(1, 1, 5, 'F', 1, 1, 2, 0, deg2rad(90)); \/\/ put a white star in the center\r\n$pdf->CloseImage();\r\n$pdf->AddPage();\r\n$pdf->Image('star', 1, 1); \/\/ put the image in various sizes\r\n$pdf->Image('star', 3, 1, 0.25);\r\n$pdf->Image('star', 5, 1, 3); \r\n$pdf->Image('star', 5, 7, 4);\r\n$pdf->Output();<\/code><\/pre>\r\n<p>produces <a href=\"\/blog\/blogfiles\/pdf\/pdftest.php?which=GraphicsPDF\">this<\/a>.<\/p>\r\n<dl>\r\n<dt><code>OpenImage($name, $w, $h)<\/code><\/dt>\r\n<dd>Start creating a vector image named <code>$name<\/code> with width <code>$w<\/code> and height <code>$h<\/code>. All drawing code will go into the image, not the page (however, state-changing calls like <a href=\"http:\/\/www.fpdf.org\/en\/doc\/setdrawcolor.htm\"><code>SetDrawColor<\/code><\/a> and <a href=\"http:\/\/www.fpdf.org\/en\/doc\/setlinewidth.htm\"><code>SetLineWidth<\/code><\/a> will carry over to the page).<\/dd>\r\n<dt><code>CloseImage()<\/code><\/dt>\r\n<dd>Stop recording the current image and return to drawing directly on the page.<\/dd>\r\n<\/dl>\r\n<h3>Curve Calculations<\/h3>\r\n<p><code>Arc<\/code>, <code>Polygon<\/code> and <code>Cloverleaf<\/code> involve a fair amount of heavy math, so I exposed methods to return simply the boundary points for the shapes rather than drawing them, to be used for building paths and the like.<\/p>\r\n<p>Each of these returns an array of arrays. Each subarray has 4, 8 or 0 elements. 0 elements indicates that one shape has ended and another needs to start; think of a six-pointed star consisting of two triangles. 4 elements indicates a line: <code>[x0, y0, x1, y1]<\/code> is a line from <code>(x0, y0)<\/code> to <code>(x1, y1)<\/code>. 8 elements indicates a curve:  <code>[x0, y0, x1, y1, x2, y2, x3, y3]<\/code> is a curve from <code>(x0, y0)<\/code> to <code>(x3, y3)<\/code> with control points <code>(x1, y1)<\/code> and <code>(x2, y2)<\/code>.<\/p>\r\n<dl>\r\n<dt><code>EllipseCurves($x, $y, $a, $b=NULL, $theta=0, $lambda1=0, $lambda2=0)<\/code><\/dt>\r\n<dd>Generate the curves for the ellipse that would be drawn by <code>Arc($x, $y, 'D', $a, $b, $theta, $lambda1, $lambda2)<\/code>.<\/dd>\r\n<dt><code>PolygonLines($x, $y, $n, $a, $b=NULL, $skip=1, $theta=0, $lambda=0)<\/code><\/dt>\r\n<dd>Generate the lines that would be drawn by <code>Polygon($x, $y, 'D', $n, $a, $b, $skip, $theta, $lambda)<\/code>.<\/dd>\r\n<dt><code>CloverleafCurves($x, $y, $n, $a, $b=NULL, $skip=1, $theta=0, $lambda=0)<\/code><\/dt>\r\n<dd>Generate the curves that would be drawn by <code>Cloverleaf($x, $y, 'D', $n, $a, $b, $skip, $theta, $lambda)<\/code>.<\/dd>\r\n<\/dl>\r\n<p>Hope this is useful to someone.<\/p>\r\n","protected":false},"excerpt":{"rendered":"Looking at FPDF and at my PDF tutorial, it is clear that there are a few things that PDF's can do that aren't part of the API of FPDF. However, FPDF is easily extensible to include everything I might find useful, so I put together a package of those routines. See the code. See the [&hellip;]","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[15,9],"tags":[],"_links":{"self":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/1735"}],"collection":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/comments?post=1735"}],"version-history":[{"count":24,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/1735\/revisions"}],"predecessor-version":[{"id":2424,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/1735\/revisions\/2424"}],"wp:attachment":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/media?parent=1735"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/categories?post=1735"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/tags?post=1735"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}