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 that produced the sample output.
Path Drawing
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 $pdf->Line
is that PDF creates nice line joins at the angles, rather than just overlaying the lines: as opposed to .
MoveTo ($x, $y)
- Moves the stylus to
($x, $y)
(in user units) without drawing the path from the previous point.$x
and$y
must be numbers. Paths must start with aMoveTo
to set the original stylus location or the result is undefined. LineTo ($x, $y)
- Draw a line from the current stylus location to
($x, $y)
, 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. CurveTo ($x1, $y1, $x2, $y2, $x3, $y3)
- Draw a cubic Bézier curve from the current location to
($x3, $y3)
, using($x1, $y1)
and($x2, $y2)
as control points. See Mike "Pomax" Kamermans's site for more information on drawing with Bézier curves. ClosePath()
- Draw a line from the current location to the last
MoveTo
point (if not the same) and mark the path as closed so the first and last lines join nicely. DrawPath($style='D')
- Actually draw the path on the page.
$style
can be:'D'
(or an empty string) to draw the path with the current drawing color.'F'
to fill the path with the current fill color.'DF'
or'FD'
to do both.
Drawing
Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='')
- Enhanced Image to allow the use of PHP GD images directly, including the alpha channel (using the code from Valentin Schmidt). Uses temporary PNG files, so requires write-access to the current directory.
- For example,
will insert the image onto the page.$im = imagecreate(110, 20); $background_color = imagecolorallocate($im, 0, 0, 0); $text_color = imagecolorallocate($im, 233, 14, 91); imagestring($im, 1, 5, 5, "A Simple Text String", $text_color); $pdf->Image($im);
- Also enhanced to allow the use of vector-based images; see
OpenImage
andCloseImage
below. Text($x, $y, $txt, $theta=0)
- Enhanced
Text
to allow rotation of the baseline by$theta
radians (counter clockwise). Rect($x, $y, $w, $h, $style='', $theta=0)
- Enhanced
Rect
to allow rotation of the rectangle by$theta
radians (counter clockwise) around the upper left corner. PlaceImage($file, $x, $y, $w=0, $h=0, $theta=0, $type='')
- Similar to
Image
but puts the image with($x, $y)
at the center rather than the upper left of the image, and rotates the image by$theta
radians (counter clockwise) around the center. UnlikeImage
, does not allow$x=NULL
to place the image in the text flow, and does not create links. Arc($x, $y, $style='', $a, $b=NULL, $theta=0, $lambda1=0, $lambda2=0)
- Draw an arc or wedge of an ellipse.
($x, $y)
is the center of the ellipse,$a
is the semimajor axis (half the width) and$b
is the semiminor axis (half the height). If$b===NULL
, then$b
is set to$a
(a circle with radius$a
). The ellipse is rotated$theta
radians counter clockwise from the horizontal. The arc goes from$lambda1
radians from the major axis (not the horizontal) to$lambda2
radians; if they are equal then the whole ellipse is drawn.$style
is the same as for Rect:'D'
to draw the arc,'F'
to fill in the wedge,'DF'
or'FD'
to do both. - The calculations to estimate circles with Bézier curves comes from L. Maisonobe.
Polygon($x, $y, $n, $style='', $a, $b=NULL, $skip=1, $theta=0, $lambda=0)
- Draw a regular polygon with
$n
sides, inscribed in the ellipse that would be drawn withArc($x, $y, '', $a, $b, $theta)
. The first vertex is placed at$lambda
to the major axis.$skip
determines the shape of the polygon: if$skip==1
, draw a regular, convex polygon. If$skip>1
, draw a star by drawing the lines from each vertexi
toi+$skip
. The higher$skip
is, the tighter the star (up to$n/2
). - So
$pdf->Polygon(1, 1, 5, 'D', 10, 10, 1)
draws a pentagon with a radius of 10, and$pdf->Polygon(1, 1, 5, 'D', 10, 10, 2)
draws a five-pointed star with a radius of 10. Cloverleaf($x, $y, $n, $style='', $a, $b=NULL, $skip=1, $theta=0, $lambda=0)
- Same as
Polygon
, but draws curves with loops at the vertices.
Transparent Drawing
In addition to allowing alpha-channel transparency in images with the enhanced Image
, you can use transparent colors for drawing, filling and text. The code is based on that of Martin Hall-May.
SetDrawColor($r, $g=null, $b=null, $alpha=0)
- Enhanced SetDrawColor to allow alpha channel transparency.
$alpha==0
is opaque and$alpha==127
is fully transparent. Note that this is backward from CSS opacity, which uses 0 for transparent and 1 for opaque. SetFillColor($r, $g=null, $b=null, $alpha=0)
- Enhanced SetFillColor to allow alpha channel transparency.
$alpha==0
is opaque and$alpha==127
is fully transparent. SetTextColor($r, $g=null, $b=null, $alpha=0)
- Enhanced SetTextColor to allow alpha channel transparency.
$alpha==0
is opaque and$alpha==127
is fully transparent.
Vector-based images
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 OpenImage
, then any of the drawing commands will draw into the image being created rather than onto the page. Use Image
or PlaceImage
to put the image onto the page.
For example,
$pdf = new GraphicsPDF('P', 'in', 'letter'); // use inches for user units
$pdf->OpenImage('star', 2, 2); // create a 2 x 2 image
$pdf->SetFillColor (0, 0, 255);
$pdf->Rect(0, 0, 2, 2, 'F'); // fill it with blue
$pdf->SetFillColor (255);
$pdf->Polygon(1, 1, 5, 'F', 1, 1, 2, 0, deg2rad(90)); // put a white star in the center
$pdf->CloseImage();
$pdf->AddPage();
$pdf->Image('star', 1, 1); // put the image in various sizes
$pdf->Image('star', 3, 1, 0.25);
$pdf->Image('star', 5, 1, 3);
$pdf->Image('star', 5, 7, 4);
$pdf->Output();
produces this.
OpenImage($name, $w, $h)
- Start creating a vector image named
$name
with width$w
and height$h
. All drawing code will go into the image, not the page (however, state-changing calls likeSetDrawColor
andSetLineWidth
will carry over to the page). CloseImage()
- Stop recording the current image and return to drawing directly on the page.
Curve Calculations
Arc
, Polygon
and Cloverleaf
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.
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: [x0, y0, x1, y1]
is a line from (x0, y0)
to (x1, y1)
. 8 elements indicates a curve: [x0, y0, x1, y1, x2, y2, x3, y3]
is a curve from (x0, y0)
to (x3, y3)
with control points (x1, y1)
and (x2, y2)
.
EllipseCurves($x, $y, $a, $b=NULL, $theta=0, $lambda1=0, $lambda2=0)
- Generate the curves for the ellipse that would be drawn by
Arc($x, $y, 'D', $a, $b, $theta, $lambda1, $lambda2)
. PolygonLines($x, $y, $n, $a, $b=NULL, $skip=1, $theta=0, $lambda=0)
- Generate the lines that would be drawn by
Polygon($x, $y, 'D', $n, $a, $b, $skip, $theta, $lambda)
. CloverleafCurves($x, $y, $n, $a, $b=NULL, $skip=1, $theta=0, $lambda=0)
- Generate the curves that would be drawn by
Cloverleaf($x, $y, 'D', $n, $a, $b, $skip, $theta, $lambda)
.
Hope this is useful to someone.
RL says:
Thanks, i Was looking for a way to draw a filled triangle, which isn’t native to FPDF
July 18, 2012, 2:45 pmDanny says:
@RL:
July 18, 2012, 3:22 pmI’m glad this turned out to be useful to someone.
–Danny
Tony says:
Thank you, I needed a watermark with transparency and your library did the trick like a charm.
February 7, 2013, 4:34 pmDanny says:
@Tony:
February 7, 2013, 5:03 pmNice to know I could help someone!
–Danny
Edwin Mitchell-Finch says:
I’ve been trying to replicate the transparent image overlays from your example – map, dice, gecko and can’t get the transparency to work:
http://mybrain.co.uk/mind/algorithm/v00002/test4.php
I used your code, and as you can see, I even used your images, but still no go…
Any ideas where the problem could be?
Ed
June 26, 2013, 7:50 amDanny says:
@Edwin:
The code creates a temporary alpha-channel (transparency information) image file (see the method
_parseOneImage
) using$file = tempnam('.','img'); imagepng($im, $file);
.If
tempnam
is creating a file that you don’t have permission to write, the transparency will fail. Remove the@unlink($file);
line from_parseOneImage
and see if you are creating a bunch of temporary files in your current directory.That’s the only thing I can think of.
June 26, 2013, 8:31 amHope it helps,
Danny
Edwin Mitchell-Finch says:
Thanks for the quick response – yes it is creating 4 temporary files when I run the script, but they are just files, they have no file extensions – is that correct?
June 26, 2013, 8:57 amDanny says:
@Edwin:
June 26, 2013, 9:32 amYes, the files have no extension (that’s just how
tempnam
works).Are they valid image files? Open them as png’s (you may need to rename them with the .png extension). Each image with an alpha channel should have two images, an original with the black background and a grayscale mask for the transparency.
Beyond that, I’m not sure. The rest of the sample code works?
–Danny
Edwin Mitchell-Finch says:
The files are exactly as you described. Could it be a version thing?
June 26, 2013, 9:49 amWhat version PHP is your server running for instance?
Danny says:
@Edwin:
June 26, 2013, 11:00 amI’m using PHP 5.3, with tPDF 1.03 (dated 2010-07-25).
The alpha channel code is based on http://www.fpdf.org/en/script/script83.php
Can you get that to work?
Edwin Mitchell-Finch says:
Sent you an email but got a mail delivery error on your address.
Thanks for your help – it worked with tFPDF version 1.03 (based on FPDF 1.6) but it doesn’t work with tFPDF version 1.24 (based on FPDF 1.7).
One last issue is that the graphics are all enlarged by around 30% is there a way of ensuring they appear at their original size?
June 27, 2013, 2:31 amEdwin Mitchell-Finch says:
The GraphicsPDF you kindly sent me didn’t have any text to disply, when I add a font using the usual $pdf->SetFont(‘Helvetica’, ”, 12); I get an error message:
tFPDF error: Could not include font metric file.
Could this be a mismatch between your version of tFPDF and the version of ttfonts.php I am using?
Would you mind sending me your complete tFPDF package including the fonts directory?
Thanks
June 27, 2013, 4:09 amDanny says:
@Edwin:
June 27, 2013, 9:18 amI’m glad we could figure most of this out, but with the ongoing issues you are having, you may want to look at
tcpdf
, which I discuss in this post. It handles transparency in PNGs and does fonts (for me at least) better than tFDF.I developed these routines more as a way to learn what makes PDF’s tick; they are not being actively maintained.
tcpdf
is. It is what I use in production.Rob Toutant says:
Very nice code …
I am not sure if you will be able to help – but i just discovered your code and thought it might be useful in creating text from opentype.js …
i got the path from opentype.js as follows
{“commands”:[{“type”:”M”,”x”:21.12,”y”:-5.5680000000000005},{“type”:”C”,”x1″:19.456,”y1″:-4.5440000000000005,”x2″:18.112000000000002,”y2″:-4.096,”x”:16.64,”y”:-4.096},{“type”:”C”,”x1″:13.696,”y1″:-4.096,”x2″:12.608,”y2″:-5.696,”x”:12.608,”y”:-9.088000000000001},{“type”:”L”,”x”:12.608,”y”:-29.248},{“type”:”L”,”x”:20.416,”y”:-29.248},{“type”:”L”,”x”:21.12,”y”:-33.728},{“type”:”L”,”x”:12.608,”y”:-33.728},{“type”:”L”,”x”:12.608,”y”:-42.048},{“type”:”L”,”x”:6.72,”y”:-41.344},{“type”:”L”,”x”:6.72,”y”:-33.728},{“type”:”L”,”x”:1.344,”y”:-33.728},{“type”:”L”,”x”:1.344,”y”:-29.248},{“type”:”L”,”x”:6.72,”y”:-29.248},{“type”:”L”,”x”:6.72,”y”:-9.024000000000001},{“type”:”C”,”x1″:6.72,”y1″:-3.2,”x2″:9.536,”y2″:0.768,”x”:15.808,”y”:0.768},{“type”:”C”,”x1″:18.624,”y1″:0.768,”x2″:21.312,”y2″:0,”x”:23.36,”y”:-1.536},{“type”:”Z”},{“type”:”M”,”x”:55.168,”y”:-14.848},{“type”:”C”,”x1″:55.232,”y1″:-15.616,”x2″:55.296,”y2″:-16.704,”x”:55.296,”y”:-17.856},{“type”:”C”,”x1″:55.296,”y1″:-28.16,”x2″:50.495999999999995,”y2″:-34.496,”x”:41.664,”y”:-34.496},{“type”:”C”,”x1″:32.704,”y1″:-34.496,”x2″:27.392,”y2″:-27.008,”x”:27.392,”y”:-16.832},{“type”:”C”,”x1″:27.392,”y1″:-6.336,”x2″:32.576,”y2″:0.768,”x”:42.623999999999995,”y”:0.768},{“type”:”C”,”x1″:46.976,”y1″:0.768,”x2″:50.751999999999995,”y2″:-0.768,”x”:53.888000000000005,”y”:-3.2640000000000002},{“type”:”L”,”x”:51.2,”y”:-7.168},{“type”:”C”,”x1″:48.32,”y1″:-4.8,”x2″:45.824,”y2″:-4.032,”x”:42.751999999999995,”y”:-4.032},{“type”:”C”,”x1″:37.632,”y1″:-4.032,”x2″:34.176,”y2″:-7.36,”x”:33.664,”y”:-14.848},{“type”:”Z”},{“type”:”M”,”x”:41.664,”y”:-29.824},{“type”:”C”,”x1″:46.912,”y1″:-29.824,”x2″:49.344,”y2″:-26.176000000000002,”x”:49.472,”y”:-19.2},{“type”:”L”,”x”:33.664,”y”:-19.2},{“type”:”C”,”x1″:34.112,”y1″:-26.432000000000002,”x2″:37.056,”y2″:-29.824,”x”:41.664,”y”:-29.824},{“type”:”Z”},{“type”:”M”,”x”:72.96000000000001,”y”:-3.968},{“type”:”C”,”x1″:69.376,”y1″:-3.968,”x2″:66.24000000000001,”y2″:-5.184,”x”:63.616,”y”:-7.36},{“type”:”L”,”x”:60.352000000000004,”y”:-3.648},{“type”:”C”,”x1″:63.29600000000001,”y1″:-1.088,”x2″:67.392,”y2″:0.768,”x”:73.08800000000001,”y”:0.768},{“type”:”C”,”x1″:79.808,”y1″:0.768,”x2″:86.4,”y2″:-2.3040000000000003,”x”:86.4,”y”:-9.536},{“type”:”C”,”x1″:86.4,”y1″:-16.064,”x2″:81.664,”y2″:-18.240000000000002,”x”:75.712,”y”:-19.904},{“type”:”C”,”x1″:70.016,”y1″:-21.44,”x2″:67.904,”y2″:-22.528,”x”:67.904,”y”:-25.408},{“type”:”C”,”x1″:67.904,”y1″:-28.096,”x2″:70.528,”y2″:-29.824,”x”:74.176,”y”:-29.824},{“type”:”C”,”x1″:77.76,”y1″:-29.824,”x2″:80.256,”y2″:-28.8,”x”:82.688,”y”:-27.136},{“type”:”L”,”x”:85.31200000000001,”y”:-31.104},{“type”:”C”,”x1″:82.56,”y1″:-33.088,”x2″:79.04,”y2″:-34.496,”x”:74.432,”y”:-34.496},{“type”:”C”,”x1″:67.328,”y1″:-34.496,”x2″:61.824000000000005,”y2″:-30.72,”x”:61.824000000000005,”y”:-25.024},{“type”:”C”,”x1″:61.824000000000005,”y1″:-19.52,”x2″:65.72800000000001,”y2″:-16.896,”x”:71.808,”y”:-15.36},{“type”:”C”,”x1″:78.464,”y1″:-13.696,”x2″:80.06400000000001,”y2″:-12.416,”x”:80.06400000000001,”y”:-9.088000000000001},{“type”:”C”,”x1″:80.06400000000001,”y1″:-5.952,”x2″:77.248,”y2″:-3.968,”x”:72.96000000000001,”y”:-3.968},{“type”:”Z”},{“type”:”M”,”x”:109.76,”y”:-5.5680000000000005},{“type”:”C”,”x1″:108.096,”y1″:-4.5440000000000005,”x2″:106.75200000000001,”y2″:-4.096,”x”:105.28,”y”:-4.096},{“type”:”C”,”x1″:102.336,”y1″:-4.096,”x2″:101.248,”y2″:-5.696,”x”:101.248,”y”:-9.088000000000001},{“type”:”L”,”x”:101.248,”y”:-29.248},{“type”:”L”,”x”:109.056,”y”:-29.248},{“type”:”L”,”x”:109.76,”y”:-33.728},{“type”:”L”,”x”:101.248,”y”:-33.728},{“type”:”L”,”x”:101.248,”y”:-42.048},{“type”:”L”,”x”:95.36,”y”:-41.344},{“type”:”L”,”x”:95.36,”y”:-33.728},{“type”:”L”,”x”:89.984,”y”:-33.728},{“type”:”L”,”x”:89.984,”y”:-29.248},{“type”:”L”,”x”:95.36,”y”:-29.248},{“type”:”L”,”x”:95.36,”y”:-9.024000000000001},{“type”:”C”,”x1″:95.36,”y1″:-3.2,”x2″:98.176,”y2″:0.768,”x”:104.44800000000001,”y”:0.768},{“type”:”C”,”x1″:107.264,”y1″:0.768,”x2″:109.952,”y2″:0,”x”:112,”y”:-1.536},{“type”:”Z”}],”fill”:”black”,”stroke”:null,”strokeWidth”:1}
implemented a simple addition to your code for testing
$pdf->SetDrawColor (120, 120, 255);
$pdf->SetFillColor (175, 238, 238);
$pdf->SetLineWidth(0.01);
for($i=0;$icommands);$i++){
$o = $path->commands[$i];
// error_log(json_encode($o).”\n”);
if($o->type==’M’){
$pdf->MoveTo( floatval( $o->x ), floatval( $o->y ));
}
else if($o->type==’C’){
$pdf->CurveTo(
floatval( $o->x ),floatval( $o->y ) ,
floatval( $o->x1 ), floatval( $o->y1 ),
floatval( $o->x2 ), floatval( $o->y2 ) );
}
else if($o->type==’L’){
$pdf->LineTo(floatval( $o->x ), floatval( $o->y ));
}
else if($o->type==’Z’){
$pdf->ClosePath();
}
else{
error_log(“unknown type “.$o->type);
}
}
$pdf->DrawPath(“FD”);
but nothing appears …
November 25, 2020, 11:03 pmany help would be greatly appreciated