Tuesday, May 27, 2008

Hacking Filled Polygon Support into FPDF

My preferred way to generate PDFs with PHP is by using the FPDF Library. The library is: free, doesn't require any add ons to run, has a terrific set of tutorials and a clear user manual. It turned out to be a great way to generate official looking PDF documents with a minimum of fuss.

Today, I needed to generate a specific chart type, and ran into a problem using FPDF. Turns out, FPDF doesn't support arbitrarily filled polygon shapes - only rectangles. D'oh.

This, however, turned out to be a great excuse to dig a little deeper into both FPDF and the PDF standard itself. From a review of the FPDF source code, I realized that most of what it was doing was emitting the right PDF operators into a text stream and letting the PDF viewer worry about the details.

I busted out the old PDF Reference Manual and learned that the operators it used were very similar to PostScript. If you've never looked into PostScript, it's a wonderfully fun language to learn.

Using my knowledge of PostScript, and reviewing the source code of FPDF I was able to hack the FPDF source to include:

function Poly($points, $close = false, $style = '') {
 if($style == 'F') {
   $op = 'f';
 } else if($style == 'FD' || $style == 'DF') {
   $op = 'B';
 } else {
   $op = 'S';
 $buffer = sprintf("%.2f %.2f m\n", $points[0][0], $this->h - $points[0][1]);
 for($i = 1; $i <>h - $points[$i][1]);
 if($close) {
   $buffer .= " h\n";
 $buffer .= " $op\n";

Here's a sample usage:


$inch = 72;
$fpdf = new FPDF('L','pt','letter');
$fpdf->SetDrawColor(0xA4, 0x17, 0x17);
$fpdf->SetFillColor(0x63, 0x8F, 0xC9);
$fpdf->SetFont('Helvetica', '', 12);

$fpdf->Text($inch, $inch, "Test of the Poly(...) method:");
$fpdf->SetXY($inch, 4 * $inch);

// Build up a set of points
$points = array();
$points[] = array($inch, 4 * $inch);
for($x = 1; $x <>Poly($points, true, 'FD');


And here's how it looks in the PDF viewer:


  1. Anonymous7:06 AM

    Hi, This is kind of what i want (well to draw a triangle with pointy ends).

    However on implimenting your code I can't understand the for loop you are using. I do not recon its php and my zend ide is spitting errors to me.

    Can you check the above code or even upload the fpdf modified file (if you still have it)

    Ta, Reuben.

  2. Anonymous9:14 PM

    I found the solution!

    replace the 3 lines above the if($close) to the following.

    $buffer = sprintf("%.2f %.2f m\n", $points[0][0]*$this->k, ($this->h-$points[0][1])*$this->k);
    for ($i=1; $i<count($points); $i++) {
    $buffer = sprintf("%.2f %.2f l\n", $points[$i][0]*$this->k,($this->h-$points[$i][1])*$this->k);

    L8r, Reuben

  3. Reuben -

    Glad you figured it out! Thanks for sharing.