From 3817deb492e1d607d469ede3731a7bee2948c21e Mon Sep 17 00:00:00 2001 From: sparkyx Date: Sun, 17 Dec 2023 12:09:11 +0100 Subject: [PATCH] =?UTF-8?q?TFPDF=20mise=20=C3=A0=20jour=201.33?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/tfpdf/tfpdf.php | 1198 +++++++++++++++++++++------------------ 1 file changed, 636 insertions(+), 562 deletions(-) diff --git a/include/tfpdf/tfpdf.php b/include/tfpdf/tfpdf.php index e1f51c794..b52048999 100644 --- a/include/tfpdf/tfpdf.php +++ b/include/tfpdf/tfpdf.php @@ -1,95 +1,94 @@ * +* Version: 1.33 * +* Date: 2022-12-20 * +* Authors: Ian Back * +* Tycho Veltmeijer (versions 1.30+) * * License: LGPL * *******************************************************************************/ -define('tFPDF_VERSION','1.24'); -#[AllowDynamicProperties] class tFPDF { - -var $unifontSubset; -var $page; // current page number -var $n; // current object number -var $offsets; // array of object offsets -var $buffer; // buffer holding in-memory PDF -var $pages; // array containing pages -var $state; // current document state -var $compress; // compression flag -var $k; // scale factor (number of points in user unit) -var $DefOrientation; // default orientation -var $CurOrientation; // current orientation -var $StdPageSizes; // standard page sizes -var $DefPageSize; // default page size -var $CurPageSize; // current page size -var $PageSizes; // used for pages with non default sizes or orientations -var $wPt, $hPt; // dimensions of current page in points -var $w, $h; // dimensions of current page in user unit -var $lMargin; // left margin -var $tMargin; // top margin -var $rMargin; // right margin -var $bMargin; // page break margin -var $cMargin; // cell margin -var $x, $y; // current position in user unit -var $lasth; // height of last printed cell -var $LineWidth; // line width in user unit -var $fontpath; // path containing fonts -var $CoreFonts; // array of core font names -var $fonts; // array of used fonts -var $FontFiles; // array of font files -var $diffs; // array of encoding differences -var $FontFamily; // current font family -var $FontStyle; // current font style -var $underline; // underlining flag -var $CurrentFont; // current font info -var $FontSizePt; // current font size in points -var $FontSize; // current font size in user unit -var $DrawColor; // commands for drawing color -var $FillColor; // commands for filling color -var $TextColor; // commands for text color -var $ColorFlag; // indicates whether fill and text colors are different -var $ws; // word spacing -var $images; // array of used images -var $PageLinks; // array of links in pages -var $links; // array of internal links -var $AutoPageBreak; // automatic page breaking -var $PageBreakTrigger; // threshold used to trigger page breaks -var $InHeader; // flag set when processing header -var $InFooter; // flag set when processing footer -var $ZoomMode; // zoom display mode -var $LayoutMode; // layout display mode -var $title; // title -var $subject; // subject -var $author; // author -var $keywords; // keywords -var $creator; // creator -var $AliasNbPages; // alias for total number of pages -var $PDFVersion; // PDF version number +const VERSION = '1.33'; +protected $unifontSubset; +protected $page; // current page number +protected $n; // current object number +protected $offsets; // array of object offsets +protected $buffer; // buffer holding in-memory PDF +protected $pages; // array containing pages +protected $state; // current document state +protected $compress; // compression flag +protected $k; // scale factor (number of points in user unit) +protected $DefOrientation; // default orientation +protected $CurOrientation; // current orientation +protected $StdPageSizes; // standard page sizes +protected $DefPageSize; // default page size +protected $CurPageSize; // current page size +protected $CurRotation; // current page rotation +protected $PageInfo; // page-related data +protected $wPt, $hPt; // dimensions of current page in points +protected $w, $h; // dimensions of current page in user unit +protected $lMargin; // left margin +protected $tMargin; // top margin +protected $rMargin; // right margin +protected $bMargin; // page break margin +protected $cMargin; // cell margin +protected $x, $y; // current position in user unit +protected $lasth; // height of last printed cell +protected $LineWidth; // line width in user unit +protected $fontpath; // path containing fonts +protected $CoreFonts; // array of core font names +protected $fonts; // array of used fonts +protected $FontFiles; // array of font files +protected $encodings; // array of encodings +protected $cmaps; // array of ToUnicode CMaps +protected $FontFamily; // current font family +protected $FontStyle; // current font style +protected $underline; // underlining flag +protected $CurrentFont; // current font info +protected $FontSizePt; // current font size in points +protected $FontSize; // current font size in user unit +protected $DrawColor; // commands for drawing color +protected $FillColor; // commands for filling color +protected $TextColor; // commands for text color +protected $ColorFlag; // indicates whether fill and text colors are different +protected $WithAlpha; // indicates whether alpha channel is used +protected $ws; // word spacing +protected $images; // array of used images +protected $PageLinks; // array of links in pages +protected $links; // array of internal links +protected $AutoPageBreak; // automatic page breaking +protected $PageBreakTrigger; // threshold used to trigger page breaks +protected $InHeader; // flag set when processing header +protected $InFooter; // flag set when processing footer +protected $AliasNbPages; // alias for total number of pages +protected $ZoomMode; // zoom display mode +protected $LayoutMode; // layout display mode +protected $metadata; // document properties +protected $CreationDate; // document creation date +protected $PDFVersion; // PDF version number /******************************************************************************* -* * * Public methods * -* * *******************************************************************************/ + function __construct($orientation='P', $unit='mm', $size='A4') { // Some checks $this->_dochecks(); // Initialization of properties + $this->state = 0; $this->page = 0; $this->n = 2; $this->buffer = ''; $this->pages = array(); - $this->PageSizes = array(); - $this->state = 0; + $this->PageInfo = array(); $this->fonts = array(); $this->FontFiles = array(); - $this->diffs = array(); + $this->encodings = array(); + $this->cmaps = array(); $this->images = array(); $this->links = array(); $this->InHeader = false; @@ -103,6 +102,7 @@ function __construct($orientation='P', $unit='mm', $size='A4') $this->FillColor = '0 g'; $this->TextColor = '0 g'; $this->ColorFlag = false; + $this->WithAlpha = false; $this->ws = 0; // Font path if(defined('FPDF_FONTPATH')) @@ -153,6 +153,8 @@ function __construct($orientation='P', $unit='mm', $size='A4') $this->CurOrientation = $this->DefOrientation; $this->wPt = $this->w*$this->k; $this->hPt = $this->h*$this->k; + // Page rotation + $this->CurRotation = 0; // Page margins (1 cm) $margin = 28.35/$this->k; $this->SetMargins($margin,$margin); @@ -166,6 +168,8 @@ function __construct($orientation='P', $unit='mm', $size='A4') $this->SetDisplayMode('default'); // Enable compression $this->SetCompression(true); + // Metadata + $this->metadata = array('Producer'=>'tFPDF '.self::VERSION); // Set default PDF version number $this->PDFVersion = '1.3'; } @@ -233,41 +237,31 @@ function SetCompression($compress) function SetTitle($title, $isUTF8=false) { // Title of document - if($isUTF8) - $title = $this->_UTF8toUTF16($title); - $this->title = $title; -} - -function SetSubject($subject, $isUTF8=false) -{ - // Subject of document - if($isUTF8) - $subject = $this->_UTF8toUTF16($subject); - $this->subject = $subject; + $this->metadata['Title'] = $isUTF8 ? $title : $this->_UTF8encode($title); } function SetAuthor($author, $isUTF8=false) { // Author of document - if($isUTF8) - $author = $this->_UTF8toUTF16($author); - $this->author = $author; + $this->metadata['Author'] = $isUTF8 ? $author : $this->_UTF8encode($author); +} + +function SetSubject($subject, $isUTF8=false) +{ + // Subject of document + $this->metadata['Subject'] = $isUTF8 ? $subject : $this->_UTF8encode($subject); } function SetKeywords($keywords, $isUTF8=false) { // Keywords of document - if($isUTF8) - $keywords = $this->_UTF8toUTF16($keywords); - $this->keywords = $keywords; + $this->metadata['Keywords'] = $isUTF8 ? $keywords : $this->_UTF8encode($keywords); } function SetCreator($creator, $isUTF8=false) { // Creator of document - if($isUTF8) - $creator = $this->_UTF8toUTF16($creator); - $this->creator = $creator; + $this->metadata['Creator'] = $isUTF8 ? $creator : $this->_UTF8encode($creator); } function AliasNbPages($alias='{nb}') @@ -279,13 +273,7 @@ function AliasNbPages($alias='{nb}') function Error($msg) { // Fatal error - die('FPDF error: '.$msg); -} - -function Open() -{ - // Begin document - $this->state = 1; + throw new Exception('tFPDF error: '.$msg); } function Close() @@ -305,11 +293,11 @@ function Close() $this->_enddoc(); } -function AddPage($orientation='', $size='') +function AddPage($orientation='', $size='', $rotation=0) { // Start a new page - if($this->state==0) - $this->Open(); + if($this->state==3) + $this->Error('The document is closed'); $family = $this->FontFamily; $style = $this->FontStyle.($this->underline ? 'U' : ''); $fontsize = $this->FontSizePt; @@ -328,7 +316,7 @@ function AddPage($orientation='', $size='') $this->_endpage(); } // Start new page - $this->_beginpage($orientation,$size); + $this->_beginpage($orientation,$size,$rotation); // Set line cap style to square $this->_out('2 J'); // Set line width @@ -427,12 +415,12 @@ function GetStringWidth($s) { // Get width of a string in the current font $s = (string)$s; - $cw = &$this->CurrentFont['cw']; + $cw = $this->CurrentFont['cw']; $w=0; if ($this->unifontSubset) { $unicode = $this->UTF8StringToArray($s); foreach($unicode as $char) { - if (isset($cw[$char])) { $w += (ord($cw[2*$char])<<8) + ord($cw[2*$char+1]); } + if (isset($cw[2*$char])) { $w += (ord($cw[2*$char])<<8) + ord($cw[2*$char+1]); } else if($char>0 && $char<128 && isset($cw[chr($char)])) { $w += $cw[chr($char)]; } else if(isset($this->CurrentFont['desc']['MissingWidth'])) { $w += $this->CurrentFont['desc']['MissingWidth']; } else if(isset($this->CurrentFont['MissingWidth'])) { $w += $this->CurrentFont['MissingWidth']; } @@ -479,7 +467,7 @@ function AddFont($family, $style='', $file='', $uni=false) $family = strtolower($family); $style = strtoupper($style); if($style=='IB') - $style='BI'; + $style = 'BI'; if($file=='') { if ($uni) { $file = str_replace(' ','',$family).strtolower($style).'.ttf'; @@ -491,11 +479,10 @@ function AddFont($family, $style='', $file='', $uni=false) $fontkey = $family.$style; if(isset($this->fonts[$fontkey])) return; - if ($uni) { if (defined("_SYSTEM_TTFONTS") && file_exists(_SYSTEM_TTFONTS.$file )) { $ttffilename = _SYSTEM_TTFONTS.$file ; } - else { $ttffilename = $this->_getfontpath().'unifont/'.$file ; } - $unifilename = $this->_getfontpath().'unifont/'.strtolower(substr($file ,0,(strpos($file ,'.')))); + else { $ttffilename = $this->fontpath.'unifont/'.$file ; } + $unifilename = $this->fontpath.'unifont/'.strtolower(substr($file ,0,(strpos($file ,'.')))); $name = ''; $originalsize = 0; $ttfstat = stat($ttffilename); @@ -504,7 +491,7 @@ function AddFont($family, $style='', $file='', $uni=false) } if (!isset($type) || !isset($name) || $originalsize != $ttfstat['size']) { $ttffile = $ttffilename; - require_once($this->_getfontpath().'unifont/ttfonts.php'); + require_once($this->fontpath.'unifont/ttfonts.php'); $ttf = new TTFontFile(); $ttf->getMetrics($ttffile); $cw = $ttf->charWidths; @@ -533,7 +520,7 @@ function AddFont($family, $style='', $file='', $uni=false) $s.='$originalsize='.$originalsize.";\n"; $s.='$fontkey=\''.$fontkey."';\n"; $s.="?>"; - if (is_writable(dirname($this->_getfontpath().'unifont/'.'x'))) { + if (is_writable(dirname($this->fontpath.'unifont/'.'x'))) { $fh = fopen($unifilename.'.mtx.php',"w"); fwrite($fh,$s,strlen($s)); fclose($fh); @@ -561,17 +548,6 @@ function AddFont($family, $style='', $file='', $uni=false) else { $info = $this->_loadfont($file); $info['i'] = count($this->fonts)+1; - if(!empty($info['diff'])) - { - // Search existing encodings - $n = array_search($info['diff'],$this->diffs); - if(!$n) - { - $n = count($this->diffs)+1; - $this->diffs[$n] = $info['diff']; - } - $info['diffn'] = $n; - } if(!empty($info['file'])) { // Embedded font @@ -606,6 +582,7 @@ function SetFont($family, $style='', $size=0) // Test if font is already selected if($this->FontFamily==$family && $this->FontStyle==$style && $this->FontSizePt==$size) return; + // Test if font is already loaded $fontkey = $family.$style; if(!isset($this->fonts[$fontkey])) @@ -674,6 +651,9 @@ function Link($x, $y, $w, $h, $link) function Text($x, $y, $txt) { // Output a string + $txt = (string)$txt; + if(!isset($this->CurrentFont)) + $this->Error('No font has been set'); if ($this->unifontSubset) { $txt2 = '('.$this->_escape($this->UTF8ToUTF16BE($txt, false)).')'; @@ -699,6 +679,7 @@ function AcceptPageBreak() function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='') { // Output a cell + $txt = (string)$txt; $k = $this->k; if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak()) { @@ -710,7 +691,7 @@ function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link $this->ws = 0; $this->_out('0 Tw'); } - $this->AddPage($this->CurOrientation,$this->CurPageSize); + $this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation); $this->x = $x; if($ws>0) { @@ -744,6 +725,8 @@ function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link } if($txt!=='') { + if(!isset($this->CurrentFont)) + $this->Error('No font has been set'); if($align=='R') $dx = $w-$this->cMargin-$this->GetStringWidth($txt); elseif($align=='C') @@ -752,7 +735,6 @@ function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link $dx = $this->cMargin; if($this->ColorFlag) $s .= 'q '.$this->TextColor.' '; - // If multibyte, Tw has no effect - do word spacing using an adjustment before each space if ($this->ws && $this->unifontSubset) { foreach($this->UTF8StringToArray($txt) as $uni) @@ -781,7 +763,7 @@ function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link $this->CurrentFont['subset'][$uni] = $uni; } else - $txt2='('.str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$txt))).')'; + $txt2='('.$this->_escape($txt).')'; $s .= sprintf('BT %.2F %.2F Td %s Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$txt2); } if($this->underline) @@ -808,11 +790,14 @@ function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false) { // Output text with automatic or explicit line breaks - $cw = &$this->CurrentFont['cw']; + if(!isset($this->CurrentFont)) + $this->Error('No font has been set'); + $cw = $this->CurrentFont['cw']; if($w==0) $w = $this->w-$this->rMargin-$this->x; $wmax = ($w-2*$this->cMargin); - $s = str_replace("\r",'',$txt); + //$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize; + $s = str_replace("\r",'',(string)$txt); if ($this->unifontSubset) { $nb=mb_strlen($s, 'utf-8'); while($nb>0 && mb_substr($s,$nb-1,1,'utf-8')=="\n") $nb--; @@ -955,11 +940,12 @@ function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false) function Write($h, $txt, $link='') { // Output text in flowing mode - $cw = &$this->CurrentFont['cw']; + if(!isset($this->CurrentFont)) + $this->Error('No font has been set'); + $cw = $this->CurrentFont['cw']; $w = $this->w-$this->rMargin-$this->x; - $wmax = ($w-2*$this->cMargin); - $s = str_replace("\r",'',$txt); + $s = str_replace("\r",'',(string)$txt); if ($this->unifontSubset) { $nb = mb_strlen($s, 'UTF-8'); if($nb==1 && $s==" ") { @@ -988,10 +974,10 @@ function Write($h, $txt, $link='') { // Explicit line break if ($this->unifontSubset) { - $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,2,'',0,$link); + $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,2,'',false,$link); } else { - $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link); + $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',false,$link); } $i++; $sep = -1; @@ -1031,19 +1017,19 @@ function Write($h, $txt, $link='') if($i==$j) $i++; if ($this->unifontSubset) { - $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,2,'',0,$link); + $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,2,'',false,$link); } else { - $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link); + $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',false,$link); } } else { if ($this->unifontSubset) { - $this->Cell($w,$h,mb_substr($s,$j,$sep-$j,'UTF-8'),0,2,'',0,$link); + $this->Cell($w,$h,mb_substr($s,$j,$sep-$j,'UTF-8'),0,2,'',false,$link); } else { - $this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',0,$link); + $this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',false,$link); } $i = $sep+1; } @@ -1064,17 +1050,17 @@ function Write($h, $txt, $link='') // Last chunk if($i!=$j) { if ($this->unifontSubset) { - $this->Cell($l,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,0,'',0,$link); + $this->Cell($l,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,0,'',false,$link); } else { - $this->Cell($l,$h,substr($s,$j),0,0,'',0,$link); + $this->Cell($l,$h,substr($s,$j),0,0,'',false,$link); } } } function Ln($h=null) { - // Line feed; default value is last cell height + // Line feed; default value is the last cell height $this->x = $this->lMargin; if($h===null) $this->y += $this->lasth; @@ -1085,6 +1071,8 @@ function Ln($h=null) function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='') { // Put an image on the page + if($file=='') + $this->Error('Image file name is empty'); if(!isset($this->images[$file])) { // First use of this image, get info @@ -1131,7 +1119,7 @@ function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='') { // Automatic page break $x2 = $this->x; - $this->AddPage($this->CurOrientation,$this->CurPageSize); + $this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation); $this->x = $x2; } $y = $this->y; @@ -1145,6 +1133,18 @@ function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='') $this->Link($x,$y,$w,$h,$link); } +function GetPageWidth() +{ + // Get current page width + return $this->w; +} + +function GetPageHeight() +{ + // Get current page height + return $this->h; +} + function GetX() { // Get x position @@ -1166,40 +1166,40 @@ function GetY() return $this->y; } -function SetY($y) +function SetY($y, $resetX=true) { - // Set y position and reset x - $this->x = $this->lMargin; + // Set y position and optionally reset x if($y>=0) $this->y = $y; else $this->y = $this->h+$y; + if($resetX) + $this->x = $this->lMargin; } function SetXY($x, $y) { // Set x and y positions - $this->SetY($y); $this->SetX($x); + $this->SetY($y,false); } -function Output($name='', $dest='') +function Output($dest='', $name='', $isUTF8=false) { // Output PDF to some destination - if($this->state<3) - $this->Close(); - $dest = strtoupper($dest); - if($dest=='') + $this->Close(); + if(strlen($name)==1 && strlen($dest)!=1) { - if($name=='') - { - $name = 'doc.pdf'; - $dest = 'I'; - } - else - $dest = 'F'; + // Fix parameter order + $tmp = $dest; + $dest = $name; + $name = $tmp; } - switch($dest) + if($dest=='') + $dest = 'I'; + if($name=='') + $name = 'doc.pdf'; + switch(strtoupper($dest)) { case 'I': // Send to standard output @@ -1208,7 +1208,7 @@ function Output($name='', $dest='') { // We send to a browser header('Content-Type: application/pdf'); - header('Content-Disposition: inline; filename="'.$name.'"'); + header('Content-Disposition: inline; '.$this->_httpencode('filename',$name,$isUTF8)); header('Cache-Control: private, max-age=0, must-revalidate'); header('Pragma: public'); } @@ -1217,19 +1217,16 @@ function Output($name='', $dest='') case 'D': // Download file $this->_checkoutput(); - header('Content-Type: application/x-download'); - header('Content-Disposition: attachment; filename="'.$name.'"'); + header('Content-Type: application/pdf'); + header('Content-Disposition: attachment; '.$this->_httpencode('filename',$name,$isUTF8)); header('Cache-Control: private, max-age=0, must-revalidate'); header('Pragma: public'); echo $this->buffer; break; case 'F': // Save to local file - $f = fopen($name,'wb'); - if(!$f) + if(!file_put_contents($name,$this->buffer)) $this->Error('Unable to create output file: '.$name); - fwrite($f,$this->buffer,strlen($this->buffer)); - fclose($f); break; case 'S': // Return as a string @@ -1241,29 +1238,17 @@ function Output($name='', $dest='') } /******************************************************************************* -* * * Protected methods * -* * *******************************************************************************/ -function _dochecks() + +protected function _dochecks() { - // Check availability of %F - if(sprintf('%.1F',1.0)!='1.0') - $this->Error('This version of PHP is not supported'); // Check availability of mbstring if(!function_exists('mb_strlen')) $this->Error('mbstring extension is not available'); - // Check mbstring overloading - if(ini_get('mbstring.func_overload') & 2) - $this->Error('mbstring overloading must be disabled'); } -function _getfontpath() -{ - return $this->fontpath; -} - -function _checkoutput() +protected function _checkoutput() { if(PHP_SAPI!='cli') { @@ -1276,14 +1261,14 @@ function _checkoutput() if(preg_match('/^(\xEF\xBB\xBF)?\s*$/',ob_get_contents())) { // It contains only a UTF-8 BOM and/or whitespace, let's clean it - ob_end_clean(); + ob_clean(); } else $this->Error("Some data has already been output, can't send PDF file"); } } -function _getpagesize($size) +protected function _getpagesize($size) { if(is_string($size)) { @@ -1302,10 +1287,11 @@ function _getpagesize($size) } } -function _beginpage($orientation, $size) +protected function _beginpage($orientation, $size, $rotation) { $this->page++; $this->pages[$this->page] = ''; + $this->PageLinks[$this->page] = array(); $this->state = 2; $this->x = $this->lMargin; $this->y = $this->tMargin; @@ -1339,74 +1325,88 @@ function _beginpage($orientation, $size) $this->CurPageSize = $size; } if($orientation!=$this->DefOrientation || $size[0]!=$this->DefPageSize[0] || $size[1]!=$this->DefPageSize[1]) - $this->PageSizes[$this->page] = array($this->wPt, $this->hPt); + $this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt); + if($rotation!=0) + { + if($rotation%90!=0) + $this->Error('Incorrect rotation value: '.$rotation); + $this->PageInfo[$this->page]['rotation'] = $rotation; + } + $this->CurRotation = $rotation; } -function _endpage() +protected function _endpage() { $this->state = 1; } -function _loadfont($font) +protected function _loadfont($font) { // Load a font definition file from the font directory + if(strpos($font,'/')!==false || strpos($font,"\\")!==false) + $this->Error('Incorrect font definition file name: '.$font); include($this->fontpath.$font); - $a = get_defined_vars(); - if(!isset($a['name'])) + if(!isset($name)) $this->Error('Could not include font definition file'); - return $a; + if(isset($enc)) + $enc = strtolower($enc); + if(!isset($subsetted)) + $subsetted = false; + return get_defined_vars(); } -function _escape($s) +protected function _isascii($s) { - // Escape special characters in strings - $s = str_replace('\\','\\\\',$s); - $s = str_replace('(','\\(',$s); - $s = str_replace(')','\\)',$s); - $s = str_replace("\r",'\\r',$s); - return $s; + // Test if string is ASCII + $nb = strlen($s); + for($i=0;$i<$nb;$i++) + { + if(ord($s[$i])>127) + return false; + } + return true; } -function _textstring($s) +protected function _httpencode($param, $value, $isUTF8) +{ + // Encode HTTP header field parameter + if($this->_isascii($value)) + return $param.'="'.$value.'"'; + if(!$isUTF8) + $value = $this->_UTF8encode($value); + return $param."*=UTF-8''".rawurlencode($value); +} + +protected function _UTF8encode($s) +{ + // Convert ISO-8859-1 to UTF-8 + return mb_convert_encoding($s,'UTF-8','ISO-8859-1'); +} + +protected function _UTF8toUTF16($s) +{ + // Convert UTF-8 to UTF-16BE with BOM + return "\xFE\xFF".mb_convert_encoding($s,'UTF-16BE','UTF-8'); +} + +protected function _escape($s) +{ + // Escape special characters + if(strpos($s,'(')!==false || strpos($s,')')!==false || strpos($s,'\\')!==false || strpos($s,"\r")!==false) + return str_replace(array('\\','(',')',"\r"), array('\\\\','\\(','\\)','\\r'), $s); + else + return $s; +} + +protected function _textstring($s) { // Format a text string + if(!$this->_isascii($s)) + $s = $this->_UTF8toUTF16($s); return '('.$this->_escape($s).')'; } -function _UTF8toUTF16($s) -{ - // Convert UTF-8 to UTF-16BE with BOM - $res = "\xFE\xFF"; - $nb = strlen($s); - $i = 0; - while($i<$nb) - { - $c1 = ord($s[$i++]); - if($c1>=224) - { - // 3-byte character - $c2 = ord($s[$i++]); - $c3 = ord($s[$i++]); - $res .= chr((($c1 & 0x0F)<<4) + (($c2 & 0x3C)>>2)); - $res .= chr((($c2 & 0x03)<<6) + ($c3 & 0x3F)); - } - elseif($c1>=192) - { - // 2-byte character - $c2 = ord($s[$i++]); - $res .= chr(($c1 & 0x1C)>>2); - $res .= chr((($c1 & 0x03)<<6) + ($c2 & 0x3F)); - } - else - { - // Single-byte character - $res .= "\0".chr($c1); - } - } - return $res; -} - -function _dounderline($x, $y, $txt) +protected function _dounderline($x, $y, $txt) { // Underline text $up = $this->CurrentFont['up']; @@ -1415,7 +1415,7 @@ function _dounderline($x, $y, $txt) 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); } -function _parsejpg($file) +protected function _parsejpg($file) { // Extract info from a JPEG file $a = getimagesize($file); @@ -1434,7 +1434,7 @@ function _parsejpg($file) return array('w'=>$a[0], 'h'=>$a[1], 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'DCTDecode', 'data'=>$data); } -function _parsepng($file) +protected function _parsepng($file) { // Extract info from a PNG file $f = fopen($file,'rb'); @@ -1445,7 +1445,7 @@ function _parsepng($file) return $info; } -function _parsepngstream($f, $file) +protected function _parsepngstream($f, $file) { // Check signature if($this->_readstream($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) @@ -1563,6 +1563,7 @@ function _parsepngstream($f, $file) unset($data); $data = gzcompress($color); $info['smask'] = gzcompress($alpha); + $this->WithAlpha = true; if($this->PDFVersion<'1.4') $this->PDFVersion = '1.4'; } @@ -1570,7 +1571,7 @@ function _parsepngstream($f, $file) return $info; } -function _readstream($f, $n) +protected function _readstream($f, $n) { // Read n bytes from stream $res = ''; @@ -1587,14 +1588,14 @@ function _readstream($f, $n) return $res; } -function _readint($f) +protected function _readint($f) { // Read a 4-byte integer from stream $a = unpack('Ni',$this->_readstream($f,4)); return $a['i']; } -function _parsegif($file) +protected function _parsegif($file) { // Extract info from a GIF file (via PNG conversion) if(!function_exists('imagepng')) @@ -1605,245 +1606,290 @@ function _parsegif($file) if(!$im) $this->Error('Missing or incorrect image file: '.$file); imageinterlace($im,0); - $f = @fopen('php://temp','rb+'); - if($f) - { - // Perform conversion in memory - ob_start(); - imagepng($im); - $data = ob_get_clean(); - fwrite($f,$data); - rewind($f); - $info = $this->_parsepngstream($f,$file); - fclose($f); - } - else - { - // Use temporary file - $tmp = tempnam('.','gif'); - if(!$tmp) - $this->Error('Unable to create a temporary file'); - if(!imagepng($im,$tmp)) - $this->Error('Error while saving to temporary file'); - $info = $this->_parsepng($tmp); - unlink($tmp); - } + ob_start(); + imagepng($im); + $data = ob_get_clean(); + imagedestroy($im); + $f = fopen('php://temp','rb+'); + if(!$f) + $this->Error('Unable to create memory stream'); + fwrite($f,$data); + rewind($f); + $info = $this->_parsepngstream($f,$file); + fclose($f); return $info; } -function _newobj() -{ - // Begin a new object - $this->n++; - $this->offsets[$this->n] = strlen($this->buffer); - $this->_out($this->n.' 0 obj'); -} - -function _putstream($s) -{ - $this->_out('stream'); - $this->_out($s); - $this->_out('endstream'); -} - -function _out($s) +protected function _out($s) { // Add a line to the document if($this->state==2) $this->pages[$this->page] .= $s."\n"; - else - $this->buffer .= $s."\n"; + elseif($this->state==1) + $this->_put($s); + elseif($this->state==0) + $this->Error('No page has been added yet'); + elseif($this->state==3) + $this->Error('The document is closed'); } -function _putpages() +protected function _put($s) +{ + $this->buffer .= $s."\n"; +} + +protected function _getoffset() +{ + return strlen($this->buffer); +} + +protected function _newobj($n=null) +{ + // Begin a new object + if($n===null) + $n = ++$this->n; + $this->offsets[$n] = $this->_getoffset(); + $this->_put($n.' 0 obj'); +} + +protected function _putstream($data) +{ + $this->_put('stream'); + $this->_put($data); + $this->_put('endstream'); +} + +protected function _putstreamobject($data) +{ + if($this->compress) + { + $entries = '/Filter /FlateDecode '; + $data = gzcompress($data); + } + else + $entries = ''; + $entries .= '/Length '.strlen($data); + $this->_newobj(); + $this->_put('<<'.$entries.'>>'); + $this->_putstream($data); + $this->_put('endobj'); +} + +protected function _putlinks($n) +{ + foreach($this->PageLinks[$n] as $pl) + { + $this->_newobj(); + $rect = sprintf('%.2F %.2F %.2F %.2F',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]); + $s = '<_textstring($pl[4]).'>>>>'; + else + { + $l = $this->links[$pl[4]]; + if(isset($this->PageInfo[$l[0]]['size'])) + $h = $this->PageInfo[$l[0]]['size'][1]; + else + $h = ($this->DefOrientation=='P') ? $this->DefPageSize[1]*$this->k : $this->DefPageSize[0]*$this->k; + $s .= sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>',$this->PageInfo[$l[0]]['n'],$h-$l[1]*$this->k); + } + $this->_put($s); + $this->_put('endobj'); + } +} + +protected function _putpage($n) +{ + $this->_newobj(); + $this->_put('<_put('/Parent 1 0 R'); + if(isset($this->PageInfo[$n]['size'])) + $this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->PageInfo[$n]['size'][0],$this->PageInfo[$n]['size'][1])); + if(isset($this->PageInfo[$n]['rotation'])) + $this->_put('/Rotate '.$this->PageInfo[$n]['rotation']); + $this->_put('/Resources 2 0 R'); + if(!empty($this->PageLinks[$n])) + { + $s = '/Annots ['; + foreach($this->PageLinks[$n] as $pl) + $s .= $pl[5].' 0 R '; + $s .= ']'; + $this->_put($s); + } + if($this->WithAlpha) + $this->_put('/Group <>'); + $this->_put('/Contents '.($this->n+1).' 0 R>>'); + $this->_put('endobj'); + // Page content + if(!empty($this->AliasNbPages)) { + $alias = $this->UTF8ToUTF16BE($this->AliasNbPages, false); + $r = $this->UTF8ToUTF16BE($this->page, false); + $this->pages[$n] = str_replace($alias,$r,$this->pages[$n]); + // Now repeat for no pages in non-subset fonts + $this->pages[$n] = str_replace($this->AliasNbPages,$this->page,$this->pages[$n]); + } + $this->_putstreamobject($this->pages[$n]); + // Link annotations + $this->_putlinks($n); +} + +protected function _putpages() { $nb = $this->page; - if(!empty($this->AliasNbPages)) + $n = $this->n; + for($i=1;$i<=$nb;$i++) { - // Replace number of pages in fonts using subsets - $alias = $this->UTF8ToUTF16BE($this->AliasNbPages, false); - $r = $this->UTF8ToUTF16BE("$nb", false); - for($n=1;$n<=$nb;$n++) - $this->pages[$n] = str_replace($alias,$r,$this->pages[$n]); - // Now repeat for no pages in non-subset fonts - for($n=1;$n<=$nb;$n++) - $this->pages[$n] = str_replace($this->AliasNbPages,$nb,$this->pages[$n]); + $this->PageInfo[$i]['n'] = ++$n; + $n++; + foreach($this->PageLinks[$i] as &$pl) + $pl[5] = ++$n; + unset($pl); } + for($i=1;$i<=$nb;$i++) + $this->_putpage($i); + // Pages root + $this->_newobj(1); + $this->_put('<PageInfo[$i]['n'].' 0 R '; + $kids .= ']'; + $this->_put($kids); + $this->_put('/Count '.$nb); if($this->DefOrientation=='P') { - $wPt = $this->DefPageSize[0]*$this->k; - $hPt = $this->DefPageSize[1]*$this->k; + $w = $this->DefPageSize[0]; + $h = $this->DefPageSize[1]; } else { - $wPt = $this->DefPageSize[1]*$this->k; - $hPt = $this->DefPageSize[0]*$this->k; + $w = $this->DefPageSize[1]; + $h = $this->DefPageSize[0]; } - $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; - for($n=1;$n<=$nb;$n++) - { - // Page - $this->_newobj(); - $this->_out('<_out('/Parent 1 0 R'); - if(isset($this->PageSizes[$n])) - $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->PageSizes[$n][0],$this->PageSizes[$n][1])); - $this->_out('/Resources 2 0 R'); - if(isset($this->PageLinks[$n])) - { - // Links - $annots = '/Annots ['; - foreach($this->PageLinks[$n] as $pl) - { - $rect = sprintf('%.2F %.2F %.2F %.2F',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]); - $annots .= '<_textstring($pl[4]).'>>>>'; - else - { - $l = $this->links[$pl[4]]; - $h = isset($this->PageSizes[$l[0]]) ? $this->PageSizes[$l[0]][1] : $hPt; - $annots .= sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>',1+2*$l[0],$h-$l[1]*$this->k); - } - } - $this->_out($annots.']'); - } - if($this->PDFVersion>'1.3') - $this->_out('/Group <>'); - $this->_out('/Contents '.($this->n+1).' 0 R>>'); - $this->_out('endobj'); - // Page content - $p = ($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n]; - $this->_newobj(); - $this->_out('<<'.$filter.'/Length '.strlen($p).'>>'); - $this->_putstream($p); - $this->_out('endobj'); - } - // Pages root - $this->offsets[1] = strlen($this->buffer); - $this->_out('1 0 obj'); - $this->_out('<_out($kids.']'); - $this->_out('/Count '.$nb); - $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$wPt,$hPt)); - $this->_out('>>'); - $this->_out('endobj'); + $this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$w*$this->k,$h*$this->k)); + $this->_put('>>'); + $this->_put('endobj'); } -function _putfonts() +protected function _putfonts() { - $nf=$this->n; - foreach($this->diffs as $diff) - { - // Encodings - $this->_newobj(); - $this->_out('<>'); - $this->_out('endobj'); - } foreach($this->FontFiles as $file=>$info) { - if (!isset($info['type']) || $info['type']!='TTF') { - // Font file embedding - $this->_newobj(); - $this->FontFiles[$file]['n']=$this->n; - $font=''; - $f=fopen($this->_getfontpath().$file,'rb',1); - if(!$f) - $this->Error('Font file not found'); - while(!feof($f)) - $font.=fread($f,8192); - fclose($f); - $compressed=(substr($file,-2)=='.z'); - if(!$compressed && isset($info['length2'])) - { - $header=(ord($font[0])==128); - if($header) - { - // Strip first binary header - $font=substr($font,6); - } - if($header && ord($font[$info['length1']])==128) - { - // Strip second binary header - $font=substr($font,0,$info['length1']).substr($font,$info['length1']+6); - } + if (!isset($info['type']) || $info['type']!='TTF') { + // Font file embedding + $this->_newobj(); + $this->FontFiles[$file]['n'] = $this->n; + $font = file_get_contents($this->fontpath.$file,true); + if(!$font) + $this->Error('Font file not found: '.$file); + $compressed = (substr($file,-2)=='.z'); + if(!$compressed && isset($info['length2'])) + $font = substr($font,6,$info['length1']).substr($font,6+$info['length1']+6,$info['length2']); + $this->_put('<_put('/Filter /FlateDecode'); + $this->_put('/Length1 '.$info['length1']); + if(isset($info['length2'])) + $this->_put('/Length2 '.$info['length2'].' /Length3 0'); + $this->_put('>>'); + $this->_putstream($font); + $this->_put('endobj'); } - $this->_out('<_out('/Filter /FlateDecode'); - $this->_out('/Length1 '.$info['length1']); - if(isset($info['length2'])) - $this->_out('/Length2 '.$info['length2'].' /Length3 0'); - $this->_out('>>'); - $this->_putstream($font); - $this->_out('endobj'); - } } foreach($this->fonts as $k=>$font) { - // Font objects - //$this->fonts[$k]['n']=$this->n+1; + // Encoding + if(isset($font['diff'])) + { + if(!isset($this->encodings[$font['enc']])) + { + $this->_newobj(); + $this->_put('<>'); + $this->_put('endobj'); + $this->encodings[$font['enc']] = $this->n; + } + } + // ToUnicode CMap + if(isset($font['uv'])) + { + if(isset($font['enc'])) + $cmapkey = $font['enc']; + else + $cmapkey = $font['name']; + if(!isset($this->cmaps[$cmapkey])) + { + $cmap = $this->_tounicodecmap($font['uv']); + $this->_putstreamobject($cmap); + $this->cmaps[$cmapkey] = $this->n; + } + } + // Font object $type = $font['type']; $name = $font['name']; if($type=='Core') { - // Standard font - $this->fonts[$k]['n']=$this->n+1; + // Core font + $this->fonts[$k]['n'] = $this->n+1; $this->_newobj(); - $this->_out('<_out('/BaseFont /'.$name); - $this->_out('/Subtype /Type1'); + $this->_put('<_put('/BaseFont /'.$name); + $this->_put('/Subtype /Type1'); if($name!='Symbol' && $name!='ZapfDingbats') - $this->_out('/Encoding /WinAnsiEncoding'); - $this->_out('>>'); - $this->_out('endobj'); + $this->_put('/Encoding /WinAnsiEncoding'); + if(isset($font['uv'])) + $this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R'); + $this->_put('>>'); + $this->_put('endobj'); } elseif($type=='Type1' || $type=='TrueType') { - // Additional Type1 or TrueType font - $this->fonts[$k]['n']=$this->n+1; + // Additional Type1 or TrueType/OpenType font + if(isset($font['subsetted']) && $font['subsetted']) + $name = 'AAAAAA+'.$name; + $this->fonts[$k]['n'] = $this->n+1; $this->_newobj(); - $this->_out('<_out('/BaseFont /'.$name); - $this->_out('/Subtype /'.$type); - $this->_out('/FirstChar 32 /LastChar 255'); - $this->_out('/Widths '.($this->n+1).' 0 R'); - $this->_out('/FontDescriptor '.($this->n+2).' 0 R'); + $this->_put('<_put('/BaseFont /'.$name); + $this->_put('/Subtype /'.$type); + $this->_put('/FirstChar 32 /LastChar 255'); + $this->_put('/Widths '.($this->n+1).' 0 R'); + $this->_put('/FontDescriptor '.($this->n+2).' 0 R'); + if($font['enc']) { if(isset($font['diff'])) - $this->_out('/Encoding '.($nf+$font['diff']).' 0 R'); + $this->_put('/Encoding '.$this->encodings[$font['enc']].' 0 R'); else - $this->_out('/Encoding /WinAnsiEncoding'); + $this->_put('/Encoding /WinAnsiEncoding'); } - $this->_out('>>'); - $this->_out('endobj'); + + if(isset($font['uv'])) + $this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R'); + $this->_put('>>'); + $this->_put('endobj'); // Widths $this->_newobj(); - $cw=&$font['cw']; - $s='['; + $cw = $font['cw']; + $s = '['; for($i=32;$i<=255;$i++) - $s.=$cw[chr($i)].' '; - $this->_out($s.']'); - $this->_out('endobj'); + $s .= $cw[chr($i)].' '; + $this->_put($s.']'); + $this->_put('endobj'); // Descriptor $this->_newobj(); - $s='<$v) - $s.=' /'.$k.' '.$v; - $file=$font['file']; - if($file) - $s.=' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R'; - $this->_out($s.'>>'); - $this->_out('endobj'); + $s .= ' /'.$k.' '.$v; + + if(!empty($font['file'])) + $s .= ' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R'; + $this->_put($s.'>>'); + $this->_put('endobj'); } // TrueType embedded SUBSETS or FULL else if ($type=='TTF') { $this->fonts[$k]['n']=$this->n+1; - require_once($this->_getfontpath().'unifont/ttfonts.php'); + require_once($this->fontpath.'unifont/ttfonts.php'); $ttf = new TTFontFile(); $fontname = 'MPDFAA'.'+'.$font['name']; $subset = $font['subset']; @@ -1857,32 +1903,32 @@ function _putfonts() // Type0 Font // A composite font - a font composed of other fonts, organized hierarchically $this->_newobj(); - $this->_out('<_out('/Subtype /Type0'); - $this->_out('/BaseFont /'.$fontname.''); - $this->_out('/Encoding /Identity-H'); - $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]'); - $this->_out('/ToUnicode '.($this->n + 2).' 0 R'); - $this->_out('>>'); - $this->_out('endobj'); + $this->_put('<_put('/Subtype /Type0'); + $this->_put('/BaseFont /'.$fontname.''); + $this->_put('/Encoding /Identity-H'); + $this->_put('/DescendantFonts ['.($this->n + 1).' 0 R]'); + $this->_put('/ToUnicode '.($this->n + 2).' 0 R'); + $this->_put('>>'); + $this->_put('endobj'); // CIDFontType2 // A CIDFont whose glyph descriptions are based on TrueType font technology $this->_newobj(); - $this->_out('<_out('/Subtype /CIDFontType2'); - $this->_out('/BaseFont /'.$fontname.''); - $this->_out('/CIDSystemInfo '.($this->n + 2).' 0 R'); - $this->_out('/FontDescriptor '.($this->n + 3).' 0 R'); + $this->_put('<_put('/Subtype /CIDFontType2'); + $this->_put('/BaseFont /'.$fontname.''); + $this->_put('/CIDSystemInfo '.($this->n + 2).' 0 R'); + $this->_put('/FontDescriptor '.($this->n + 3).' 0 R'); if (isset($font['desc']['MissingWidth'])){ $this->_out('/DW '.$font['desc']['MissingWidth'].''); } $this->_putTTfontwidths($font, $ttf->maxUni); - $this->_out('/CIDToGIDMap '.($this->n + 4).' 0 R'); - $this->_out('>>'); - $this->_out('endobj'); + $this->_put('/CIDToGIDMap '.($this->n + 4).' 0 R'); + $this->_put('>>'); + $this->_put('endobj'); // ToUnicode $this->_newobj(); @@ -1906,29 +1952,29 @@ function _putfonts() $toUni .= "CMapName currentdict /CMap defineresource pop\n"; $toUni .= "end\n"; $toUni .= "end"; - $this->_out('<>'); + $this->_put('<>'); $this->_putstream($toUni); - $this->_out('endobj'); + $this->_put('endobj'); // CIDSystemInfo dictionary $this->_newobj(); - $this->_out('<_out('/Ordering (UCS)'); - $this->_out('/Supplement 0'); - $this->_out('>>'); - $this->_out('endobj'); + $this->_put('<_put('/Ordering (UCS)'); + $this->_put('/Supplement 0'); + $this->_put('>>'); + $this->_put('endobj'); // Font descriptor $this->_newobj(); - $this->_out('<_out('/FontName /'.$fontname); + $this->_put('<_put('/FontName /'.$fontname); foreach($font['desc'] as $kd=>$v) { if ($kd == 'Flags') { $v = $v | 4; $v = $v & ~32; } // SYMBOLIC font flag $this->_out(' /'.$kd.' '.$v); } - $this->_out('/FontFile2 '.($this->n + 2).' 0 R'); - $this->_out('>>'); - $this->_out('endobj'); + $this->_put('/FontFile2 '.($this->n + 2).' 0 R'); + $this->_put('>>'); + $this->_put('endobj'); // Embed CIDToGIDMap // A specification of the mapping from CIDs to glyph indices @@ -1940,27 +1986,27 @@ function _putfonts() } $cidtogidmap = gzcompress($cidtogidmap); $this->_newobj(); - $this->_out('<_out('/Filter /FlateDecode'); - $this->_out('>>'); + $this->_put('<_put('/Filter /FlateDecode'); + $this->_put('>>'); $this->_putstream($cidtogidmap); - $this->_out('endobj'); + $this->_put('endobj'); //Font file $this->_newobj(); - $this->_out('<_out('/Filter /FlateDecode'); - $this->_out('/Length1 '.$ttfontsize); - $this->_out('>>'); + $this->_put('<_put('/Filter /FlateDecode'); + $this->_put('/Length1 '.$ttfontsize); + $this->_put('>>'); $this->_putstream($fontstream); - $this->_out('endobj'); + $this->_put('endobj'); unset($ttf); } else { // Allow for additional types $this->fonts[$k]['n'] = $this->n+1; - $mtd='_put'.strtolower($type); + $mtd = '_put'.strtolower($type); if(!method_exists($this,$mtd)) $this->Error('Unsupported font type: '.$type); $this->$mtd($font); @@ -1968,7 +2014,7 @@ function _putfonts() } } -function _putTTfontwidths(&$font, $maxUni) { +protected function _putTTfontwidths($font, $maxUni) { if (file_exists($font['unifilename'].'.cw127.php')) { include($font['unifilename'].'.cw127.php') ; $startcid = 128; @@ -1986,7 +2032,7 @@ function _putTTfontwidths(&$font, $maxUni) { // for each character for ($cid=$startcid; $cid<$cwlen; $cid++) { if ($cid==128 && (!file_exists($font['unifilename'].'.cw127.php'))) { - if (is_writable(dirname($this->_getfontpath().'unifont/x'))) { + if (is_writable(dirname($this->fontpath.'unifont/x'))) { $fh = fopen($font['unifilename'].'.cw127.php',"wb"); $cw127=' 255 && (!isset($font['subset'][$cid]) || !$font['subset'][$cid])) { continue; } @@ -2068,7 +2116,58 @@ function _putTTfontwidths(&$font, $maxUni) { $this->_out('/W ['.$w.' ]'); } -function _putimages() +protected function _tounicodecmap($uv) +{ + $ranges = ''; + $nbr = 0; + $chars = ''; + $nbc = 0; + foreach($uv as $c=>$v) + { + if(is_array($v)) + { + $ranges .= sprintf("<%02X> <%02X> <%04X>\n",$c,$c+$v[1]-1,$v[0]); + $nbr++; + } + else + { + $chars .= sprintf("<%02X> <%04X>\n",$c,$v); + $nbc++; + } + } + $s = "/CIDInit /ProcSet findresource begin\n"; + $s .= "12 dict begin\n"; + $s .= "begincmap\n"; + $s .= "/CIDSystemInfo\n"; + $s .= "<0) + { + $s .= "$nbr beginbfrange\n"; + $s .= $ranges; + $s .= "endbfrange\n"; + } + if($nbc>0) + { + $s .= "$nbc beginbfchar\n"; + $s .= $chars; + $s .= "endbfchar\n"; + } + $s .= "endcmap\n"; + $s .= "CMapName currentdict /CMap defineresource pop\n"; + $s .= "end\n"; + $s .= "end"; + return $s; +} + +protected function _putimages() { foreach(array_keys($this->images) as $file) { @@ -2078,39 +2177,39 @@ function _putimages() } } -function _putimage(&$info) +protected function _putimage(&$info) { $this->_newobj(); $info['n'] = $this->n; - $this->_out('<_out('/Subtype /Image'); - $this->_out('/Width '.$info['w']); - $this->_out('/Height '.$info['h']); + $this->_put('<_put('/Subtype /Image'); + $this->_put('/Width '.$info['w']); + $this->_put('/Height '.$info['h']); if($info['cs']=='Indexed') - $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]'); + $this->_put('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]'); else { - $this->_out('/ColorSpace /'.$info['cs']); + $this->_put('/ColorSpace /'.$info['cs']); if($info['cs']=='DeviceCMYK') - $this->_out('/Decode [1 0 1 0 1 0 1 0]'); + $this->_put('/Decode [1 0 1 0 1 0 1 0]'); } - $this->_out('/BitsPerComponent '.$info['bpc']); + $this->_put('/BitsPerComponent '.$info['bpc']); if(isset($info['f'])) - $this->_out('/Filter /'.$info['f']); + $this->_put('/Filter /'.$info['f']); if(isset($info['dp'])) - $this->_out('/DecodeParms <<'.$info['dp'].'>>'); + $this->_put('/DecodeParms <<'.$info['dp'].'>>'); if(isset($info['trns']) && is_array($info['trns'])) { $trns = ''; for($i=0;$i_out('/Mask ['.$trns.']'); + $this->_put('/Mask ['.$trns.']'); } if(isset($info['smask'])) - $this->_out('/SMask '.($this->n+1).' 0 R'); - $this->_out('/Length '.strlen($info['data']).'>>'); + $this->_put('/SMask '.($this->n+1).' 0 R'); + $this->_put('/Length '.strlen($info['data']).'>>'); $this->_putstream($info['data']); - $this->_out('endobj'); + $this->_put('endobj'); // Soft mask if(isset($info['smask'])) { @@ -2120,146 +2219,131 @@ function _putimage(&$info) } // Palette if($info['cs']=='Indexed') - { - $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; - $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal']; - $this->_newobj(); - $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>'); - $this->_putstream($pal); - $this->_out('endobj'); - } + $this->_putstreamobject($info['pal']); } -function _putxobjectdict() +protected function _putxobjectdict() { foreach($this->images as $image) - $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R'); + $this->_put('/I'.$image['i'].' '.$image['n'].' 0 R'); } -function _putresourcedict() +protected function _putresourcedict() { - $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); - $this->_out('/Font <<'); - foreach($this->fonts as $font) { - $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R'); - } - $this->_out('>>'); - $this->_out('/XObject <<'); + $this->_put('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); + $this->_put('/Font <<'); + foreach($this->fonts as $font) + $this->_put('/F'.$font['i'].' '.$font['n'].' 0 R'); + $this->_put('>>'); + $this->_put('/XObject <<'); $this->_putxobjectdict(); - $this->_out('>>'); + $this->_put('>>'); } -function _putresources() +protected function _putresources() { $this->_putfonts(); $this->_putimages(); // Resource dictionary - $this->offsets[2] = strlen($this->buffer); - $this->_out('2 0 obj'); - $this->_out('<<'); + $this->_newobj(2); + $this->_put('<<'); $this->_putresourcedict(); - $this->_out('>>'); - $this->_out('endobj'); + $this->_put('>>'); + $this->_put('endobj'); } -function _putinfo() +protected function _putinfo() { - $this->_out('/Producer '.$this->_textstring('tFPDF '.tFPDF_VERSION)); - if(!empty($this->title)) - $this->_out('/Title '.$this->_textstring($this->title)); - if(!empty($this->subject)) - $this->_out('/Subject '.$this->_textstring($this->subject)); - if(!empty($this->author)) - $this->_out('/Author '.$this->_textstring($this->author)); - if(!empty($this->keywords)) - $this->_out('/Keywords '.$this->_textstring($this->keywords)); - if(!empty($this->creator)) - $this->_out('/Creator '.$this->_textstring($this->creator)); - $this->_out('/CreationDate '.$this->_textstring('D:'.@date('YmdHis'))); + $date = @date('YmdHisO',$this->CreationDate); + $this->metadata['CreationDate'] = 'D:'.substr($date,0,-2)."'".substr($date,-2)."'"; + foreach($this->metadata as $key=>$value) + $this->_put('/'.$key.' '.$this->_textstring($value)); } -function _putcatalog() +protected function _putcatalog() { - $this->_out('/Type /Catalog'); - $this->_out('/Pages 1 0 R'); + $n = $this->PageInfo[1]['n']; + $this->_put('/Type /Catalog'); + $this->_put('/Pages 1 0 R'); if($this->ZoomMode=='fullpage') - $this->_out('/OpenAction [3 0 R /Fit]'); + $this->_put('/OpenAction ['.$n.' 0 R /Fit]'); elseif($this->ZoomMode=='fullwidth') - $this->_out('/OpenAction [3 0 R /FitH null]'); + $this->_put('/OpenAction ['.$n.' 0 R /FitH null]'); elseif($this->ZoomMode=='real') - $this->_out('/OpenAction [3 0 R /XYZ null null 1]'); + $this->_put('/OpenAction ['.$n.' 0 R /XYZ null null 1]'); elseif(!is_string($this->ZoomMode)) - $this->_out('/OpenAction [3 0 R /XYZ null null '.sprintf('%.2F',$this->ZoomMode/100).']'); + $this->_put('/OpenAction ['.$n.' 0 R /XYZ null null '.sprintf('%.2F',$this->ZoomMode/100).']'); if($this->LayoutMode=='single') - $this->_out('/PageLayout /SinglePage'); + $this->_put('/PageLayout /SinglePage'); elseif($this->LayoutMode=='continuous') - $this->_out('/PageLayout /OneColumn'); + $this->_put('/PageLayout /OneColumn'); elseif($this->LayoutMode=='two') - $this->_out('/PageLayout /TwoColumnLeft'); + $this->_put('/PageLayout /TwoColumnLeft'); } -function _putheader() +protected function _putheader() { - $this->_out('%PDF-'.$this->PDFVersion); + $this->_put('%PDF-'.$this->PDFVersion); } -function _puttrailer() +protected function _puttrailer() { - $this->_out('/Size '.($this->n+1)); - $this->_out('/Root '.$this->n.' 0 R'); - $this->_out('/Info '.($this->n-1).' 0 R'); + $this->_put('/Size '.($this->n+1)); + $this->_put('/Root '.$this->n.' 0 R'); + $this->_put('/Info '.($this->n-1).' 0 R'); } -function _enddoc() +protected function _enddoc() { + $this->CreationDate = time(); $this->_putheader(); $this->_putpages(); $this->_putresources(); // Info $this->_newobj(); - $this->_out('<<'); + $this->_put('<<'); $this->_putinfo(); - $this->_out('>>'); - $this->_out('endobj'); + $this->_put('>>'); + $this->_put('endobj'); // Catalog $this->_newobj(); - $this->_out('<<'); + $this->_put('<<'); $this->_putcatalog(); - $this->_out('>>'); - $this->_out('endobj'); + $this->_put('>>'); + $this->_put('endobj'); // Cross-ref - $o = strlen($this->buffer); - $this->_out('xref'); - $this->_out('0 '.($this->n+1)); - $this->_out('0000000000 65535 f '); + $offset = $this->_getoffset(); + $this->_put('xref'); + $this->_put('0 '.($this->n+1)); + $this->_put('0000000000 65535 f '); for($i=1;$i<=$this->n;$i++) - $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i])); + $this->_put(sprintf('%010d 00000 n ',$this->offsets[$i])); // Trailer - $this->_out('trailer'); - $this->_out('<<'); + $this->_put('trailer'); + $this->_put('<<'); $this->_puttrailer(); - $this->_out('>>'); - $this->_out('startxref'); - $this->_out($o); - $this->_out('%%EOF'); + $this->_put('>>'); + $this->_put('startxref'); + $this->_put($offset); + $this->_put('%%EOF'); $this->state = 3; } // ********* NEW FUNCTIONS ********* // Converts UTF-8 strings to UTF16-BE. -function UTF8ToUTF16BE($str, $setbom=true) { +protected function UTF8ToUTF16BE($str, $setbom=true) { $outstr = ""; if ($setbom) { $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM) } - $outstr .= mb_convert_encoding($str??"", 'UTF-16BE', 'UTF-8'); + $outstr .= mb_convert_encoding($str, 'UTF-16BE', 'UTF-8'); return $outstr; } // Converts UTF-8 strings to codepoints array -function UTF8StringToArray($str) { +protected function UTF8StringToArray($str) { $out = array(); - $len = strlen($str??""); + $len = strlen($str); for ($i = 0; $i < $len; $i++) { $uni = -1; $h = ord($str[$i]); @@ -2283,15 +2367,5 @@ function UTF8StringToArray($str) { return $out; } - -// End of class } - -// Handle special IE contype request -if(isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT']=='contype') -{ - header('Content-Type: application/pdf'); - exit; -} - ?>