f0a3e45efa6eb6899246d1f969b3dd7e8d0f7991
[atutor.git] / mods / pdf_converter / fpdf.php
1 <?php\r
2 /*******************************************************************************\r
3 * Software: FPDF                                                               *\r
4 * Version:  1.53                                                               *\r
5 * Date:     2004-12-31                                                         *\r
6 * Author:   Olivier PLATHEY                                                    *\r
7 * License:  Freeware                                                           *\r
8 *                                                                              *\r
9 * You may use, modify and redistribute this software as you wish.              *\r
10 *******************************************************************************/\r
11 \r
12 if(!class_exists('FPDF'))\r
13 {\r
14 define('FPDF_VERSION','1.53');\r
15 \r
16 class FPDF\r
17 {\r
18 //Private properties\r
19 var $page;               //current page number\r
20 var $n;                  //current object number\r
21 var $offsets;            //array of object offsets\r
22 var $buffer;             //buffer holding in-memory PDF\r
23 var $pages;              //array containing pages\r
24 var $state;              //current document state\r
25 var $compress;           //compression flag\r
26 var $DefOrientation;     //default orientation\r
27 var $CurOrientation;     //current orientation\r
28 var $OrientationChanges; //array indicating orientation changes\r
29 var $k;                  //scale factor (number of points in user unit)\r
30 var $fwPt,$fhPt;         //dimensions of page format in points\r
31 var $fw,$fh;             //dimensions of page format in user unit\r
32 var $wPt,$hPt;           //current dimensions of page in points\r
33 var $w,$h;               //current dimensions of page in user unit\r
34 var $lMargin;            //left margin\r
35 var $tMargin;            //top margin\r
36 var $rMargin;            //right margin\r
37 var $bMargin;            //page break margin\r
38 var $cMargin;            //cell margin\r
39 var $x,$y;               //current position in user unit for cell positioning\r
40 var $lasth;              //height of last cell printed\r
41 var $LineWidth;          //line width in user unit\r
42 var $CoreFonts;          //array of standard font names\r
43 var $fonts;              //array of used fonts\r
44 var $FontFiles;          //array of font files\r
45 var $diffs;              //array of encoding differences\r
46 var $images;             //array of used images\r
47 var $PageLinks;          //array of links in pages\r
48 var $links;              //array of internal links\r
49 var $FontFamily;         //current font family\r
50 var $FontStyle;          //current font style\r
51 var $underline;          //underlining flag\r
52 var $CurrentFont;        //current font info\r
53 var $FontSizePt;         //current font size in points\r
54 var $FontSize;           //current font size in user unit\r
55 var $DrawColor;          //commands for drawing color\r
56 var $FillColor;          //commands for filling color\r
57 var $TextColor;          //commands for text color\r
58 var $ColorFlag;          //indicates whether fill and text colors are different\r
59 var $ws;                 //word spacing\r
60 var $AutoPageBreak;      //automatic page breaking\r
61 var $PageBreakTrigger;   //threshold used to trigger page breaks\r
62 var $InFooter;           //flag set when processing footer\r
63 var $ZoomMode;           //zoom display mode\r
64 var $LayoutMode;         //layout display mode\r
65 var $title;              //title\r
66 var $subject;            //subject\r
67 var $author;             //author\r
68 var $keywords;           //keywords\r
69 var $creator;            //creator\r
70 var $AliasNbPages;       //alias for total number of pages\r
71 var $PDFVersion;         //PDF version number\r
72 \r
73 /*******************************************************************************\r
74 *                                                                              *\r
75 *                               Public methods                                 *\r
76 *                                                                              *\r
77 *******************************************************************************/\r
78 function FPDF($orientation='P',$unit='mm',$format='A4')\r
79 {\r
80         //Some checks\r
81         $this->_dochecks();\r
82         //Initialization of properties\r
83         $this->page=0;\r
84         $this->n=2;\r
85         $this->buffer='';\r
86         $this->pages=array();\r
87         $this->OrientationChanges=array();\r
88         $this->state=0;\r
89         $this->fonts=array();\r
90         $this->FontFiles=array();\r
91         $this->diffs=array();\r
92         $this->images=array();\r
93         $this->links=array();\r
94         $this->InFooter=false;\r
95         $this->lasth=0;\r
96         $this->FontFamily='';\r
97         $this->FontStyle='';\r
98         $this->FontSizePt=12;\r
99         $this->underline=false;\r
100         $this->DrawColor='0 G';\r
101         $this->FillColor='0 g';\r
102         $this->TextColor='0 g';\r
103         $this->ColorFlag=false;\r
104         $this->ws=0;\r
105         //Standard fonts\r
106         $this->CoreFonts=array('courier'=>'Courier','courierB'=>'Courier-Bold','courierI'=>'Courier-Oblique','courierBI'=>'Courier-BoldOblique',\r
107                 'helvetica'=>'Helvetica','helveticaB'=>'Helvetica-Bold','helveticaI'=>'Helvetica-Oblique','helveticaBI'=>'Helvetica-BoldOblique',\r
108                 'times'=>'Times-Roman','timesB'=>'Times-Bold','timesI'=>'Times-Italic','timesBI'=>'Times-BoldItalic',\r
109                 'symbol'=>'Symbol','zapfdingbats'=>'ZapfDingbats');\r
110         //Scale factor\r
111         if($unit=='pt')\r
112                 $this->k=1;\r
113         elseif($unit=='mm')\r
114                 $this->k=72/25.4;\r
115         elseif($unit=='cm')\r
116                 $this->k=72/2.54;\r
117         elseif($unit=='in')\r
118                 $this->k=72;\r
119         else\r
120                 $this->Error('Incorrect unit: '.$unit);\r
121         //Page format\r
122         if(is_string($format))\r
123         {\r
124                 $format=strtolower($format);\r
125                 if($format=='a3')\r
126                         $format=array(841.89,1190.55);\r
127                 elseif($format=='a4')\r
128                         $format=array(595.28,841.89);\r
129                 elseif($format=='a5')\r
130                         $format=array(420.94,595.28);\r
131                 elseif($format=='letter')\r
132                         $format=array(612,792);\r
133                 elseif($format=='legal')\r
134                         $format=array(612,1008);\r
135                 else\r
136                         $this->Error('Unknown page format: '.$format);\r
137                 $this->fwPt=$format[0];\r
138                 $this->fhPt=$format[1];\r
139         }\r
140         else\r
141         {\r
142                 $this->fwPt=$format[0]*$this->k;\r
143                 $this->fhPt=$format[1]*$this->k;\r
144         }\r
145         $this->fw=$this->fwPt/$this->k;\r
146         $this->fh=$this->fhPt/$this->k;\r
147         //Page orientation\r
148         $orientation=strtolower($orientation);\r
149         if($orientation=='p' || $orientation=='portrait')\r
150         {\r
151                 $this->DefOrientation='P';\r
152                 $this->wPt=$this->fwPt;\r
153                 $this->hPt=$this->fhPt;\r
154         }\r
155         elseif($orientation=='l' || $orientation=='landscape')\r
156         {\r
157                 $this->DefOrientation='L';\r
158                 $this->wPt=$this->fhPt;\r
159                 $this->hPt=$this->fwPt;\r
160         }\r
161         else\r
162                 $this->Error('Incorrect orientation: '.$orientation);\r
163         $this->CurOrientation=$this->DefOrientation;\r
164         $this->w=$this->wPt/$this->k;\r
165         $this->h=$this->hPt/$this->k;\r
166         //Page margins (1 cm)\r
167         $margin=28.35/$this->k;\r
168         $this->SetMargins($margin,$margin);\r
169         //Interior cell margin (1 mm)\r
170         $this->cMargin=$margin/10;\r
171         //Line width (0.2 mm)\r
172         $this->LineWidth=.567/$this->k;\r
173         //Automatic page break\r
174         $this->SetAutoPageBreak(true,2*$margin);\r
175         //Full width display mode\r
176         $this->SetDisplayMode('fullwidth');\r
177         //Enable compression\r
178         $this->SetCompression(true);\r
179         //Set default PDF version number\r
180         $this->PDFVersion='1.3';\r
181 }\r
182 \r
183 function SetMargins($left,$top,$right=-1)\r
184 {\r
185         //Set left, top and right margins\r
186         $this->lMargin=$left;\r
187         $this->tMargin=$top;\r
188         if($right==-1)\r
189                 $right=$left;\r
190         $this->rMargin=$right;\r
191 }\r
192 \r
193 function SetLeftMargin($margin)\r
194 {\r
195         //Set left margin\r
196         $this->lMargin=$margin;\r
197         if($this->page>0 && $this->x<$margin)\r
198                 $this->x=$margin;\r
199 }\r
200 \r
201 function SetTopMargin($margin)\r
202 {\r
203         //Set top margin\r
204         $this->tMargin=$margin;\r
205 }\r
206 \r
207 function SetRightMargin($margin)\r
208 {\r
209         //Set right margin\r
210         $this->rMargin=$margin;\r
211 }\r
212 \r
213 function SetAutoPageBreak($auto,$margin=0)\r
214 {\r
215         //Set auto page break mode and triggering margin\r
216         $this->AutoPageBreak=$auto;\r
217         $this->bMargin=$margin;\r
218         $this->PageBreakTrigger=$this->h-$margin;\r
219 }\r
220 \r
221 function SetDisplayMode($zoom,$layout='continuous')\r
222 {\r
223         //Set display mode in viewer\r
224         if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom))\r
225                 $this->ZoomMode=$zoom;\r
226         else\r
227                 $this->Error('Incorrect zoom display mode: '.$zoom);\r
228         if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default')\r
229                 $this->LayoutMode=$layout;\r
230         else\r
231                 $this->Error('Incorrect layout display mode: '.$layout);\r
232 }\r
233 \r
234 function SetCompression($compress)\r
235 {\r
236         //Set page compression\r
237         if(function_exists('gzcompress'))\r
238                 $this->compress=$compress;\r
239         else\r
240                 $this->compress=false;\r
241 }\r
242 \r
243 function SetTitle($title)\r
244 {\r
245         //Title of document\r
246         $this->title=$title;\r
247 }\r
248 \r
249 function SetSubject($subject)\r
250 {\r
251         //Subject of document\r
252         $this->subject=$subject;\r
253 }\r
254 \r
255 function SetAuthor($author)\r
256 {\r
257         //Author of document\r
258         $this->author=$author;\r
259 }\r
260 \r
261 function SetKeywords($keywords)\r
262 {\r
263         //Keywords of document\r
264         $this->keywords=$keywords;\r
265 }\r
266 \r
267 function SetCreator($creator)\r
268 {\r
269         //Creator of document\r
270         $this->creator=$creator;\r
271 }\r
272 \r
273 function AliasNbPages($alias='{nb}')\r
274 {\r
275         //Define an alias for total number of pages\r
276         $this->AliasNbPages=$alias;\r
277 }\r
278 \r
279 function Error($msg)\r
280 {\r
281         //Fatal error\r
282         die('<B>FPDF error: </B>'.$msg);\r
283 }\r
284 \r
285 function Open()\r
286 {\r
287         //Begin document\r
288         $this->state=1;\r
289 }\r
290 \r
291 function Close()\r
292 {\r
293         //Terminate document\r
294         if($this->state==3)\r
295                 return;\r
296         if($this->page==0)\r
297                 $this->AddPage();\r
298         //Page footer\r
299         $this->InFooter=true;\r
300         $this->Footer();\r
301         $this->InFooter=false;\r
302         //Close page\r
303         $this->_endpage();\r
304         //Close document\r
305         $this->_enddoc();\r
306 }\r
307 \r
308 function AddPage($orientation='')\r
309 {\r
310         //Start a new page\r
311         if($this->state==0)\r
312                 $this->Open();\r
313         $family=$this->FontFamily;\r
314         $style=$this->FontStyle.($this->underline ? 'U' : '');\r
315         $size=$this->FontSizePt;\r
316         $lw=$this->LineWidth;\r
317         $dc=$this->DrawColor;\r
318         $fc=$this->FillColor;\r
319         $tc=$this->TextColor;\r
320         $cf=$this->ColorFlag;\r
321         if($this->page>0)\r
322         {\r
323                 //Page footer\r
324                 $this->InFooter=true;\r
325                 $this->Footer();\r
326                 $this->InFooter=false;\r
327                 //Close page\r
328                 $this->_endpage();\r
329         }\r
330         //Start new page\r
331         $this->_beginpage($orientation);\r
332         //Set line cap style to square\r
333         $this->_out('2 J');\r
334         //Set line width\r
335         $this->LineWidth=$lw;\r
336         $this->_out(sprintf('%.2f w',$lw*$this->k));\r
337         //Set font\r
338         if($family)\r
339                 $this->SetFont($family,$style,$size);\r
340         //Set colors\r
341         $this->DrawColor=$dc;\r
342         if($dc!='0 G')\r
343                 $this->_out($dc);\r
344         $this->FillColor=$fc;\r
345         if($fc!='0 g')\r
346                 $this->_out($fc);\r
347         $this->TextColor=$tc;\r
348         $this->ColorFlag=$cf;\r
349         //Page header\r
350         $this->Header();\r
351         //Restore line width\r
352         if($this->LineWidth!=$lw)\r
353         {\r
354                 $this->LineWidth=$lw;\r
355                 $this->_out(sprintf('%.2f w',$lw*$this->k));\r
356         }\r
357         //Restore font\r
358         if($family)\r
359                 $this->SetFont($family,$style,$size);\r
360         //Restore colors\r
361         if($this->DrawColor!=$dc)\r
362         {\r
363                 $this->DrawColor=$dc;\r
364                 $this->_out($dc);\r
365         }\r
366         if($this->FillColor!=$fc)\r
367         {\r
368                 $this->FillColor=$fc;\r
369                 $this->_out($fc);\r
370         }\r
371         $this->TextColor=$tc;\r
372         $this->ColorFlag=$cf;\r
373 }\r
374 \r
375 function Header()\r
376 {\r
377         //To be implemented in your own inherited class\r
378 }\r
379 \r
380 function Footer()\r
381 {\r
382         //To be implemented in your own inherited class\r
383 }\r
384 \r
385 function PageNo()\r
386 {\r
387         //Get current page number\r
388         return $this->page;\r
389 }\r
390 \r
391 function SetDrawColor($r,$g=-1,$b=-1)\r
392 {\r
393         //Set color for all stroking operations\r
394         if(($r==0 && $g==0 && $b==0) || $g==-1)\r
395                 $this->DrawColor=sprintf('%.3f G',$r/255);\r
396         else\r
397                 $this->DrawColor=sprintf('%.3f %.3f %.3f RG',$r/255,$g/255,$b/255);\r
398         if($this->page>0)\r
399                 $this->_out($this->DrawColor);\r
400 }\r
401 \r
402 function SetFillColor($r,$g=-1,$b=-1)\r
403 {\r
404         //Set color for all filling operations\r
405         if(($r==0 && $g==0 && $b==0) || $g==-1)\r
406                 $this->FillColor=sprintf('%.3f g',$r/255);\r
407         else\r
408                 $this->FillColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255);\r
409         $this->ColorFlag=($this->FillColor!=$this->TextColor);\r
410         if($this->page>0)\r
411                 $this->_out($this->FillColor);\r
412 }\r
413 \r
414 function SetTextColor($r,$g=-1,$b=-1)\r
415 {\r
416         //Set color for text\r
417         if(($r==0 && $g==0 && $b==0) || $g==-1)\r
418                 $this->TextColor=sprintf('%.3f g',$r/255);\r
419         else\r
420                 $this->TextColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255);\r
421         $this->ColorFlag=($this->FillColor!=$this->TextColor);\r
422 }\r
423 \r
424 function GetStringWidth($s)\r
425 {\r
426         //Get width of a string in the current font\r
427         $s=(string)$s;\r
428         $cw=&$this->CurrentFont['cw'];\r
429         $w=0;\r
430         $l=strlen($s);\r
431         for($i=0;$i<$l;$i++)\r
432                 $w+=$cw[$s{$i}];\r
433         return $w*$this->FontSize/1000;\r
434 }\r
435 \r
436 function SetLineWidth($width)\r
437 {\r
438         //Set line width\r
439         $this->LineWidth=$width;\r
440         if($this->page>0)\r
441                 $this->_out(sprintf('%.2f w',$width*$this->k));\r
442 }\r
443 \r
444 function Line($x1,$y1,$x2,$y2)\r
445 {\r
446         //Draw a line\r
447         $this->_out(sprintf('%.2f %.2f m %.2f %.2f l S',$x1*$this->k,($this->h-$y1)*$this->k,$x2*$this->k,($this->h-$y2)*$this->k));\r
448 }\r
449 \r
450 function Rect($x,$y,$w,$h,$style='')\r
451 {\r
452         //Draw a rectangle\r
453         if($style=='F')\r
454                 $op='f';\r
455         elseif($style=='FD' || $style=='DF')\r
456                 $op='B';\r
457         else\r
458                 $op='S';\r
459         $this->_out(sprintf('%.2f %.2f %.2f %.2f re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op));\r
460 }\r
461 \r
462 function AddFont($family,$style='',$file='')\r
463 {\r
464         //Add a TrueType or Type1 font\r
465         $family=strtolower($family);\r
466         if($file=='')\r
467                 $file=str_replace(' ','',$family).strtolower($style).'.php';\r
468         if($family=='arial')\r
469                 $family='helvetica';\r
470         $style=strtoupper($style);\r
471         if($style=='IB')\r
472                 $style='BI';\r
473         $fontkey=$family.$style;\r
474         if(isset($this->fonts[$fontkey]))\r
475                 $this->Error('Font already added: '.$family.' '.$style);\r
476         include($this->_getfontpath().$file);\r
477         if(!isset($name))\r
478                 $this->Error('Could not include font definition file');\r
479         $i=count($this->fonts)+1;\r
480         $this->fonts[$fontkey]=array('i'=>$i,'type'=>$type,'name'=>$name,'desc'=>$desc,'up'=>$up,'ut'=>$ut,'cw'=>$cw,'enc'=>$enc,'file'=>$file);\r
481         if($diff)\r
482         {\r
483                 //Search existing encodings\r
484                 $d=0;\r
485                 $nb=count($this->diffs);\r
486                 for($i=1;$i<=$nb;$i++)\r
487                 {\r
488                         if($this->diffs[$i]==$diff)\r
489                         {\r
490                                 $d=$i;\r
491                                 break;\r
492                         }\r
493                 }\r
494                 if($d==0)\r
495                 {\r
496                         $d=$nb+1;\r
497                         $this->diffs[$d]=$diff;\r
498                 }\r
499                 $this->fonts[$fontkey]['diff']=$d;\r
500         }\r
501         if($file)\r
502         {\r
503                 if($type=='TrueType')\r
504                         $this->FontFiles[$file]=array('length1'=>$originalsize);\r
505                 else\r
506                         $this->FontFiles[$file]=array('length1'=>$size1,'length2'=>$size2);\r
507         }\r
508 }\r
509 \r
510 function SetFont($family,$style='',$size=0)\r
511 {\r
512         //Select a font; size given in points\r
513         global $fpdf_charwidths;\r
514 \r
515         $family=strtolower($family);\r
516         if($family=='')\r
517                 $family=$this->FontFamily;\r
518         if($family=='arial')\r
519                 $family='helvetica';\r
520         elseif($family=='symbol' || $family=='zapfdingbats')\r
521                 $style='';\r
522         $style=strtoupper($style);\r
523         if(strpos($style,'U')!==false)\r
524         {\r
525                 $this->underline=true;\r
526                 $style=str_replace('U','',$style);\r
527         }\r
528         else\r
529                 $this->underline=false;\r
530         if($style=='IB')\r
531                 $style='BI';\r
532         if($size==0)\r
533                 $size=$this->FontSizePt;\r
534         //Test if font is already selected\r
535         if($this->FontFamily==$family && $this->FontStyle==$style && $this->FontSizePt==$size)\r
536                 return;\r
537         //Test if used for the first time\r
538         $fontkey=$family.$style;\r
539         if(!isset($this->fonts[$fontkey]))\r
540         {\r
541                 //Check if one of the standard fonts\r
542                 if(isset($this->CoreFonts[$fontkey]))\r
543                 {\r
544                         if(!isset($fpdf_charwidths[$fontkey]))\r
545                         {\r
546                                 //Load metric file\r
547                                 $file=$family;\r
548                                 if($family=='times' || $family=='helvetica')\r
549                                         $file.=strtolower($style);\r
550                                 include($this->_getfontpath().$file.'.php');\r
551                                 if(!isset($fpdf_charwidths[$fontkey]))\r
552                                         $this->Error('Could not include font metric file');\r
553                         }\r
554                         $i=count($this->fonts)+1;\r
555                         $this->fonts[$fontkey]=array('i'=>$i,'type'=>'core','name'=>$this->CoreFonts[$fontkey],'up'=>-100,'ut'=>50,'cw'=>$fpdf_charwidths[$fontkey]);\r
556                 }\r
557                 else\r
558                         $this->Error('Undefined font: '.$family.' '.$style);\r
559         }\r
560         //Select it\r
561         $this->FontFamily=$family;\r
562         $this->FontStyle=$style;\r
563         $this->FontSizePt=$size;\r
564         $this->FontSize=$size/$this->k;\r
565         $this->CurrentFont=&$this->fonts[$fontkey];\r
566         if($this->page>0)\r
567                 $this->_out(sprintf('BT /F%d %.2f Tf ET',$this->CurrentFont['i'],$this->FontSizePt));\r
568 }\r
569 \r
570 function SetFontSize($size)\r
571 {\r
572         //Set font size in points\r
573         if($this->FontSizePt==$size)\r
574                 return;\r
575         $this->FontSizePt=$size;\r
576         $this->FontSize=$size/$this->k;\r
577         if($this->page>0)\r
578                 $this->_out(sprintf('BT /F%d %.2f Tf ET',$this->CurrentFont['i'],$this->FontSizePt));\r
579 }\r
580 \r
581 function AddLink()\r
582 {\r
583         //Create a new internal link\r
584         $n=count($this->links)+1;\r
585         $this->links[$n]=array(0,0);\r
586         return $n;\r
587 }\r
588 \r
589 function SetLink($link,$y=0,$page=-1)\r
590 {\r
591         //Set destination of internal link\r
592         if($y==-1)\r
593                 $y=$this->y;\r
594         if($page==-1)\r
595                 $page=$this->page;\r
596         $this->links[$link]=array($page,$y);\r
597 }\r
598 \r
599 function Link($x,$y,$w,$h,$link)\r
600 {\r
601         //Put a link on the page\r
602         $this->PageLinks[$this->page][]=array($x*$this->k,$this->hPt-$y*$this->k,$w*$this->k,$h*$this->k,$link);\r
603 }\r
604 \r
605 function Text($x,$y,$txt)\r
606 {\r
607         //Output a string\r
608         $s=sprintf('BT %.2f %.2f Td (%s) Tj ET',$x*$this->k,($this->h-$y)*$this->k,$this->_escape($txt));\r
609         if($this->underline && $txt!='')\r
610                 $s.=' '.$this->_dounderline($x,$y,$txt);\r
611         if($this->ColorFlag)\r
612                 $s='q '.$this->TextColor.' '.$s.' Q';\r
613         $this->_out($s);\r
614 }\r
615 \r
616 function AcceptPageBreak()\r
617 {\r
618         //Accept automatic page break or not\r
619         return $this->AutoPageBreak;\r
620 }\r
621 \r
622 function Cell($w,$h=0,$txt='',$border=0,$ln=0,$align='',$fill=0,$link='')\r
623 {\r
624         //Output a cell\r
625         $k=$this->k;\r
626         if($this->y+$h>$this->PageBreakTrigger && !$this->InFooter && $this->AcceptPageBreak())\r
627         {\r
628                 //Automatic page break\r
629                 $x=$this->x;\r
630                 $ws=$this->ws;\r
631                 if($ws>0)\r
632                 {\r
633                         $this->ws=0;\r
634                         $this->_out('0 Tw');\r
635                 }\r
636                 $this->AddPage($this->CurOrientation);\r
637                 $this->x=$x;\r
638                 if($ws>0)\r
639                 {\r
640                         $this->ws=$ws;\r
641                         $this->_out(sprintf('%.3f Tw',$ws*$k));\r
642                 }\r
643         }\r
644         if($w==0)\r
645                 $w=$this->w-$this->rMargin-$this->x;\r
646         $s='';\r
647         if($fill==1 || $border==1)\r
648         {\r
649                 if($fill==1)\r
650                         $op=($border==1) ? 'B' : 'f';\r
651                 else\r
652                         $op='S';\r
653                 $s=sprintf('%.2f %.2f %.2f %.2f re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op);\r
654         }\r
655         if(is_string($border))\r
656         {\r
657                 $x=$this->x;\r
658                 $y=$this->y;\r
659                 if(strpos($border,'L')!==false)\r
660                         $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k);\r
661                 if(strpos($border,'T')!==false)\r
662                         $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k);\r
663                 if(strpos($border,'R')!==false)\r
664                         $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k);\r
665                 if(strpos($border,'B')!==false)\r
666                         $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k);\r
667         }\r
668         if($txt!=='')\r
669         {\r
670                 if($align=='R')\r
671                         $dx=$w-$this->cMargin-$this->GetStringWidth($txt);\r
672                 elseif($align=='C')\r
673                         $dx=($w-$this->GetStringWidth($txt))/2;\r
674                 else\r
675                         $dx=$this->cMargin;\r
676                 if($this->ColorFlag)\r
677                         $s.='q '.$this->TextColor.' ';\r
678                 $txt2=str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$txt)));\r
679                 $s.=sprintf('BT %.2f %.2f Td (%s) Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$txt2);\r
680                 if($this->underline)\r
681                         $s.=' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt);\r
682                 if($this->ColorFlag)\r
683                         $s.=' Q';\r
684                 if($link)\r
685                         $this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link);\r
686         }\r
687         if($s)\r
688                 $this->_out($s);\r
689         $this->lasth=$h;\r
690         if($ln>0)\r
691         {\r
692                 //Go to next line\r
693                 $this->y+=$h;\r
694                 if($ln==1)\r
695                         $this->x=$this->lMargin;\r
696         }\r
697         else\r
698                 $this->x+=$w;\r
699 }\r
700 \r
701 function MultiCell($w,$h,$txt,$border=0,$align='J',$fill=0)\r
702 {\r
703         //Output text with automatic or explicit line breaks\r
704         $cw=&$this->CurrentFont['cw'];\r
705         if($w==0)\r
706                 $w=$this->w-$this->rMargin-$this->x;\r
707         $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;\r
708         $s=str_replace("\r",'',$txt);\r
709         $nb=strlen($s);\r
710         if($nb>0 && $s[$nb-1]=="\n")\r
711                 $nb--;\r
712         $b=0;\r
713         if($border)\r
714         {\r
715                 if($border==1)\r
716                 {\r
717                         $border='LTRB';\r
718                         $b='LRT';\r
719                         $b2='LR';\r
720                 }\r
721                 else\r
722                 {\r
723                         $b2='';\r
724                         if(strpos($border,'L')!==false)\r
725                                 $b2.='L';\r
726                         if(strpos($border,'R')!==false)\r
727                                 $b2.='R';\r
728                         $b=(strpos($border,'T')!==false) ? $b2.'T' : $b2;\r
729                 }\r
730         }\r
731         $sep=-1;\r
732         $i=0;\r
733         $j=0;\r
734         $l=0;\r
735         $ns=0;\r
736         $nl=1;\r
737         while($i<$nb)\r
738         {\r
739                 //Get next character\r
740                 $c=$s{$i};\r
741                 if($c=="\n")\r
742                 {\r
743                         //Explicit line break\r
744                         if($this->ws>0)\r
745                         {\r
746                                 $this->ws=0;\r
747                                 $this->_out('0 Tw');\r
748                         }\r
749                         $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);\r
750                         $i++;\r
751                         $sep=-1;\r
752                         $j=$i;\r
753                         $l=0;\r
754                         $ns=0;\r
755                         $nl++;\r
756                         if($border && $nl==2)\r
757                                 $b=$b2;\r
758                         continue;\r
759                 }\r
760                 if($c==' ')\r
761                 {\r
762                         $sep=$i;\r
763                         $ls=$l;\r
764                         $ns++;\r
765                 }\r
766                 $l+=$cw[$c];\r
767                 if($l>$wmax)\r
768                 {\r
769                         //Automatic line break\r
770                         if($sep==-1)\r
771                         {\r
772                                 if($i==$j)\r
773                                         $i++;\r
774                                 if($this->ws>0)\r
775                                 {\r
776                                         $this->ws=0;\r
777                                         $this->_out('0 Tw');\r
778                                 }\r
779                                 $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);\r
780                         }\r
781                         else\r
782                         {\r
783                                 if($align=='J')\r
784                                 {\r
785                                         $this->ws=($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0;\r
786                                         $this->_out(sprintf('%.3f Tw',$this->ws*$this->k));\r
787                                 }\r
788                                 $this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill);\r
789                                 $i=$sep+1;\r
790                         }\r
791                         $sep=-1;\r
792                         $j=$i;\r
793                         $l=0;\r
794                         $ns=0;\r
795                         $nl++;\r
796                         if($border && $nl==2)\r
797                                 $b=$b2;\r
798                 }\r
799                 else\r
800                         $i++;\r
801         }\r
802         //Last chunk\r
803         if($this->ws>0)\r
804         {\r
805                 $this->ws=0;\r
806                 $this->_out('0 Tw');\r
807         }\r
808         if($border && strpos($border,'B')!==false)\r
809                 $b.='B';\r
810         $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);\r
811         $this->x=$this->lMargin;\r
812 }\r
813 \r
814 function Write($h,$txt,$link='')\r
815 {\r
816         //Output text in flowing mode\r
817         $cw=&$this->CurrentFont['cw'];\r
818         $w=$this->w-$this->rMargin-$this->x;\r
819         $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;\r
820         $s=str_replace("\r",'',$txt);\r
821         $nb=strlen($s);\r
822         $sep=-1;\r
823         $i=0;\r
824         $j=0;\r
825         $l=0;\r
826         $nl=1;\r
827         while($i<$nb)\r
828         {\r
829                 //Get next character\r
830                 $c=$s{$i};\r
831                 if($c=="\n")\r
832                 {\r
833                         //Explicit line break\r
834                         $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link);\r
835                         $i++;\r
836                         $sep=-1;\r
837                         $j=$i;\r
838                         $l=0;\r
839                         if($nl==1)\r
840                         {\r
841                                 $this->x=$this->lMargin;\r
842                                 $w=$this->w-$this->rMargin-$this->x;\r
843                                 $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;\r
844                         }\r
845                         $nl++;\r
846                         continue;\r
847                 }\r
848                 if($c==' ')\r
849                         $sep=$i;\r
850                 $l+=$cw[$c];\r
851                 if($l>$wmax)\r
852                 {\r
853                         //Automatic line break\r
854                         if($sep==-1)\r
855                         {\r
856                                 if($this->x>$this->lMargin)\r
857                                 {\r
858                                         //Move to next line\r
859                                         $this->x=$this->lMargin;\r
860                                         $this->y+=$h;\r
861                                         $w=$this->w-$this->rMargin-$this->x;\r
862                                         $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;\r
863                                         $i++;\r
864                                         $nl++;\r
865                                         continue;\r
866                                 }\r
867                                 if($i==$j)\r
868                                         $i++;\r
869                                 $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link);\r
870                         }\r
871                         else\r
872                         {\r
873                                 $this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',0,$link);\r
874                                 $i=$sep+1;\r
875                         }\r
876                         $sep=-1;\r
877                         $j=$i;\r
878                         $l=0;\r
879                         if($nl==1)\r
880                         {\r
881                                 $this->x=$this->lMargin;\r
882                                 $w=$this->w-$this->rMargin-$this->x;\r
883                                 $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;\r
884                         }\r
885                         $nl++;\r
886                 }\r
887                 else\r
888                         $i++;\r
889         }\r
890         //Last chunk\r
891         if($i!=$j)\r
892                 $this->Cell($l/1000*$this->FontSize,$h,substr($s,$j),0,0,'',0,$link);\r
893 }\r
894 \r
895 function Image($file,$x,$y,$w=0,$h=0,$type='',$link='')\r
896 {\r
897         //Put an image on the page\r
898         if(!isset($this->images[$file]))\r
899         {\r
900                 //First use of image, get info\r
901                 if($type=='')\r
902                 {\r
903                         $pos=strrpos($file,'.');\r
904                         if(!$pos)\r
905                                 $this->Error('Image file has no extension and no type was specified: '.$file);\r
906                         $type=substr($file,$pos+1);\r
907                 }\r
908                 $type=strtolower($type);\r
909                 $mqr=get_magic_quotes_runtime();\r
910                 set_magic_quotes_runtime(0);\r
911                 if($type=='jpg' || $type=='jpeg')\r
912                         $info=$this->_parsejpg($file);\r
913                 elseif($type=='png')\r
914                         $info=$this->_parsepng($file);\r
915                 else\r
916                 {\r
917                         //Allow for additional formats\r
918                         $mtd='_parse'.$type;\r
919                         if(!method_exists($this,$mtd))\r
920                                 $this->Error('Unsupported image type: '.$type);\r
921                         $info=$this->$mtd($file);\r
922                 }\r
923                 set_magic_quotes_runtime($mqr);\r
924                 $info['i']=count($this->images)+1;\r
925                 $this->images[$file]=$info;\r
926         }\r
927         else\r
928                 $info=$this->images[$file];\r
929         //Automatic width and height calculation if needed\r
930         if($w==0 && $h==0)\r
931         {\r
932                 //Put image at 72 dpi\r
933                 $w=$info['w']/$this->k;\r
934                 $h=$info['h']/$this->k;\r
935         }\r
936         if($w==0)\r
937                 $w=$h*$info['w']/$info['h'];\r
938         if($h==0)\r
939                 $h=$w*$info['h']/$info['w'];\r
940         $this->_out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q',$w*$this->k,$h*$this->k,$x*$this->k,($this->h-($y+$h))*$this->k,$info['i']));\r
941         if($link)\r
942                 $this->Link($x,$y,$w,$h,$link);\r
943 }\r
944 \r
945 function Ln($h='')\r
946 {\r
947         //Line feed; default value is last cell height\r
948         $this->x=$this->lMargin;\r
949         if(is_string($h))\r
950                 $this->y+=$this->lasth;\r
951         else\r
952                 $this->y+=$h;\r
953 }\r
954 \r
955 function GetX()\r
956 {\r
957         //Get x position\r
958         return $this->x;\r
959 }\r
960 \r
961 function SetX($x)\r
962 {\r
963         //Set x position\r
964         if($x>=0)\r
965                 $this->x=$x;\r
966         else\r
967                 $this->x=$this->w+$x;\r
968 }\r
969 \r
970 function GetY()\r
971 {\r
972         //Get y position\r
973         return $this->y;\r
974 }\r
975 \r
976 function SetY($y)\r
977 {\r
978         //Set y position and reset x\r
979         $this->x=$this->lMargin;\r
980         if($y>=0)\r
981                 $this->y=$y;\r
982         else\r
983                 $this->y=$this->h+$y;\r
984 }\r
985 \r
986 function SetXY($x,$y)\r
987 {\r
988         //Set x and y positions\r
989         $this->SetY($y);\r
990         $this->SetX($x);\r
991 }\r
992 \r
993 function Output($name='',$dest='')\r
994 {\r
995         //Output PDF to some destination\r
996         //Finish document if necessary\r
997         if($this->state<3)\r
998                 $this->Close();\r
999         //Normalize parameters\r
1000         if(is_bool($dest))\r
1001                 $dest=$dest ? 'D' : 'F';\r
1002         $dest=strtoupper($dest);\r
1003         if($dest=='')\r
1004         {\r
1005                 if($name=='')\r
1006                 {\r
1007                         $name='doc.pdf';\r
1008                         $dest='I';\r
1009                 }\r
1010                 else\r
1011                         $dest='F';\r
1012         }\r
1013         switch($dest)\r
1014         {\r
1015                 case 'I':\r
1016                         //Send to standard output\r
1017                         if(ob_get_contents())\r
1018                                 $this->Error('Some data has already been output, can\'t send PDF file');\r
1019                         if(php_sapi_name()!='cli')\r
1020                         {\r
1021                                 //We send to a browser\r
1022                                 header('Content-Type: application/pdf');\r
1023                                 if(headers_sent())\r
1024                                         $this->Error('Some data has already been output to browser, can\'t send PDF file');\r
1025                                 header('Content-Length: '.strlen($this->buffer));\r
1026                                 header('Content-disposition: inline; filename="'.$name.'"');\r
1027                         }\r
1028                         echo $this->buffer;\r
1029                         break;\r
1030                 case 'D':\r
1031                         //Download file\r
1032                         if(ob_get_contents())\r
1033                                 $this->Error('Some data has already been output, can\'t send PDF file');\r
1034                         if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE'))\r
1035                                 header('Content-Type: application/force-download');\r
1036                         else\r
1037                                 header('Content-Type: application/octet-stream');\r
1038                         if(headers_sent())\r
1039                                 $this->Error('Some data has already been output to browser, can\'t send PDF file');\r
1040                         header('Content-Length: '.strlen($this->buffer));\r
1041                         header('Content-disposition: attachment; filename="'.$name.'"');\r
1042                         echo $this->buffer;\r
1043                         break;\r
1044                 case 'F':\r
1045                         //Save to local file\r
1046                         $f=fopen($name,'wb');\r
1047                         if(!$f)\r
1048                                 $this->Error('Unable to create output file: '.$name);\r
1049                         fwrite($f,$this->buffer,strlen($this->buffer));\r
1050                         fclose($f);\r
1051                         break;\r
1052                 case 'S':\r
1053                         //Return as a string\r
1054                         return $this->buffer;\r
1055                 default:\r
1056                         $this->Error('Incorrect output destination: '.$dest);\r
1057         }\r
1058         return '';\r
1059 }\r
1060 \r
1061 /*******************************************************************************\r
1062 *                                                                              *\r
1063 *                              Protected methods                               *\r
1064 *                                                                              *\r
1065 *******************************************************************************/\r
1066 function _dochecks()\r
1067 {\r
1068         //Check for locale-related bug\r
1069         if(1.1==1)\r
1070                 $this->Error('Don\'t alter the locale before including class file');\r
1071         //Check for decimal separator\r
1072         if(sprintf('%.1f',1.0)!='1.0')\r
1073                 setlocale(LC_NUMERIC,'C');\r
1074 }\r
1075 \r
1076 function _getfontpath()\r
1077 {\r
1078         if(!defined('FPDF_FONTPATH') && is_dir(dirname(__FILE__).'/font'))\r
1079                 define('FPDF_FONTPATH',dirname(__FILE__).'/font/');\r
1080         return defined('FPDF_FONTPATH') ? FPDF_FONTPATH : '';\r
1081 }\r
1082 \r
1083 function _putpages()\r
1084 {\r
1085         $nb=$this->page;\r
1086         if(!empty($this->AliasNbPages))\r
1087         {\r
1088                 //Replace number of pages\r
1089                 for($n=1;$n<=$nb;$n++)\r
1090                         $this->pages[$n]=str_replace($this->AliasNbPages,$nb,$this->pages[$n]);\r
1091         }\r
1092         if($this->DefOrientation=='P')\r
1093         {\r
1094                 $wPt=$this->fwPt;\r
1095                 $hPt=$this->fhPt;\r
1096         }\r
1097         else\r
1098         {\r
1099                 $wPt=$this->fhPt;\r
1100                 $hPt=$this->fwPt;\r
1101         }\r
1102         $filter=($this->compress) ? '/Filter /FlateDecode ' : '';\r
1103         for($n=1;$n<=$nb;$n++)\r
1104         {\r
1105                 //Page\r
1106                 $this->_newobj();\r
1107                 $this->_out('<</Type /Page');\r
1108                 $this->_out('/Parent 1 0 R');\r
1109                 if(isset($this->OrientationChanges[$n]))\r
1110                         $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$hPt,$wPt));\r
1111                 $this->_out('/Resources 2 0 R');\r
1112                 if(isset($this->PageLinks[$n]))\r
1113                 {\r
1114                         //Links\r
1115                         $annots='/Annots [';\r
1116                         foreach($this->PageLinks[$n] as $pl)\r
1117                         {\r
1118                                 $rect=sprintf('%.2f %.2f %.2f %.2f',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]);\r
1119                                 $annots.='<</Type /Annot /Subtype /Link /Rect ['.$rect.'] /Border [0 0 0] ';\r
1120                                 if(is_string($pl[4]))\r
1121                                         $annots.='/A <</S /URI /URI '.$this->_textstring($pl[4]).'>>>>';\r
1122                                 else\r
1123                                 {\r
1124                                         $l=$this->links[$pl[4]];\r
1125                                         $h=isset($this->OrientationChanges[$l[0]]) ? $wPt : $hPt;\r
1126                                         $annots.=sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]>>',1+2*$l[0],$h-$l[1]*$this->k);\r
1127                                 }\r
1128                         }\r
1129                         $this->_out($annots.']');\r
1130                 }\r
1131                 $this->_out('/Contents '.($this->n+1).' 0 R>>');\r
1132                 $this->_out('endobj');\r
1133                 //Page content\r
1134                 $p=($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n];\r
1135                 $this->_newobj();\r
1136                 $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');\r
1137                 $this->_putstream($p);\r
1138                 $this->_out('endobj');\r
1139         }\r
1140         //Pages root\r
1141         $this->offsets[1]=strlen($this->buffer);\r
1142         $this->_out('1 0 obj');\r
1143         $this->_out('<</Type /Pages');\r
1144         $kids='/Kids [';\r
1145         for($i=0;$i<$nb;$i++)\r
1146                 $kids.=(3+2*$i).' 0 R ';\r
1147         $this->_out($kids.']');\r
1148         $this->_out('/Count '.$nb);\r
1149         $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$wPt,$hPt));\r
1150         $this->_out('>>');\r
1151         $this->_out('endobj');\r
1152 }\r
1153 \r
1154 function _putfonts()\r
1155 {\r
1156         $nf=$this->n;\r
1157         foreach($this->diffs as $diff)\r
1158         {\r
1159                 //Encodings\r
1160                 $this->_newobj();\r
1161                 $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');\r
1162                 $this->_out('endobj');\r
1163         }\r
1164         $mqr=get_magic_quotes_runtime();\r
1165         set_magic_quotes_runtime(0);\r
1166         foreach($this->FontFiles as $file=>$info)\r
1167         {\r
1168                 //Font file embedding\r
1169                 $this->_newobj();\r
1170                 $this->FontFiles[$file]['n']=$this->n;\r
1171                 $font='';\r
1172                 $f=fopen($this->_getfontpath().$file,'rb',1);\r
1173                 if(!$f)\r
1174                         $this->Error('Font file not found');\r
1175                 while(!feof($f))\r
1176                         $font.=fread($f,8192);\r
1177                 fclose($f);\r
1178                 $compressed=(substr($file,-2)=='.z');\r
1179                 if(!$compressed && isset($info['length2']))\r
1180                 {\r
1181                         $header=(ord($font{0})==128);\r
1182                         if($header)\r
1183                         {\r
1184                                 //Strip first binary header\r
1185                                 $font=substr($font,6);\r
1186                         }\r
1187                         if($header && ord($font{$info['length1']})==128)\r
1188                         {\r
1189                                 //Strip second binary header\r
1190                                 $font=substr($font,0,$info['length1']).substr($font,$info['length1']+6);\r
1191                         }\r
1192                 }\r
1193                 $this->_out('<</Length '.strlen($font));\r
1194                 if($compressed)\r
1195                         $this->_out('/Filter /FlateDecode');\r
1196                 $this->_out('/Length1 '.$info['length1']);\r
1197                 if(isset($info['length2']))\r
1198                         $this->_out('/Length2 '.$info['length2'].' /Length3 0');\r
1199                 $this->_out('>>');\r
1200                 $this->_putstream($font);\r
1201                 $this->_out('endobj');\r
1202         }\r
1203         set_magic_quotes_runtime($mqr);\r
1204         foreach($this->fonts as $k=>$font)\r
1205         {\r
1206                 //Font objects\r
1207                 $this->fonts[$k]['n']=$this->n+1;\r
1208                 $type=$font['type'];\r
1209                 $name=$font['name'];\r
1210                 if($type=='core')\r
1211                 {\r
1212                         //Standard font\r
1213                         $this->_newobj();\r
1214                         $this->_out('<</Type /Font');\r
1215                         $this->_out('/BaseFont /'.$name);\r
1216                         $this->_out('/Subtype /Type1');\r
1217                         if($name!='Symbol' && $name!='ZapfDingbats')\r
1218                                 $this->_out('/Encoding /WinAnsiEncoding');\r
1219                         $this->_out('>>');\r
1220                         $this->_out('endobj');\r
1221                 }\r
1222                 elseif($type=='Type1' || $type=='TrueType')\r
1223                 {\r
1224                         //Additional Type1 or TrueType font\r
1225                         $this->_newobj();\r
1226                         $this->_out('<</Type /Font');\r
1227                         $this->_out('/BaseFont /'.$name);\r
1228                         $this->_out('/Subtype /'.$type);\r
1229                         $this->_out('/FirstChar 32 /LastChar 255');\r
1230                         $this->_out('/Widths '.($this->n+1).' 0 R');\r
1231                         $this->_out('/FontDescriptor '.($this->n+2).' 0 R');\r
1232                         if($font['enc'])\r
1233                         {\r
1234                                 if(isset($font['diff']))\r
1235                                         $this->_out('/Encoding '.($nf+$font['diff']).' 0 R');\r
1236                                 else\r
1237                                         $this->_out('/Encoding /WinAnsiEncoding');\r
1238                         }\r
1239                         $this->_out('>>');\r
1240                         $this->_out('endobj');\r
1241                         //Widths\r
1242                         $this->_newobj();\r
1243                         $cw=&$font['cw'];\r
1244                         $s='[';\r
1245                         for($i=32;$i<=255;$i++)\r
1246                                 $s.=$cw[chr($i)].' ';\r
1247                         $this->_out($s.']');\r
1248                         $this->_out('endobj');\r
1249                         //Descriptor\r
1250                         $this->_newobj();\r
1251                         $s='<</Type /FontDescriptor /FontName /'.$name;\r
1252                         foreach($font['desc'] as $k=>$v)\r
1253                                 $s.=' /'.$k.' '.$v;\r
1254                         $file=$font['file'];\r
1255                         if($file)\r
1256                                 $s.=' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R';\r
1257                         $this->_out($s.'>>');\r
1258                         $this->_out('endobj');\r
1259                 }\r
1260                 else\r
1261                 {\r
1262                         //Allow for additional types\r
1263                         $mtd='_put'.strtolower($type);\r
1264                         if(!method_exists($this,$mtd))\r
1265                                 $this->Error('Unsupported font type: '.$type);\r
1266                         $this->$mtd($font);\r
1267                 }\r
1268         }\r
1269 }\r
1270 \r
1271 function _putimages()\r
1272 {\r
1273         $filter=($this->compress) ? '/Filter /FlateDecode ' : '';\r
1274         reset($this->images);\r
1275         while(list($file,$info)=each($this->images))\r
1276         {\r
1277                 $this->_newobj();\r
1278                 $this->images[$file]['n']=$this->n;\r
1279                 $this->_out('<</Type /XObject');\r
1280                 $this->_out('/Subtype /Image');\r
1281                 $this->_out('/Width '.$info['w']);\r
1282                 $this->_out('/Height '.$info['h']);\r
1283                 if($info['cs']=='Indexed')\r
1284                         $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]');\r
1285                 else\r
1286                 {\r
1287                         $this->_out('/ColorSpace /'.$info['cs']);\r
1288                         if($info['cs']=='DeviceCMYK')\r
1289                                 $this->_out('/Decode [1 0 1 0 1 0 1 0]');\r
1290                 }\r
1291                 $this->_out('/BitsPerComponent '.$info['bpc']);\r
1292                 if(isset($info['f']))\r
1293                         $this->_out('/Filter /'.$info['f']);\r
1294                 if(isset($info['parms']))\r
1295                         $this->_out($info['parms']);\r
1296                 if(isset($info['trns']) && is_array($info['trns']))\r
1297                 {\r
1298                         $trns='';\r
1299                         for($i=0;$i<count($info['trns']);$i++)\r
1300                                 $trns.=$info['trns'][$i].' '.$info['trns'][$i].' ';\r
1301                         $this->_out('/Mask ['.$trns.']');\r
1302                 }\r
1303                 $this->_out('/Length '.strlen($info['data']).'>>');\r
1304                 $this->_putstream($info['data']);\r
1305                 unset($this->images[$file]['data']);\r
1306                 $this->_out('endobj');\r
1307                 //Palette\r
1308                 if($info['cs']=='Indexed')\r
1309                 {\r
1310                         $this->_newobj();\r
1311                         $pal=($this->compress) ? gzcompress($info['pal']) : $info['pal'];\r
1312                         $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');\r
1313                         $this->_putstream($pal);\r
1314                         $this->_out('endobj');\r
1315                 }\r
1316         }\r
1317 }\r
1318 \r
1319 function _putxobjectdict()\r
1320 {\r
1321         foreach($this->images as $image)\r
1322                 $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');\r
1323 }\r
1324 \r
1325 function _putresourcedict()\r
1326 {\r
1327         $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');\r
1328         $this->_out('/Font <<');\r
1329         foreach($this->fonts as $font)\r
1330                 $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');\r
1331         $this->_out('>>');\r
1332         $this->_out('/XObject <<');\r
1333         $this->_putxobjectdict();\r
1334         $this->_out('>>');\r
1335 }\r
1336 \r
1337 function _putresources()\r
1338 {\r
1339         $this->_putfonts();\r
1340         $this->_putimages();\r
1341         //Resource dictionary\r
1342         $this->offsets[2]=strlen($this->buffer);\r
1343         $this->_out('2 0 obj');\r
1344         $this->_out('<<');\r
1345         $this->_putresourcedict();\r
1346         $this->_out('>>');\r
1347         $this->_out('endobj');\r
1348 }\r
1349 \r
1350 function _putinfo()\r
1351 {\r
1352         $this->_out('/Producer '.$this->_textstring('FPDF '.FPDF_VERSION));\r
1353         if(!empty($this->title))\r
1354                 $this->_out('/Title '.$this->_textstring($this->title));\r
1355         if(!empty($this->subject))\r
1356                 $this->_out('/Subject '.$this->_textstring($this->subject));\r
1357         if(!empty($this->author))\r
1358                 $this->_out('/Author '.$this->_textstring($this->author));\r
1359         if(!empty($this->keywords))\r
1360                 $this->_out('/Keywords '.$this->_textstring($this->keywords));\r
1361         if(!empty($this->creator))\r
1362                 $this->_out('/Creator '.$this->_textstring($this->creator));\r
1363         $this->_out('/CreationDate '.$this->_textstring('D:'.date('YmdHis')));\r
1364 }\r
1365 \r
1366 function _putcatalog()\r
1367 {\r
1368         $this->_out('/Type /Catalog');\r
1369         $this->_out('/Pages 1 0 R');\r
1370         if($this->ZoomMode=='fullpage')\r
1371                 $this->_out('/OpenAction [3 0 R /Fit]');\r
1372         elseif($this->ZoomMode=='fullwidth')\r
1373                 $this->_out('/OpenAction [3 0 R /FitH null]');\r
1374         elseif($this->ZoomMode=='real')\r
1375                 $this->_out('/OpenAction [3 0 R /XYZ null null 1]');\r
1376         elseif(!is_string($this->ZoomMode))\r
1377                 $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode/100).']');\r
1378         if($this->LayoutMode=='single')\r
1379                 $this->_out('/PageLayout /SinglePage');\r
1380         elseif($this->LayoutMode=='continuous')\r
1381                 $this->_out('/PageLayout /OneColumn');\r
1382         elseif($this->LayoutMode=='two')\r
1383                 $this->_out('/PageLayout /TwoColumnLeft');\r
1384 }\r
1385 \r
1386 function _putheader()\r
1387 {\r
1388         $this->_out('%PDF-'.$this->PDFVersion);\r
1389 }\r
1390 \r
1391 function _puttrailer()\r
1392 {\r
1393         $this->_out('/Size '.($this->n+1));\r
1394         $this->_out('/Root '.$this->n.' 0 R');\r
1395         $this->_out('/Info '.($this->n-1).' 0 R');\r
1396 }\r
1397 \r
1398 function _enddoc()\r
1399 {\r
1400         $this->_putheader();\r
1401         $this->_putpages();\r
1402         $this->_putresources();\r
1403         //Info\r
1404         $this->_newobj();\r
1405         $this->_out('<<');\r
1406         $this->_putinfo();\r
1407         $this->_out('>>');\r
1408         $this->_out('endobj');\r
1409         //Catalog\r
1410         $this->_newobj();\r
1411         $this->_out('<<');\r
1412         $this->_putcatalog();\r
1413         $this->_out('>>');\r
1414         $this->_out('endobj');\r
1415         //Cross-ref\r
1416         $o=strlen($this->buffer);\r
1417         $this->_out('xref');\r
1418         $this->_out('0 '.($this->n+1));\r
1419         $this->_out('0000000000 65535 f ');\r
1420         for($i=1;$i<=$this->n;$i++)\r
1421                 $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i]));\r
1422         //Trailer\r
1423         $this->_out('trailer');\r
1424         $this->_out('<<');\r
1425         $this->_puttrailer();\r
1426         $this->_out('>>');\r
1427         $this->_out('startxref');\r
1428         $this->_out($o);\r
1429         $this->_out('%%EOF');\r
1430         $this->state=3;\r
1431 }\r
1432 \r
1433 function _beginpage($orientation)\r
1434 {\r
1435         $this->page++;\r
1436         $this->pages[$this->page]='';\r
1437         $this->state=2;\r
1438         $this->x=$this->lMargin;\r
1439         $this->y=$this->tMargin;\r
1440         $this->FontFamily='';\r
1441         //Page orientation\r
1442         if(!$orientation)\r
1443                 $orientation=$this->DefOrientation;\r
1444         else\r
1445         {\r
1446                 $orientation=strtoupper($orientation{0});\r
1447                 if($orientation!=$this->DefOrientation)\r
1448                         $this->OrientationChanges[$this->page]=true;\r
1449         }\r
1450         if($orientation!=$this->CurOrientation)\r
1451         {\r
1452                 //Change orientation\r
1453                 if($orientation=='P')\r
1454                 {\r
1455                         $this->wPt=$this->fwPt;\r
1456                         $this->hPt=$this->fhPt;\r
1457                         $this->w=$this->fw;\r
1458                         $this->h=$this->fh;\r
1459                 }\r
1460                 else\r
1461                 {\r
1462                         $this->wPt=$this->fhPt;\r
1463                         $this->hPt=$this->fwPt;\r
1464                         $this->w=$this->fh;\r
1465                         $this->h=$this->fw;\r
1466                 }\r
1467                 $this->PageBreakTrigger=$this->h-$this->bMargin;\r
1468                 $this->CurOrientation=$orientation;\r
1469         }\r
1470 }\r
1471 \r
1472 function _endpage()\r
1473 {\r
1474         //End of page contents\r
1475         $this->state=1;\r
1476 }\r
1477 \r
1478 function _newobj()\r
1479 {\r
1480         //Begin a new object\r
1481         $this->n++;\r
1482         $this->offsets[$this->n]=strlen($this->buffer);\r
1483         $this->_out($this->n.' 0 obj');\r
1484 }\r
1485 \r
1486 function _dounderline($x,$y,$txt)\r
1487 {\r
1488         //Underline text\r
1489         $up=$this->CurrentFont['up'];\r
1490         $ut=$this->CurrentFont['ut'];\r
1491         $w=$this->GetStringWidth($txt)+$this->ws*substr_count($txt,' ');\r
1492         return sprintf('%.2f %.2f %.2f %.2f re f',$x*$this->k,($this->h-($y-$up/1000*$this->FontSize))*$this->k,$w*$this->k,-$ut/1000*$this->FontSizePt);\r
1493 }\r
1494 \r
1495 function _parsejpg($file)\r
1496 {\r
1497         //Extract info from a JPEG file\r
1498         $a=GetImageSize($file);\r
1499         if(!$a)\r
1500                 $this->Error('Missing or incorrect image file: '.$file);\r
1501         if($a[2]!=2)\r
1502                 $this->Error('Not a JPEG file: '.$file);\r
1503         if(!isset($a['channels']) || $a['channels']==3)\r
1504                 $colspace='DeviceRGB';\r
1505         elseif($a['channels']==4)\r
1506                 $colspace='DeviceCMYK';\r
1507         else\r
1508                 $colspace='DeviceGray';\r
1509         $bpc=isset($a['bits']) ? $a['bits'] : 8;\r
1510         //Read whole file\r
1511         $f=fopen($file,'rb');\r
1512         $data='';\r
1513         while(!feof($f))\r
1514                 $data.=fread($f,4096);\r
1515         fclose($f);\r
1516         return array('w'=>$a[0],'h'=>$a[1],'cs'=>$colspace,'bpc'=>$bpc,'f'=>'DCTDecode','data'=>$data);\r
1517 }\r
1518 \r
1519 function _parsepng($file)\r
1520 {\r
1521         //Extract info from a PNG file\r
1522         $f=fopen($file,'rb');\r
1523         if(!$f)\r
1524                 $this->Error('Can\'t open image file: '.$file);\r
1525         //Check signature\r
1526         if(fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10))\r
1527                 $this->Error('Not a PNG file: '.$file);\r
1528         //Read header chunk\r
1529         fread($f,4);\r
1530         if(fread($f,4)!='IHDR')\r
1531                 $this->Error('Incorrect PNG file: '.$file);\r
1532         $w=$this->_freadint($f);\r
1533         $h=$this->_freadint($f);\r
1534         $bpc=ord(fread($f,1));\r
1535         if($bpc>8)\r
1536                 $this->Error('16-bit depth not supported: '.$file);\r
1537         $ct=ord(fread($f,1));\r
1538         if($ct==0)\r
1539                 $colspace='DeviceGray';\r
1540         elseif($ct==2)\r
1541                 $colspace='DeviceRGB';\r
1542         elseif($ct==3)\r
1543                 $colspace='Indexed';\r
1544         else\r
1545                 $this->Error('Alpha channel not supported: '.$file);\r
1546         if(ord(fread($f,1))!=0)\r
1547                 $this->Error('Unknown compression method: '.$file);\r
1548         if(ord(fread($f,1))!=0)\r
1549                 $this->Error('Unknown filter method: '.$file);\r
1550         if(ord(fread($f,1))!=0)\r
1551                 $this->Error('Interlacing not supported: '.$file);\r
1552         fread($f,4);\r
1553         $parms='/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';\r
1554         //Scan chunks looking for palette, transparency and image data\r
1555         $pal='';\r
1556         $trns='';\r
1557         $data='';\r
1558         do\r
1559         {\r
1560                 $n=$this->_freadint($f);\r
1561                 $type=fread($f,4);\r
1562                 if($type=='PLTE')\r
1563                 {\r
1564                         //Read palette\r
1565                         $pal=fread($f,$n);\r
1566                         fread($f,4);\r
1567                 }\r
1568                 elseif($type=='tRNS')\r
1569                 {\r
1570                         //Read transparency info\r
1571                         $t=fread($f,$n);\r
1572                         if($ct==0)\r
1573                                 $trns=array(ord(substr($t,1,1)));\r
1574                         elseif($ct==2)\r
1575                                 $trns=array(ord(substr($t,1,1)),ord(substr($t,3,1)),ord(substr($t,5,1)));\r
1576                         else\r
1577                         {\r
1578                                 $pos=strpos($t,chr(0));\r
1579                                 if($pos!==false)\r
1580                                         $trns=array($pos);\r
1581                         }\r
1582                         fread($f,4);\r
1583                 }\r
1584                 elseif($type=='IDAT')\r
1585                 {\r
1586                         //Read image data block\r
1587                         $data.=fread($f,$n);\r
1588                         fread($f,4);\r
1589                 }\r
1590                 elseif($type=='IEND')\r
1591                         break;\r
1592                 else\r
1593                         fread($f,$n+4);\r
1594         }\r
1595         while($n);\r
1596         if($colspace=='Indexed' && empty($pal))\r
1597                 $this->Error('Missing palette in '.$file);\r
1598         fclose($f);\r
1599         return array('w'=>$w,'h'=>$h,'cs'=>$colspace,'bpc'=>$bpc,'f'=>'FlateDecode','parms'=>$parms,'pal'=>$pal,'trns'=>$trns,'data'=>$data);\r
1600 }\r
1601 \r
1602 function _freadint($f)\r
1603 {\r
1604         //Read a 4-byte integer from file\r
1605         $a=unpack('Ni',fread($f,4));\r
1606         return $a['i'];\r
1607 }\r
1608 \r
1609 function _textstring($s)\r
1610 {\r
1611         //Format a text string\r
1612         return '('.$this->_escape($s).')';\r
1613 }\r
1614 \r
1615 function _escape($s)\r
1616 {\r
1617         //Add \ before \, ( and )\r
1618         return str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$s)));\r
1619 }\r
1620 \r
1621 function _putstream($s)\r
1622 {\r
1623         $this->_out('stream');\r
1624         $this->_out($s);\r
1625         $this->_out('endstream');\r
1626 }\r
1627 \r
1628 function _out($s)\r
1629 {\r
1630         //Add a line to the document\r
1631         if($this->state==2)\r
1632                 $this->pages[$this->page].=$s."\n";\r
1633         else\r
1634                 $this->buffer.=$s."\n";\r
1635 }\r
1636 //End of class\r
1637 }\r
1638 \r
1639 //Handle special IE contype request\r
1640 if(isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT']=='contype')\r
1641 {\r
1642         header('Content-Type: application/pdf');\r
1643         exit;\r
1644 }\r
1645 \r
1646 }\r
1647 ?>\r