PluginProbe
Taskbuilder – Project Management & Task Management Tool With Kanban Board / 6.0.0
Taskbuilder – Project Management & Task Management Tool With Kanban Board v6.0.0
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 1.0.6 All 57 releases
taskbuilder / asset / lib / fpdf / fpdf.php

fpdf.php in Taskbuilder – Project Management & Task Management Tool With Kanban Board 6.0.0, at asset/lib/fpdf/fpdf.php

1,936 lines 49.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 ?>
1936