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