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