PluginProbe
ERP: Complete HR, Accounting & CRM Suite Built for WooCommerce / 1.2.6
ERP: Complete HR, Accounting & CRM Suite Built for WooCommerce v1.2.6
1.17.9 1.17.8 1.17.7 1.17.6 1.17.5 1.17.4 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.3.0 1.3.1 1.3.11 1.3.12 1.3.13 1.3.14 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 All 143 releases
erp / includes / pdf / tfpdf.php

tfpdf.php in ERP: Complete HR, Accounting & CRM Suite Built for WooCommerce 1.2.6, at includes/pdf/tfpdf.php

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