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