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