vendor/setasign/fpdf/fpdf.php line 475

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