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