FIGURE 30.6 pdf.php generates a certificate from an PDF template. One problem with this approach is that the code runs quite slowly because of the regular expression matching required. Regular expressions run much more slowly than str_replace() that we could use for the RTF version. If you are going to match a large number of placeholders or try to generate many of these doc- uments on the same server, you might want look at other approaches. This would be less of a problem for a simpler template. Much of the bulk in this file is data representing the images. Generating a PDF Document Using PDFlib PDFlib is intended for generating dynamic PDF documents via the Web. It is not strictly part of PHP, but rather a separate library, with a large number of functions intended to be called from a wide variety of programming languages. Language bindings are available for C, C++, Java, Perl, Python, Tcl, and ActiveX/COM. Interestingly, there is not an official PDFlib PHP binding available yet. The current binding is not documented or supported by PDFlib. The PDFlib Web site states, “Other language bind- ings, such as PHP, will be supported in the future,” but it has said this for at least a year. Generating Personalized Documents in Portable Document Format (PDF) C HAPTER 30 30 GENERATING PERSONALIZED DOCUMENTS IN PDF 765 36 7842 CH30 3/6/01 3:40 PM Page 765 Although it is possible that the official PHP binding might be better then the current one when (or if) it arrives, the current one is very good. The only problem is that you need to read the PHP documentation, available at http://www.php.net/manual/ref.pdf.php for an overview, installation instructions, and the PDFlib documentation, available as the file PDFlib-manual.pdf in the PDFlib distribution, for a detailed reference on the API. A Hello World Script for PDFlib After you have PHP and installed it with PDFlib enabled, you can test it with a simple program such as the Hello World example in Listing 30.5. LISTING 30.5 testpdf.php—Classic Hello World Example Using PDFlib Via PHP <? //create file $fp = fopen(“hello.pdf”, “w”); if(!$fp) { echo “Error: could not create the PDF file”; exit; } // start the pdf document $pdf = pdf_open($fp); pdf_set_info($pdf, “Creator”, “pdftest.php”); pdf_set_info($pdf, “Author”, “Luke Welling and Laura Thomson”); pdf_set_info($pdf, “Title”, “Hello World (PHP)”); // US letter is 11” x 8.5” and there are approximately 72 points per inch pdf_begin_page($pdf, 8.5*72, 11*72); pdf_add_outline($pdf, “Page 1”); pdf_set_font($pdf, “Helvetica-Bold”, 24, “host”); pdf_set_text_pos($pdf, 50, 700); // write text pdf_show($pdf,”Hello,world!”); pdf_continue_text($pdf,”(says PHP)”); // end the document pdf_end_page($pdf); pdf_close($pdf); fclose($fp); Building Practical PHP and MySQL Projects P ART V 766 36 7842 CH30 3/6/01 3:40 PM Page 766 LISTING 30.5 Continued // display a link to download echo “download the pdf <a href = ‘hello.pdf’>here</a>”; ?> The most likely error you will see if this script fails is the following: Fatal error: Call to undefined function: pdf_open() in /home/book/public_html/chapter30/pdftest.php on line 6 This means that you do not have the PDFlib extension compiled into PHP. The installation is fairly straightforward, but some details change depending on the exact ver- sions of PHP and PDFlib that you are using. A good place to check for detailed suggestions is the user contributed notes on the PDFlib page in the annotated PHP manual. When you have this script up and running on your system, it is time to look at how it works. The first section of the code, including the line $fp = fopen(“hello.pdf”, “w”); creates a writeable file. It is worth noting that the code here is writing directly in to the current directory even though we have already discussed a number of reasons why it is a bad idea to have your permissions set up to allow PHP to write within the Web tree. The line $pdf = pdf_open($fp); initializes a PDF document using the file we already opened. You can also call pdf_open() without parameters to create a document in memory to be output directly to the browser. In any case, you will need to capture the return value of pdf_open(), as every subsequent call to a PDF function will need it. The function pdf_set_info() enables you to tag the document with a subject, title, creator, author, a list of keywords, and one custom, user-defined field. Here we are setting a creator, author, and title. Note that all six info fields are optional. pdf_set_info($pdf, “Creator”, “pdftest.php”); pdf_set_info($pdf, “Author”, “Luke Welling and Laura Thomson”); pdf_set_info($pdf, “Title”, “Hello World (PHP)”); A PDF document consists of a number of pages. To start a new page, we need to call pdf_begin_page(). As well as the identifier returned by pdf_open(), pdf_begin_page() requires the dimensions of the page. Each page in a document can be a different size, but unless you have a good reason not to, you should use a common paper size. Generating Personalized Documents in Portable Document Format (PDF) C HAPTER 30 30 GENERATING PERSONALIZED DOCUMENTS IN PDF 767 36 7842 CH30 3/6/01 3:40 PM Page 767 PDFlib works in points, both for page size, and for locating coordinate locations on each page. For reference, A4 is approximately 595 by 842 points and U.S. letter is 612 by 792 points. This means that our line pdf_begin_page($pdf, 8.5*72, 11*72); creates a page in our document, sized for U.S. letter paper. A PDF document does not need to be just a printable document. Many PDF features can be included in the document such as hyperlinks and bookmarks. The function pdf_add_outline() will add a bookmark to the document outline. The bookmarks in a document will appear in a separate pane in Acrobat Reader, allowing us to skip straight to important sections. This line pdf_add_outline($pdf, “Page 1”); adds an outline entry labeled Page 1, which will refer to the current page. Fonts available on systems vary from operating system to operating system and even from indi- vidual machine to machine. In order to guarantee consistent results, a set of core fonts will work with every PDF reader. The 14 core fonts are • Courier • Courier-Bold • Courier-Oblique • Courier-BoldOblique • Helvetica • Helvetica-Bold • Helvetica-Oblique • Helvetica-BoldOblique • Times-Roman • Times-Bold • Times-Italic • Times-BoldItalic • Symbol • ZapfDingbats Fonts outside this set can be embedded in documents, but this will increase the file size and might not be acceptable under whatever license you own that particular font under. We can choose a font, its size, and character encoding as follows: Building Practical PHP and MySQL Projects P ART V 768 36 7842 CH30 3/6/01 3:40 PM Page 768 pdf_set_font($pdf, “Helvetica-Bold”, 24, “host”); Font sizes are specified in points. We have chosen host character encoding. The allowable val- ues are winansi, builtin, macroman, ebcdic, or host. The meanings of the different values are as follows: • winansi—ISO 8859-1 plus special characters added by Microsoft such as a Euro symbol. • macroman—Mac Roman encoding. The default Macintosh character set. • ebcdic—EBCDIC as used on IBM AS/400 systems. • builtin—Use the encoding built in to the font. Normally used with non Latin fonts and symbols. • host—Automatically selects macroman on a Mac, ebcdic on an EBCDIC-based system, and winansi on all other systems. If you do not need to include special characters, the choice of encoding is not important. A PDF document is not like an HTML document or a word processor document. Text does not simply start at the top left and flow onto other lines as required. We need to choose where to place each line of text. As already mentioned, PDF uses points to specify locations. The origin (the x, y coordinate [0, 0]) is at the bottom left corner of the page. Given that our page is 612 by 792 points, the point (50, 700) is about two thirds of an inch from the left of the page and about one and one third inches from the top. To set our text position at this point, we use pdf_set_text_pos($pdf, 50, 700); Finally, having set up the page, we can write some text on it. To add text at the current position using the current font, we use pdf_show(). The line pdf_show($pdf,”Hello,world!”); adds the test “Hello World!” to our document. To move to the next line and write more text, we use pdf_continue_text(). To add the string “(says PHP)”, we use pdf_continue_text($pdf,”(says PHP)”); The exact location where this will appear will depend on the font and size selected. When we have finished adding elements to a page, we need to call pdf_end_page() as follows: pdf_end_page($pdf); Generating Personalized Documents in Portable Document Format (PDF) C HAPTER 30 30 GENERATING PERSONALIZED DOCUMENTS IN PDF 769 36 7842 CH30 3/6/01 3:40 PM Page 769 When we have finished the whole PDF document, we need to close it using pdf_close(). When we are generating a file, we also need to close the file. The lines pdf_close($pdf); fclose($fp); complete the generation of our Hello World document. All we need to do is provide a way to download it. echo “download the pdf <a href = ‘hello.pdf’>here</a>”; This example was derived from the C language example in the PDFlib documentation and should provide a starting point. The document we want to produce for the certificate is more complicated, including a border, a vector image, and a bitmap image. With the other two techniques, we added these features using our word processor. With PDFlib, we must add them manually. Generating Our Certificate with PDFlib In order to use PDFlib, we have chosen to make some compromises. Although it is almost cer- tainly possible to exactly duplicate the certificate we used previously, a lot more effort would be required to generate and position each element manually rather than using a tool such as Microsoft Word to help lay out the document. We are using the same text as before, including the red rosette and the bitmap signature, but we are not going to duplicate the complex border. The complete code for this script is shown in Listing 30.6. LISTING 30.6 pdflib.php—Generating Our Certificate Using PDFlib <? if(!$name||!$score) { echo “<h1>Error:</h1>This page was called incorrectly”; } else { //generate the headers to help a browser choose the correct application header( “Content-type: application/pdf” ); header( “Content-Disposition: filename=cert.pdf”); $date = date( “F d, Y” ); Building Practical PHP and MySQL Projects P ART V 770 36 7842 CH30 3/6/01 3:40 PM Page 770 LISTING 30.6 Continued // create a pdf document in memory $pdf = pdf_open(); // set up the page size in points // US letter is 11” x 8.5” // there are approximately 72 points per inch $width = 11*72; $height = 8.5*72; pdf_begin_page($pdf, $width, $height); // draw a borders $inset = 20; // space between border and page edge $border = 10; // width of main border line $inner = 2; // gap within the border //draw outer border pdf_rect($pdf, $inset-$inner, $inset-$inner, $width-2*($inset-$inner), $height-2*($inset-$inner)); pdf_stroke($pdf); //draw main border $border points wide pdf_setlinewidth($pdf, $border); pdf_rect($pdf, $inset+$border/2, $inset+$border/2, $width-2*($inset+$border/2), $height-2*($inset+$border/2)); pdf_stroke($pdf); pdf_setlinewidth($pdf, 1.0); //draw inner border pdf_rect($pdf, $inset+$border+$inner, $inset+$border+$inner, $width-2*($inset+$border+$inner), $height-2*($inset+$border+$inner)); pdf_stroke($pdf); //add text pdf_set_font($pdf, “Times-Roman”, 48, “host”); $startx = ($width - pdf_stringwidth($pdf, “PHP Certification”))/2; pdf_show_xy($pdf, “PHP Certification”, $startx, 490); pdf_set_font($pdf, “Times-Roman”, 26, “host”); Generating Personalized Documents in Portable Document Format (PDF) C HAPTER 30 30 GENERATING PERSONALIZED DOCUMENTS IN PDF 771 36 7842 CH30 3/6/01 3:40 PM Page 771 LISTING 30.6 Continued $startx = 70; pdf_show_xy($pdf, “This is to certify that:”, $startx, 430); pdf_show_xy($pdf, strtoupper($name), $startx+90, 391); pdf_set_font($pdf, “Times-Roman”, 20, “host”); pdf_show_xy($pdf, “has demonstrated that they are certifiable “. “by passing a rigorous exam”, $startx, 340); pdf_show_xy($pdf, “consisting of three multiple choice questions.”, $startx, 310); pdf_show_xy($pdf, “$name obtained a score of $score”.”%.”, $startx, 260); pdf_show_xy($pdf, “The test was set and overseen by the “, $startx, 210); pdf_show_xy($pdf, “Fictional Institute of PHP Certification”, $startx, 180); pdf_show_xy($pdf, “on $date.”, $startx, 150); pdf_show_xy($pdf, “Authorised by:”, $startx, 100); // add bitmap signature image $signature = pdf_open_image_file($pdf, “tiff”, “/htdocs/book/chapter30/signature.tif”); pdf_place_image($pdf, $signature, 200, 75, 1); pdf_close_image($pdf, $signature); pdf_setrgbcolor_fill($pdf, 0, 0, .4); //dark blue pdf_setrgbcolor_stroke($pdf, 0, 0, 0); // black // draw ribbon 1 pdf_moveto($pdf, 630, 150); pdf_lineto($pdf, 610, 55); pdf_lineto($pdf, 632, 69); pdf_lineto($pdf, 646, 49); pdf_lineto($pdf, 666, 150); pdf_closepath($pdf); pdf_fill($pdf); // outline ribbon 1 pdf_moveto($pdf, 630, 150); pdf_lineto($pdf, 610, 55); pdf_lineto($pdf, 632, 69); pdf_lineto($pdf, 646, 49); Building Practical PHP and MySQL Projects P ART V 772 36 7842 CH30 3/6/01 3:40 PM Page 772 LISTING 30.6 Continued pdf_lineto($pdf, 666, 150); pdf_closepath($pdf); pdf_stroke($pdf); // draw ribbon 2 pdf_moveto($pdf, 660, 150); pdf_lineto($pdf, 680, 49); pdf_lineto($pdf, 695, 69); pdf_lineto($pdf, 716, 55); pdf_lineto($pdf, 696, 150); pdf_closepath($pdf); pdf_fill($pdf); // outline ribbon 2 pdf_moveto($pdf, 660, 150); pdf_lineto($pdf, 680, 49); pdf_lineto($pdf, 695, 69); pdf_lineto($pdf, 716, 55); pdf_lineto($pdf, 696, 150); pdf_closepath($pdf); pdf_stroke($pdf); pdf_setrgbcolor_fill($pdf, .8, 0, 0); //red //draw rosette draw_star(665, 175, 32, 57, 10, $pdf, true); //outline rosette draw_star(665, 175, 32, 57, 10, $pdf, false); pdf_end_page($pdf); pdf_close($pdf); } function draw_star($centerx, $centery, $points, $radius, $point_size, $pdf, $filled) { $inner_radius = $radius-$point_size; for ($i = 0; $i<=$points*2; $i++ ) { $angle= ($i*2*pi())/($points*2); if($i%2) { $x = $radius*cos($angle) + $centerx; Generating Personalized Documents in Portable Document Format (PDF) C HAPTER 30 30 GENERATING PERSONALIZED DOCUMENTS IN PDF 773 36 7842 CH30 3/6/01 3:40 PM Page 773 LISTING 30.6 Continued $y = $radius*sin($angle) + $centery; } else { $x = $inner_radius*cos($angle) + $centerx; $y = $inner_radius*sin($angle) + $centery; } if($i==0) pdf_moveto($pdf, $x, $y); else if($i==$points*2) pdf_closepath($pdf); else pdf_lineto($pdf, $x, $y); } if($filled) pdf_fill($pdf); pdf_stroke($pdf); } ?> The certificate produced using this script is shown in Figure 30.7. As you can see, it is quite similar to the others, except that the border is simpler and the star looks a little different. This is because we have drawn them into the document rather than using an existing clip art file. Building Practical PHP and MySQL Projects P ART V 774 F IGURE 30.7 pdflib.php draws the certificate into a PDF document. 36 7842 CH30 3/6/01 3:40 PM Page 774 . 150); pdf_closepath($pdf); pdf_fill($pdf); // outline ribbon 1 pdf_moveto($pdf, 630, 150); pdf_lineto($pdf, 610, 55); pdf_lineto($pdf, 632, 69); pdf_lineto($pdf, 646, 49); Building Practical PHP. 150); pdf_lineto($pdf, 680, 49); pdf_lineto($pdf, 695, 69); pdf_lineto($pdf, 716, 55); pdf_lineto($pdf, 696, 150); pdf_closepath($pdf); pdf_fill($pdf); // outline ribbon 2 pdf_moveto($pdf, 660,. 660, 150); pdf_lineto($pdf, 680, 49); pdf_lineto($pdf, 695, 69); pdf_lineto($pdf, 716, 55); pdf_lineto($pdf, 696, 150); pdf_closepath($pdf); pdf_stroke($pdf); pdf_setrgbcolor_fill($pdf, .8, 0,