diff --git a/src/bb-library/tfpdf.php b/src/bb-library/tfpdf.php index 3460a9a2f..c7096b251 100644 --- a/src/bb-library/tfpdf.php +++ b/src/bb-library/tfpdf.php @@ -1,2299 +1,2378 @@ * +* Tycho Veltmeijer (versions 1.30+) * +* License: LGPL * +*******************************************************************************/ -/** -* tFPDF (based on FPDF 1.7) -* -* Version: 1.24 -* Date: 2011-09-24 -* Author: Ian Back -* License: LGPL -*/ - -define('tFPDF_VERSION','1.24'); +define('tFPDF_VERSION','1.32'); 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 - - /******************************************************************************* - * * - * Public methods * - * * - *******************************************************************************/ - function __construct($orientation='P', $unit='mm', $size='A4') - { - // Some checks - $this->_dochecks(); - // Initialization of properties - $this->page = 0; - $this->n = 2; - $this->buffer = ''; - $this->pages = array(); - $this->PageSizes = array(); - $this->state = 0; - $this->fonts = array(); - $this->FontFiles = array(); - $this->diffs = array(); - $this->images = array(); - $this->links = array(); - $this->InHeader = false; - $this->InFooter = false; - $this->lasth = 0; - $this->FontFamily = ''; - $this->FontStyle = ''; - $this->FontSizePt = 12; - $this->underline = false; - $this->DrawColor = '0 G'; - $this->FillColor = '0 g'; - $this->TextColor = '0 g'; - $this->ColorFlag = false; - $this->ws = 0; - // Font path - if(defined('FPDF_FONTPATH')) - { - $this->fontpath = FPDF_FONTPATH; - if(substr($this->fontpath,-1)!='/' && substr($this->fontpath,-1)!='\\') - $this->fontpath .= '/'; - } - elseif(is_dir(dirname(__FILE__).'/font')) - $this->fontpath = dirname(__FILE__).'/font/'; - else - $this->fontpath = ''; - // Core fonts - $this->CoreFonts = array('courier', 'helvetica', 'times', 'symbol', 'zapfdingbats'); - // Scale factor - if($unit=='pt') - $this->k = 1; - elseif($unit=='mm') - $this->k = 72/25.4; - elseif($unit=='cm') - $this->k = 72/2.54; - elseif($unit=='in') - $this->k = 72; - else - $this->Error('Incorrect unit: '.$unit); - // Page sizes - $this->StdPageSizes = array('a3'=>array(841.89,1190.55), 'a4'=>array(595.28,841.89), 'a5'=>array(420.94,595.28), - 'letter'=>array(612,792), 'legal'=>array(612,1008)); - $size = $this->_getpagesize($size); - $this->DefPageSize = $size; - $this->CurPageSize = $size; - // Page orientation - $orientation = strtolower($orientation); - if($orientation=='p' || $orientation=='portrait') - { - $this->DefOrientation = 'P'; - $this->w = $size[0]; - $this->h = $size[1]; - } - elseif($orientation=='l' || $orientation=='landscape') - { - $this->DefOrientation = 'L'; - $this->w = $size[1]; - $this->h = $size[0]; - } - else - $this->Error('Incorrect orientation: '.$orientation); - $this->CurOrientation = $this->DefOrientation; - $this->wPt = $this->w*$this->k; - $this->hPt = $this->h*$this->k; - // Page margins (1 cm) - $margin = 28.35/$this->k; - $this->SetMargins($margin,$margin); - // Interior cell margin (1 mm) - $this->cMargin = $margin/10; - // Line width (0.2 mm) - $this->LineWidth = .567/$this->k; - // Automatic page break - $this->SetAutoPageBreak(true,2*$margin); - // Default display mode - $this->SetDisplayMode('default'); - // Enable compression - $this->SetCompression(true); - // Set default PDF version number - $this->PDFVersion = '1.3'; - } - - function SetMargins($left, $top, $right=null) - { - // Set left, top and right margins - $this->lMargin = $left; - $this->tMargin = $top; - if($right===null) - $right = $left; - $this->rMargin = $right; - } - - function SetLeftMargin($margin) - { - // Set left margin - $this->lMargin = $margin; - if($this->page>0 && $this->x<$margin) - $this->x = $margin; - } - - function SetTopMargin($margin) - { - // Set top margin - $this->tMargin = $margin; - } - - function SetRightMargin($margin) - { - // Set right margin - $this->rMargin = $margin; - } - - function SetAutoPageBreak($auto, $margin=0) - { - // Set auto page break mode and triggering margin - $this->AutoPageBreak = $auto; - $this->bMargin = $margin; - $this->PageBreakTrigger = $this->h-$margin; - } - - function SetDisplayMode($zoom, $layout='default') - { - // Set display mode in viewer - if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom)) - $this->ZoomMode = $zoom; - else - $this->Error('Incorrect zoom display mode: '.$zoom); - if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default') - $this->LayoutMode = $layout; - else - $this->Error('Incorrect layout display mode: '.$layout); - } - - function SetCompression($compress) - { - // Set page compression - if(function_exists('gzcompress')) - $this->compress = $compress; - else - $this->compress = false; - } - - 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; - } - - function SetAuthor($author, $isUTF8=false) - { - // Author of document - if($isUTF8) - $author = $this->_UTF8toUTF16($author); - $this->author = $author; - } - - function SetKeywords($keywords, $isUTF8=false) - { - // Keywords of document - if($isUTF8) - $keywords = $this->_UTF8toUTF16($keywords); - $this->keywords = $keywords; - } - - function SetCreator($creator, $isUTF8=false) - { - // Creator of document - if($isUTF8) - $creator = $this->_UTF8toUTF16($creator); - $this->creator = $creator; - } - - function AliasNbPages($alias='{nb}') - { - // Define an alias for total number of pages - $this->AliasNbPages = $alias; - } - - function Error($msg) - { - // Fatal error - die('FPDF error: '.$msg); - } - - function Open() - { - // Begin document - $this->state = 1; - } - - function Close() - { - // Terminate document - if($this->state==3) - return; - if($this->page==0) - $this->AddPage(); - // Page footer - $this->InFooter = true; - $this->Footer(); - $this->InFooter = false; - // Close page - $this->_endpage(); - // Close document - $this->_enddoc(); - } - - function AddPage($orientation='', $size='') - { - // Start a new page - if($this->state==0) - $this->Open(); - $family = $this->FontFamily; - $style = $this->FontStyle.($this->underline ? 'U' : ''); - $fontsize = $this->FontSizePt; - $lw = $this->LineWidth; - $dc = $this->DrawColor; - $fc = $this->FillColor; - $tc = $this->TextColor; - $cf = $this->ColorFlag; - if($this->page>0) - { - // Page footer - $this->InFooter = true; - $this->Footer(); - $this->InFooter = false; - // Close page - $this->_endpage(); - } - // Start new page - $this->_beginpage($orientation,$size); - // Set line cap style to square - $this->_out('2 J'); - // Set line width - $this->LineWidth = $lw; - $this->_out(sprintf('%.2F w',$lw*$this->k)); - // Set font - if($family) - $this->SetFont($family,$style,$fontsize); - // Set colors - $this->DrawColor = $dc; - if($dc!='0 G') - $this->_out($dc); - $this->FillColor = $fc; - if($fc!='0 g') - $this->_out($fc); - $this->TextColor = $tc; - $this->ColorFlag = $cf; - // Page header - $this->InHeader = true; - $this->Header(); - $this->InHeader = false; - // Restore line width - if($this->LineWidth!=$lw) - { - $this->LineWidth = $lw; - $this->_out(sprintf('%.2F w',$lw*$this->k)); - } - // Restore font - if($family) - $this->SetFont($family,$style,$fontsize); - // Restore colors - if($this->DrawColor!=$dc) - { - $this->DrawColor = $dc; - $this->_out($dc); - } - if($this->FillColor!=$fc) - { - $this->FillColor = $fc; - $this->_out($fc); - } - $this->TextColor = $tc; - $this->ColorFlag = $cf; - } - - function Header() - { - // To be implemented in your own inherited class - } - - function Footer() - { - // To be implemented in your own inherited class - } - - function PageNo() - { - // Get current page number - return $this->page; - } - - function SetDrawColor($r, $g=null, $b=null) - { - // Set color for all stroking operations - if(($r==0 && $g==0 && $b==0) || $g===null) - $this->DrawColor = sprintf('%.3F G',$r/255); - else - $this->DrawColor = sprintf('%.3F %.3F %.3F RG',$r/255,$g/255,$b/255); - if($this->page>0) - $this->_out($this->DrawColor); - } - - function SetFillColor($r, $g=null, $b=null) - { - // Set color for all filling operations - if(($r==0 && $g==0 && $b==0) || $g===null) - $this->FillColor = sprintf('%.3F g',$r/255); - else - $this->FillColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255); - $this->ColorFlag = ($this->FillColor!=$this->TextColor); - if($this->page>0) - $this->_out($this->FillColor); - } - - function SetTextColor($r, $g=null, $b=null) - { - // Set color for text - if(($r==0 && $g==0 && $b==0) || $g===null) - $this->TextColor = sprintf('%.3F g',$r/255); - else - $this->TextColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255); - $this->ColorFlag = ($this->FillColor!=$this->TextColor); - } - - function GetStringWidth($s) - { - // Get width of a string in the current font - $s = (string)$s; - $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]); } - 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']; } - else { $w += 500; } - } - } - else { - $l = strlen($s); - for($i=0;$i<$l;$i++) - $w += $cw[$s[$i]]; - } - return $w*$this->FontSize/1000; - } - - function SetLineWidth($width) - { - // Set line width - $this->LineWidth = $width; - if($this->page>0) - $this->_out(sprintf('%.2F w',$width*$this->k)); - } - - function Line($x1, $y1, $x2, $y2) - { - // Draw a line - $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)); - } - - function Rect($x, $y, $w, $h, $style='') - { - // Draw a rectangle - if($style=='F') - $op = 'f'; - elseif($style=='FD' || $style=='DF') - $op = 'B'; - else - $op = 'S'; - $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op)); - } - - function AddFont($family, $style='', $file='', $uni=false) - { - // Add a TrueType, OpenType or Type1 font - $family = strtolower($family); - $style = strtoupper($style); - if($style=='IB') - $style='BI'; - if($file=='') { - if ($uni) { - $file = str_replace(' ','',$family).strtolower($style).'.ttf'; - } - else { - $file = str_replace(' ','',$family).strtolower($style).'.php'; - } - } - $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 ,'.')))); - $name = ''; - $originalsize = 0; - $ttfstat = stat($ttffilename); - if (file_exists($unifilename.'.mtx.php')) { - include($unifilename.'.mtx.php'); - } - if (!isset($type) || !isset($name) || $originalsize != $ttfstat['size']) { - $ttffile = $ttffilename; - require_once($this->_getfontpath().'unifont/ttfonts.php'); - $ttf = new TTFontFile(); - $ttf->getMetrics($ttffile); - $cw = $ttf->charWidths; - $name = preg_replace('/[ ()]/','',$ttf->fullName); - - $desc= array('Ascent'=>round($ttf->ascent), - 'Descent'=>round($ttf->descent), - 'CapHeight'=>round($ttf->capHeight), - 'Flags'=>$ttf->flags, - 'FontBBox'=>'['.round($ttf->bbox[0])." ".round($ttf->bbox[1])." ".round($ttf->bbox[2])." ".round($ttf->bbox[3]).']', - 'ItalicAngle'=>$ttf->italicAngle, - 'StemV'=>round($ttf->stemV), - 'MissingWidth'=>round($ttf->defaultWidth)); - $up = round($ttf->underlinePosition); - $ut = round($ttf->underlineThickness); - $originalsize = $ttfstat['size']+0; - $type = 'TTF'; - // Generate metrics .php file - $s='"; - if (is_writable(dirname($this->_getfontpath().'unifont/'.'x'))) { - $fh = fopen($unifilename.'.mtx.php',"w"); - fwrite($fh,$s,strlen($s)); - fclose($fh); - $fh = fopen($unifilename.'.cw.dat',"wb"); - fwrite($fh,$cw,strlen($cw)); - fclose($fh); - @unlink($unifilename.'.cw127.php'); - } - unset($ttf); - } - else { - $cw = @file_get_contents($unifilename.'.cw.dat'); - } - $i = count($this->fonts)+1; - if(!empty($this->AliasNbPages)) - $sbarr = range(0,57); - else - $sbarr = range(0,32); - $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'ttffile'=>$ttffile, 'fontkey'=>$fontkey, 'subset'=>$sbarr, 'unifilename'=>$unifilename); - - $this->FontFiles[$fontkey]=array('length1'=>$originalsize, 'type'=>"TTF", 'ttffile'=>$ttffile); - $this->FontFiles[$file]=array('type'=>"TTF"); - unset($cw); - } - 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 - if($info['type']=='TrueType') - $this->FontFiles[$info['file']] = array('length1'=>$info['originalsize']); - else - $this->FontFiles[$info['file']] = array('length1'=>$info['size1'], 'length2'=>$info['size2']); - } - $this->fonts[$fontkey] = $info; - } - } - - function SetFont($family, $style='', $size=0) - { - // Select a font; size given in points - if($family=='') - $family = $this->FontFamily; - else - $family = strtolower($family); - $style = strtoupper($style); - if(strpos($style,'U')!==false) - { - $this->underline = true; - $style = str_replace('U','',$style); - } - else - $this->underline = false; - if($style=='IB') - $style = 'BI'; - if($size==0) - $size = $this->FontSizePt; - // 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])) - { - // Test if one of the core fonts - if($family=='arial') - $family = 'helvetica'; - if(in_array($family,$this->CoreFonts)) - { - if($family=='symbol' || $family=='zapfdingbats') - $style = ''; - $fontkey = $family.$style; - if(!isset($this->fonts[$fontkey])) - $this->AddFont($family,$style); - } - else - $this->Error('Undefined font: '.$family.' '.$style); - } - // Select it - $this->FontFamily = $family; - $this->FontStyle = $style; - $this->FontSizePt = $size; - $this->FontSize = $size/$this->k; - $this->CurrentFont = &$this->fonts[$fontkey]; - if ($this->fonts[$fontkey]['type']=='TTF') { $this->unifontSubset = true; } - else { $this->unifontSubset = false; } - if($this->page>0) - $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); - } - - function SetFontSize($size) - { - // Set font size in points - if($this->FontSizePt==$size) - return; - $this->FontSizePt = $size; - $this->FontSize = $size/$this->k; - if($this->page>0) - $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); - } - - function AddLink() - { - // Create a new internal link - $n = count($this->links)+1; - $this->links[$n] = array(0, 0); - return $n; - } - - function SetLink($link, $y=0, $page=-1) - { - // Set destination of internal link - if($y==-1) - $y = $this->y; - if($page==-1) - $page = $this->page; - $this->links[$link] = array($page, $y); - } - - function Link($x, $y, $w, $h, $link) - { - // Put a link on the page - $this->PageLinks[$this->page][] = array($x*$this->k, $this->hPt-$y*$this->k, $w*$this->k, $h*$this->k, $link); - } - - function Text($x, $y, $txt) - { - // Output a string - if ($this->unifontSubset) - { - $txt2 = '('.$this->_escape($this->UTF8ToUTF16BE($txt, false)).')'; - foreach($this->UTF8StringToArray($txt) as $uni) - $this->CurrentFont['subset'][$uni] = $uni; - } - else - $txt2 = '('.$this->_escape($txt).')'; - $s = sprintf('BT %.2F %.2F Td %s Tj ET',$x*$this->k,($this->h-$y)*$this->k,$txt2); - if($this->underline && $txt!='') - $s .= ' '.$this->_dounderline($x,$y,$txt); - if($this->ColorFlag) - $s = 'q '.$this->TextColor.' '.$s.' Q'; - $this->_out($s); - } - - function AcceptPageBreak() - { - // Accept automatic page break or not - return $this->AutoPageBreak; - } - - function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='') - { - // Output a cell - $k = $this->k; - if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak()) - { - // Automatic page break - $x = $this->x; - $ws = $this->ws; - if($ws>0) - { - $this->ws = 0; - $this->_out('0 Tw'); - } - $this->AddPage($this->CurOrientation,$this->CurPageSize); - $this->x = $x; - if($ws>0) - { - $this->ws = $ws; - $this->_out(sprintf('%.3F Tw',$ws*$k)); - } - } - if($w==0) - $w = $this->w-$this->rMargin-$this->x; - $s = ''; - if($fill || $border==1) - { - if($fill) - $op = ($border==1) ? 'B' : 'f'; - else - $op = 'S'; - $s = sprintf('%.2F %.2F %.2F %.2F re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op); - } - if(is_string($border)) - { - $x = $this->x; - $y = $this->y; - if(strpos($border,'L')!==false) - $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k); - if(strpos($border,'T')!==false) - $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k); - if(strpos($border,'R')!==false) - $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k); - if(strpos($border,'B')!==false) - $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k); - } - if($txt!=='') - { - if($align=='R') - $dx = $w-$this->cMargin-$this->GetStringWidth($txt); - elseif($align=='C') - $dx = ($w-$this->GetStringWidth($txt))/2; - else - $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) - $this->CurrentFont['subset'][$uni] = $uni; - $space = $this->_escape($this->UTF8ToUTF16BE(' ', false)); - $s .= sprintf('BT 0 Tw %.2F %.2F Td [',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k); - $t = explode(' ',$txt); - $numt = count($t); - for($i=0;$i<$numt;$i++) { - $tx = $t[$i]; - $tx = '('.$this->_escape($this->UTF8ToUTF16BE($tx, false)).')'; - $s .= sprintf('%s ',$tx); - if (($i+1)<$numt) { - $adj = -($this->ws*$this->k)*1000/$this->FontSizePt; - $s .= sprintf('%d(%s) ',$adj,$space); - } - } - $s .= '] TJ'; - $s .= ' ET'; - } - else { - if ($this->unifontSubset) - { - $txt2 = '('.$this->_escape($this->UTF8ToUTF16BE($txt, false)).')'; - foreach($this->UTF8StringToArray($txt) as $uni) - $this->CurrentFont['subset'][$uni] = $uni; - } - else - $txt2='('.str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$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) - $s .= ' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt); - if($this->ColorFlag) - $s .= ' Q'; - if($link) - $this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link); - } - if($s) - $this->_out($s); - $this->lasth = $h; - if($ln>0) - { - // Go to next line - $this->y += $h; - if($ln==1) - $this->x = $this->lMargin; - } - else - $this->x += $w; - } - - function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false) - { - // Output text with automatic or explicit line breaks - $cw = &$this->CurrentFont['cw']; - if($w==0) - $w = $this->w-$this->rMargin-$this->x; - $wmax = ($w-2*$this->cMargin); - $s = str_replace("\r",'',$txt); - if ($this->unifontSubset) { - $nb=mb_strlen($s, 'utf-8'); - while($nb>0 && mb_substr($s,$nb-1,1,'utf-8')=="\n") $nb--; - } - else { - $nb = strlen($s); - if($nb>0 && $s[$nb-1]=="\n") - $nb--; - } - $b = 0; - if($border) - { - if($border==1) - { - $border = 'LTRB'; - $b = 'LRT'; - $b2 = 'LR'; - } - else - { - $b2 = ''; - if(strpos($border,'L')!==false) - $b2 .= 'L'; - if(strpos($border,'R')!==false) - $b2 .= 'R'; - $b = (strpos($border,'T')!==false) ? $b2.'T' : $b2; - } - } - $sep = -1; - $i = 0; - $j = 0; - $l = 0; - $ns = 0; - $nl = 1; - while($i<$nb) - { - // Get next character - if ($this->unifontSubset) { - $c = mb_substr($s,$i,1,'UTF-8'); - } - else { - $c=$s[$i]; - } - if($c=="\n") - { - // Explicit line break - if($this->ws>0) - { - $this->ws = 0; - $this->_out('0 Tw'); - } - if ($this->unifontSubset) { - $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill); - } - else { - $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); - } - $i++; - $sep = -1; - $j = $i; - $l = 0; - $ns = 0; - $nl++; - if($border && $nl==2) - $b = $b2; - continue; - } - if($c==' ') - { - $sep = $i; - $ls = $l; - $ns++; - } - - if ($this->unifontSubset) { $l += $this->GetStringWidth($c); } - else { $l += $cw[$c]*$this->FontSize/1000; } - - if($l>$wmax) - { - // Automatic line break - if($sep==-1) - { - if($i==$j) - $i++; - if($this->ws>0) - { - $this->ws = 0; - $this->_out('0 Tw'); - } - if ($this->unifontSubset) { - $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill); - } - else { - $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); - } - } - else - { - if($align=='J') - { - $this->ws = ($ns>1) ? ($wmax-$ls)/($ns-1) : 0; - $this->_out(sprintf('%.3F Tw',$this->ws*$this->k)); - } - if ($this->unifontSubset) { - $this->Cell($w,$h,mb_substr($s,$j,$sep-$j,'UTF-8'),$b,2,$align,$fill); - } - else { - $this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill); - } - $i = $sep+1; - } - $sep = -1; - $j = $i; - $l = 0; - $ns = 0; - $nl++; - if($border && $nl==2) - $b = $b2; - } - else - $i++; - } - // Last chunk - if($this->ws>0) - { - $this->ws = 0; - $this->_out('0 Tw'); - } - if($border && strpos($border,'B')!==false) - $b .= 'B'; - if ($this->unifontSubset) { - $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill); - } - else { - $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); - } - $this->x = $this->lMargin; - } - - function Write($h, $txt, $link='') - { - // Output text in flowing mode - $cw = &$this->CurrentFont['cw']; - $w = $this->w-$this->rMargin-$this->x; - - $wmax = ($w-2*$this->cMargin); - $s = str_replace("\r",'',$txt); - if ($this->unifontSubset) { - $nb = mb_strlen($s, 'UTF-8'); - if($nb==1 && $s==" ") { - $this->x += $this->GetStringWidth($s); - return; - } - } - else { - $nb = strlen($s); - } - $sep = -1; - $i = 0; - $j = 0; - $l = 0; - $nl = 1; - while($i<$nb) - { - // Get next character - if ($this->unifontSubset) { - $c = mb_substr($s,$i,1,'UTF-8'); - } - else { - $c = $s[$i]; - } - if($c=="\n") - { - // Explicit line break - if ($this->unifontSubset) { - $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,2,'',0,$link); - } - else { - $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link); - } - $i++; - $sep = -1; - $j = $i; - $l = 0; - if($nl==1) - { - $this->x = $this->lMargin; - $w = $this->w-$this->rMargin-$this->x; - $wmax = ($w-2*$this->cMargin); - } - $nl++; - continue; - } - if($c==' ') - $sep = $i; - - if ($this->unifontSubset) { $l += $this->GetStringWidth($c); } - else { $l += $cw[$c]*$this->FontSize/1000; } - - if($l>$wmax) - { - // Automatic line break - if($sep==-1) - { - if($this->x>$this->lMargin) - { - // Move to next line - $this->x = $this->lMargin; - $this->y += $h; - $w = $this->w-$this->rMargin-$this->x; - $wmax = ($w-2*$this->cMargin); - $i++; - $nl++; - continue; - } - if($i==$j) - $i++; - if ($this->unifontSubset) { - $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,2,'',0,$link); - } - else { - $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link); - } - } - else - { - if ($this->unifontSubset) { - $this->Cell($w,$h,mb_substr($s,$j,$sep-$j,'UTF-8'),0,2,'',0,$link); - } - else { - $this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',0,$link); - } - $i = $sep+1; - } - $sep = -1; - $j = $i; - $l = 0; - if($nl==1) - { - $this->x = $this->lMargin; - $w = $this->w-$this->rMargin-$this->x; - $wmax = ($w-2*$this->cMargin); - } - $nl++; - } - else - $i++; - } - // Last chunk - if($i!=$j) { - if ($this->unifontSubset) { - $this->Cell($l,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,0,'',0,$link); - } - else { - $this->Cell($l,$h,substr($s,$j),0,0,'',0,$link); - } - } - } - - function Ln($h=null) - { - // Line feed; default value is last cell height - $this->x = $this->lMargin; - if($h===null) - $this->y += $this->lasth; - else - $this->y += $h; - } - - function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='') - { - // Put an image on the page - if(!isset($this->images[$file])) - { - // First use of this image, get info - if($type=='') - { - $pos = strrpos($file,'.'); - if(!$pos) - $this->Error('Image file has no extension and no type was specified: '.$file); - $type = substr($file,$pos+1); - } - $type = strtolower($type); - if($type=='jpeg') - $type = 'jpg'; - $mtd = '_parse'.$type; - if(!method_exists($this,$mtd)) - $this->Error('Unsupported image type: '.$type); - $info = $this->$mtd($file); - $info['i'] = count($this->images)+1; - $this->images[$file] = $info; - } - else - $info = $this->images[$file]; - - // Automatic width and height calculation if needed - if($w==0 && $h==0) - { - // Put image at 96 dpi - $w = -96; - $h = -96; - } - if($w<0) - $w = -$info['w']*72/$w/$this->k; - if($h<0) - $h = -$info['h']*72/$h/$this->k; - if($w==0) - $w = $h*$info['w']/$info['h']; - if($h==0) - $h = $w*$info['h']/$info['w']; - - // Flowing mode - if($y===null) - { - if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak()) - { - // Automatic page break - $x2 = $this->x; - $this->AddPage($this->CurOrientation,$this->CurPageSize); - $this->x = $x2; - } - $y = $this->y; - $this->y += $h; - } - - if($x===null) - $x = $this->x; - $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'])); - if($link) - $this->Link($x,$y,$w,$h,$link); - } - - function GetX() - { - // Get x position - return $this->x; - } - - function SetX($x) - { - // Set x position - if($x>=0) - $this->x = $x; - else - $this->x = $this->w+$x; - } - - function GetY() - { - // Get y position - return $this->y; - } - - function SetY($y) - { - // Set y position and reset x - $this->x = $this->lMargin; - if($y>=0) - $this->y = $y; - else - $this->y = $this->h+$y; - } - - function SetXY($x, $y) - { - // Set x and y positions - $this->SetY($y); - $this->SetX($x); - } - - function Output($name='', $dest='') - { - // Output PDF to some destination - if($this->state<3) - $this->Close(); - $dest = strtoupper($dest); - if($dest=='') - { - if($name=='') - { - $name = 'doc.pdf'; - $dest = 'I'; - } - else - $dest = 'F'; - } - switch($dest) - { - case 'I': - // Send to standard output - $this->_checkoutput(); - if(PHP_SAPI!='cli') - { - // We send to a browser - header('Content-Type: application/pdf'); - header('Content-Disposition: inline; filename="'.$name.'"'); - header('Cache-Control: private, max-age=0, must-revalidate'); - header('Pragma: public'); - } - echo $this->buffer; - break; - case 'D': - // Download file - $this->_checkoutput(); - header('Content-Type: application/x-download'); - header('Content-Disposition: attachment; filename="'.$name.'"'); - 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) - $this->Error('Unable to create output file: '.$name); - fwrite($f,$this->buffer,strlen($this->buffer)); - fclose($f); - break; - case 'S': - // Return as a string - return $this->buffer; - default: - $this->Error('Incorrect output destination: '.$dest); - } - return ''; - } - - /******************************************************************************* - * * - * Protected methods * - * * - *******************************************************************************/ - 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'); - // Ensure runtime magic quotes are disabled - if(version_compare(PHP_VERSION, '5.3.0', '<') and get_magic_quotes_runtime()) - @set_magic_quotes_runtime(0); - } - - function _getfontpath() - { - return $this->fontpath; - } - - function _checkoutput() - { - if(PHP_SAPI!='cli') - { - if(headers_sent($file,$line)) - $this->Error("Some data has already been output, can't send PDF file (output started at $file:$line)"); - } - if(ob_get_length()) - { - // The output buffer is not empty - 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_clean(); - } - else - $this->Error("Some data has already been output, can't send PDF file"); - } - } - - function _getpagesize($size) - { - if(is_string($size)) - { - $size = strtolower($size); - if(!isset($this->StdPageSizes[$size])) - $this->Error('Unknown page size: '.$size); - $a = $this->StdPageSizes[$size]; - return array($a[0]/$this->k, $a[1]/$this->k); - } - else - { - if($size[0]>$size[1]) - return array($size[1], $size[0]); - else - return $size; - } - } - - function _beginpage($orientation, $size) - { - $this->page++; - $this->pages[$this->page] = ''; - $this->state = 2; - $this->x = $this->lMargin; - $this->y = $this->tMargin; - $this->FontFamily = ''; - // Check page size and orientation - if($orientation=='') - $orientation = $this->DefOrientation; - else - $orientation = strtoupper($orientation[0]); - if($size=='') - $size = $this->DefPageSize; - else - $size = $this->_getpagesize($size); - if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1]) - { - // New size or orientation - if($orientation=='P') - { - $this->w = $size[0]; - $this->h = $size[1]; - } - else - { - $this->w = $size[1]; - $this->h = $size[0]; - } - $this->wPt = $this->w*$this->k; - $this->hPt = $this->h*$this->k; - $this->PageBreakTrigger = $this->h-$this->bMargin; - $this->CurOrientation = $orientation; - $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); - } - - function _endpage() - { - $this->state = 1; - } - - function _loadfont($font) - { - // Load a font definition file from the font directory - include($this->fontpath.$font); - $a = get_defined_vars(); - if(!isset($a['name'])) - $this->Error('Could not include font definition file'); - return $a; - } - - function _escape($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; - } - - function _textstring($s) - { - // Format a text string - 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) - { - // Underline text - $up = $this->CurrentFont['up']; - $ut = $this->CurrentFont['ut']; - $w = $this->GetStringWidth($txt)+$this->ws*substr_count($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) - { - // Extract info from a JPEG file - $a = getimagesize($file); - if(!$a) - $this->Error('Missing or incorrect image file: '.$file); - if($a[2]!=2) - $this->Error('Not a JPEG file: '.$file); - if(!isset($a['channels']) || $a['channels']==3) - $colspace = 'DeviceRGB'; - elseif($a['channels']==4) - $colspace = 'DeviceCMYK'; - else - $colspace = 'DeviceGray'; - $bpc = isset($a['bits']) ? $a['bits'] : 8; - $data = file_get_contents($file); - return array('w'=>$a[0], 'h'=>$a[1], 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'DCTDecode', 'data'=>$data); - } - - function _parsepng($file) - { - // Extract info from a PNG file - $f = fopen($file,'rb.php'); - if(!$f) - $this->Error('Can\'t open image file: '.$file); - $info = $this->_parsepngstream($f,$file); - fclose($f); - return $info; - } - - function _parsepngstream($f, $file) - { - // Check signature - if($this->_readstream($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) - $this->Error('Not a PNG file: '.$file); - - // Read header chunk - $this->_readstream($f,4); - if($this->_readstream($f,4)!='IHDR') - $this->Error('Incorrect PNG file: '.$file); - $w = $this->_readint($f); - $h = $this->_readint($f); - $bpc = ord($this->_readstream($f,1)); - if($bpc>8) - $this->Error('16-bit depth not supported: '.$file); - $ct = ord($this->_readstream($f,1)); - if($ct==0 || $ct==4) - $colspace = 'DeviceGray'; - elseif($ct==2 || $ct==6) - $colspace = 'DeviceRGB'; - elseif($ct==3) - $colspace = 'Indexed'; - else - $this->Error('Unknown color type: '.$file); - if(ord($this->_readstream($f,1))!=0) - $this->Error('Unknown compression method: '.$file); - if(ord($this->_readstream($f,1))!=0) - $this->Error('Unknown filter method: '.$file); - if(ord($this->_readstream($f,1))!=0) - $this->Error('Interlacing not supported: '.$file); - $this->_readstream($f,4); - $dp = '/Predictor 15 /Colors '.($colspace=='DeviceRGB' ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w; - - // Scan chunks looking for palette, transparency and image data - $pal = ''; - $trns = ''; - $data = ''; - do - { - $n = $this->_readint($f); - $type = $this->_readstream($f,4); - if($type=='PLTE') - { - // Read palette - $pal = $this->_readstream($f,$n); - $this->_readstream($f,4); - } - elseif($type=='tRNS') - { - // Read transparency info - $t = $this->_readstream($f,$n); - if($ct==0) - $trns = array(ord(substr($t,1,1))); - elseif($ct==2) - $trns = array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1))); - else - { - $pos = strpos($t,chr(0)); - if($pos!==false) - $trns = array($pos); - } - $this->_readstream($f,4); - } - elseif($type=='IDAT') - { - // Read image data block - $data .= $this->_readstream($f,$n); - $this->_readstream($f,4); - } - elseif($type=='IEND') - break; - else - $this->_readstream($f,$n+4); - } - while($n); - - if($colspace=='Indexed' && empty($pal)) - $this->Error('Missing palette in '.$file); - $info = array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'dp'=>$dp, 'pal'=>$pal, 'trns'=>$trns); - if($ct>=4) - { - // Extract alpha channel - if(!function_exists('gzuncompress')) - $this->Error('Zlib not available, can\'t handle alpha channel: '.$file); - $data = gzuncompress($data); - $color = ''; - $alpha = ''; - if($ct==4) - { - // Gray image - $len = 2*$w; - for($i=0;$i<$h;$i++) - { - $pos = (1+$len)*$i; - $color .= $data[$pos]; - $alpha .= $data[$pos]; - $line = substr($data,$pos+1,$len); - $color .= preg_replace('/(.)./s','$1',$line); - $alpha .= preg_replace('/.(.)/s','$1',$line); - } - } - else - { - // RGB image - $len = 4*$w; - for($i=0;$i<$h;$i++) - { - $pos = (1+$len)*$i; - $color .= $data[$pos]; - $alpha .= $data[$pos]; - $line = substr($data,$pos+1,$len); - $color .= preg_replace('/(.{3})./s','$1',$line); - $alpha .= preg_replace('/.{3}(.)/s','$1',$line); - } - } - unset($data); - $data = gzcompress($color); - $info['smask'] = gzcompress($alpha); - if($this->PDFVersion<'1.4') - $this->PDFVersion = '1.4'; - } - $info['data'] = $data; - return $info; - } - - function _readstream($f, $n) - { - // Read n bytes from stream - $res = ''; - while($n>0 && !feof($f)) - { - $s = fread($f,$n); - if($s===false) - $this->Error('Error while reading stream'); - $n -= strlen($s); - $res .= $s; - } - if($n>0) - $this->Error('Unexpected end of stream'); - return $res; - } - - function _readint($f) - { - // Read a 4-byte integer from stream - $a = unpack('Ni',$this->_readstream($f,4)); - return $a['i']; - } - - function _parsegif($file) - { - // Extract info from a GIF file (via PNG conversion) - if(!function_exists('imagepng')) - $this->Error('GD extension is required for GIF support'); - if(!function_exists('imagecreatefromgif')) - $this->Error('GD has no GIF read support'); - $im = imagecreatefromgif($file); - if(!$im) - $this->Error('Missing or incorrect image file: '.$file); - imageinterlace($im,0); - $f = @fopen('php://temp','rb.php+'); - if($f) - { - // Perform conversion in memory - ob_start(); - imagepng($im); - $data = ob_get_clean(); - imagedestroy($im); - 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'); - imagedestroy($im); - $info = $this->_parsepng($tmp); - unlink($tmp); - } - 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) - { - // Add a line to the document - if($this->state==2) - $this->pages[$this->page] .= $s."\n"; - else - $this->buffer .= $s."\n"; - } - - function _putpages() - { - $nb = $this->page; - if(!empty($this->AliasNbPages)) - { - // 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]); - } - if($this->DefOrientation=='P') - { - $wPt = $this->DefPageSize[0]*$this->k; - $hPt = $this->DefPageSize[1]*$this->k; - } - else - { - $wPt = $this->DefPageSize[1]*$this->k; - $hPt = $this->DefPageSize[0]*$this->k; - } - $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'); - } - - 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.php',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); - } - } - $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; - $type = $font['type']; - $name = $font['name']; - if($type=='Core') - { - // Standard font - $this->fonts[$k]['n']=$this->n+1; - $this->_newobj(); - $this->_out('<_out('/BaseFont /'.$name); - $this->_out('/Subtype /Type1'); - if($name!='Symbol' && $name!='ZapfDingbats') - $this->_out('/Encoding /WinAnsiEncoding'); - $this->_out('>>'); - $this->_out('endobj'); - } - elseif($type=='Type1' || $type=='TrueType') - { - // Additional Type1 or TrueType font - $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'); - if($font['enc']) - { - if(isset($font['diff'])) - $this->_out('/Encoding '.($nf+$font['diff']).' 0 R'); - else - $this->_out('/Encoding /WinAnsiEncoding'); - } - $this->_out('>>'); - $this->_out('endobj'); - // Widths - $this->_newobj(); - $cw=&$font['cw']; - $s='['; - for($i=32;$i<=255;$i++) - $s.=$cw[chr($i)].' '; - $this->_out($s.']'); - $this->_out('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'); - } - // TrueType embedded SUBSETS or FULL - else if ($type=='TTF') { - $this->fonts[$k]['n']=$this->n+1; - require_once($this->_getfontpath().'unifont/ttfonts.php'); - $ttf = new TTFontFile(); - $fontname = 'MPDFAA'.'+'.$font['name']; - $subset = $font['subset']; - unset($subset[0]); - $ttfontstream = $ttf->makeSubset($font['ttffile'], $subset); - $ttfontsize = strlen($ttfontstream); - $fontstream = gzcompress($ttfontstream); - $codeToGlyph = $ttf->codeToGlyph; - unset($codeToGlyph[0]); - - // 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'); - - // 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'); - 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'); - - // ToUnicode - $this->_newobj(); - $toUni = "/CIDInit /ProcSet findresource begin\n"; - $toUni .= "12 dict begin\n"; - $toUni .= "begincmap\n"; - $toUni .= "/CIDSystemInfo\n"; - $toUni .= "<_out('<>'); - $this->_putstream($toUni); - $this->_out('endobj'); - - // CIDSystemInfo dictionary - $this->_newobj(); - $this->_out('<_out('/Ordering (UCS)'); - $this->_out('/Supplement 0'); - $this->_out('>>'); - $this->_out('endobj'); - - // Font descriptor - $this->_newobj(); - $this->_out('<_out('/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'); - - // Embed CIDToGIDMap - // A specification of the mapping from CIDs to glyph indices - $cidtogidmap = ''; - $cidtogidmap = str_pad('', 256*256*2, "\x00"); - foreach($codeToGlyph as $cc=>$glyph) { - $cidtogidmap[$cc*2] = chr($glyph >> 8); - $cidtogidmap[$cc*2 + 1] = chr($glyph & 0xFF); - } - $cidtogidmap = gzcompress($cidtogidmap); - $this->_newobj(); - $this->_out('<_out('/Filter /FlateDecode'); - $this->_out('>>'); - $this->_putstream($cidtogidmap); - $this->_out('endobj'); - - //Font file - $this->_newobj(); - $this->_out('<_out('/Filter /FlateDecode'); - $this->_out('/Length1 '.$ttfontsize); - $this->_out('>>'); - $this->_putstream($fontstream); - $this->_out('endobj'); - unset($ttf); - } - else - { - // Allow for additional types - $this->fonts[$k]['n'] = $this->n+1; - $mtd='_put'.strtolower($type); - if(!method_exists($this,$mtd)) - $this->Error('Unsupported font type: '.$type); - $this->$mtd($font); - } - } - } - - function _putTTfontwidths(&$font, $maxUni) { - if (file_exists($font['unifilename'].'.cw127.php')) { - include($font['unifilename'].'.cw127.php') ; - $startcid = 128; - } - else { - $rangeid = 0; - $range = array(); - $prevcid = -2; - $prevwidth = -1; - $interval = false; - $startcid = 1; - } - $cwlen = $maxUni + 1; - - // 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'))) { - $fh = fopen($font['unifilename'].'.cw127.php',"wb"); - $cw127='"; - fwrite($fh,$cw127,strlen($cw127)); - fclose($fh); - } - } - if ($font['cw'][$cid*2] == "\00" && $font['cw'][$cid*2+1] == "\00") { continue; } - $width = (ord($font['cw'][$cid*2]) << 8) + ord($font['cw'][$cid*2+1]); - if ($width == 65535) { $width = 0; } - if ($cid > 255 && (!isset($font['subset'][$cid]) || !$font['subset'][$cid])) { continue; } - if (!isset($font['dw']) || (isset($font['dw']) && $width != $font['dw'])) { - if ($cid == ($prevcid + 1)) { - if ($width == $prevwidth) { - if ($width == $range[$rangeid][0]) { - $range[$rangeid][] = $width; - } - else { - array_pop($range[$rangeid]); - // new range - $rangeid = $prevcid; - $range[$rangeid] = array(); - $range[$rangeid][] = $prevwidth; - $range[$rangeid][] = $width; - } - $interval = true; - $range[$rangeid]['interval'] = true; - } else { - if ($interval) { - // new range - $rangeid = $cid; - $range[$rangeid] = array(); - $range[$rangeid][] = $width; - } - else { $range[$rangeid][] = $width; } - $interval = false; - } - } else { - $rangeid = $cid; - $range[$rangeid] = array(); - $range[$rangeid][] = $width; - $interval = false; - } - $prevcid = $cid; - $prevwidth = $width; - } - } - $prevk = -1; - $nextk = -1; - $prevint = false; - foreach ($range as $k => $ws) { - $cws = count($ws); - if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) { - if (isset($range[$k]['interval'])) { unset($range[$k]['interval']); } - $range[$prevk] = array_merge($range[$prevk], $range[$k]); - unset($range[$k]); - } - else { $prevk = $k; } - $nextk = $k + $cws; - if (isset($ws['interval'])) { - if ($cws > 3) { $prevint = true; } - else { $prevint = false; } - unset($range[$k]['interval']); - --$nextk; - } - else { $prevint = false; } - } - $w = ''; - foreach ($range as $k => $ws) { - if (count(array_count_values($ws)) == 1) { $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0]; } - else { $w .= ' '.$k.' [ '.implode(' ', $ws).' ]' . "\n"; } - } - $this->_out('/W ['.$w.' ]'); - } - - function _putimages() - { - foreach(array_keys($this->images) as $file) - { - $this->_putimage($this->images[$file]); - unset($this->images[$file]['data']); - unset($this->images[$file]['smask']); - } - } - - function _putimage(&$info) - { - $this->_newobj(); - $info['n'] = $this->n; - $this->_out('<_out('/Subtype /Image'); - $this->_out('/Width '.$info['w']); - $this->_out('/Height '.$info['h']); - if($info['cs']=='Indexed') - $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]'); - else - { - $this->_out('/ColorSpace /'.$info['cs']); - if($info['cs']=='DeviceCMYK') - $this->_out('/Decode [1 0 1 0 1 0 1 0]'); - } - $this->_out('/BitsPerComponent '.$info['bpc']); - if(isset($info['f'])) - $this->_out('/Filter /'.$info['f']); - if(isset($info['dp'])) - $this->_out('/DecodeParms <<'.$info['dp'].'>>'); - if(isset($info['trns']) && is_array($info['trns'])) - { - $trns = ''; - for($i=0;$i_out('/Mask ['.$trns.']'); - } - if(isset($info['smask'])) - $this->_out('/SMask '.($this->n+1).' 0 R'); - $this->_out('/Length '.strlen($info['data']).'>>'); - $this->_putstream($info['data']); - $this->_out('endobj'); - // Soft mask - if(isset($info['smask'])) - { - $dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns '.$info['w']; - $smask = array('w'=>$info['w'], 'h'=>$info['h'], 'cs'=>'DeviceGray', 'bpc'=>8, 'f'=>$info['f'], 'dp'=>$dp, 'data'=>$info['smask']); - $this->_putimage($smask); - } - // 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'); - } - } - - function _putxobjectdict() - { - foreach($this->images as $image) - $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R'); - } - - 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->_putxobjectdict(); - $this->_out('>>'); - } - - function _putresources() - { - $this->_putfonts(); - $this->_putimages(); - // Resource dictionary - $this->offsets[2] = strlen($this->buffer); - $this->_out('2 0 obj'); - $this->_out('<<'); - $this->_putresourcedict(); - $this->_out('>>'); - $this->_out('endobj'); - } - - 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'))); - } - - function _putcatalog() - { - $this->_out('/Type /Catalog'); - $this->_out('/Pages 1 0 R'); - if($this->ZoomMode=='fullpage') - $this->_out('/OpenAction [3 0 R /Fit]'); - elseif($this->ZoomMode=='fullwidth') - $this->_out('/OpenAction [3 0 R /FitH null]'); - elseif($this->ZoomMode=='real') - $this->_out('/OpenAction [3 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).']'); - if($this->LayoutMode=='single') - $this->_out('/PageLayout /SinglePage'); - elseif($this->LayoutMode=='continuous') - $this->_out('/PageLayout /OneColumn'); - elseif($this->LayoutMode=='two') - $this->_out('/PageLayout /TwoColumnLeft'); - } - - function _putheader() - { - $this->_out('%PDF-'.$this->PDFVersion); - } - - function _puttrailer() - { - $this->_out('/Size '.($this->n+1)); - $this->_out('/Root '.$this->n.' 0 R'); - $this->_out('/Info '.($this->n-1).' 0 R'); - } - - function _enddoc() - { - $this->_putheader(); - $this->_putpages(); - $this->_putresources(); - // Info - $this->_newobj(); - $this->_out('<<'); - $this->_putinfo(); - $this->_out('>>'); - $this->_out('endobj'); - // Catalog - $this->_newobj(); - $this->_out('<<'); - $this->_putcatalog(); - $this->_out('>>'); - $this->_out('endobj'); - // Cross-ref - $o = strlen($this->buffer); - $this->_out('xref'); - $this->_out('0 '.($this->n+1)); - $this->_out('0000000000 65535 f '); - for($i=1;$i<=$this->n;$i++) - $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i])); - // Trailer - $this->_out('trailer'); - $this->_out('<<'); - $this->_puttrailer(); - $this->_out('>>'); - $this->_out('startxref'); - $this->_out($o); - $this->_out('%%EOF'); - $this->state = 3; - } - - // ********* NEW FUNCTIONS ********* - // Converts UTF-8 strings to UTF16-BE. - function UTF8ToUTF16BE($str, $setbom=true) { - $outstr = ""; - if ($setbom) { - $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM) - } - $outstr .= mb_convert_encoding($str, 'UTF-16BE', 'UTF-8'); - return $outstr; - } - - // Converts UTF-8 strings to codepoints array - function UTF8StringToArray($str) { - $out = array(); - $len = strlen($str); - for ($i = 0; $i < $len; $i++) { - $uni = -1; - $h = ord($str[$i]); - if ( $h <= 0x7F ) - $uni = $h; - elseif ( $h >= 0xC2 ) { - if ( ($h <= 0xDF) && ($i < $len -1) ) - $uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F); - elseif ( ($h <= 0xEF) && ($i < $len -2) ) - $uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6 - | (ord($str[++$i]) & 0x3F); - elseif ( ($h <= 0xF4) && ($i < $len -3) ) - $uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12 - | (ord($str[++$i]) & 0x3F) << 6 - | (ord($str[++$i]) & 0x3F); - } - if ($uni >= 0) { - $out[] = $uni; - } - } - return $out; - } -} - -// Handle special IE contype request -if(isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] == 'contype') +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 $PDFVersion; // PDF version number + +/******************************************************************************* +* Public methods * +*******************************************************************************/ + +function __construct($orientation='P', $unit='mm', $size='A4') { - header('Content-Type: application/pdf'); - exit; + // Some checks + $this->_dochecks(); + // Initialization of properties + $this->state = 0; + $this->page = 0; + $this->n = 2; + $this->buffer = ''; + $this->pages = array(); + $this->PageInfo = array(); + $this->fonts = array(); + $this->FontFiles = array(); + $this->encodings = array(); + $this->cmaps = array(); + $this->images = array(); + $this->links = array(); + $this->InHeader = false; + $this->InFooter = false; + $this->lasth = 0; + $this->FontFamily = ''; + $this->FontStyle = ''; + $this->FontSizePt = 12; + $this->underline = false; + $this->DrawColor = '0 G'; + $this->FillColor = '0 g'; + $this->TextColor = '0 g'; + $this->ColorFlag = false; + $this->WithAlpha = false; + $this->ws = 0; + // Font path + if(defined('FPDF_FONTPATH')) + { + $this->fontpath = FPDF_FONTPATH; + if(substr($this->fontpath,-1)!='/' && substr($this->fontpath,-1)!='\\') + $this->fontpath .= '/'; + } + elseif(is_dir(dirname(__FILE__).'/font')) + $this->fontpath = dirname(__FILE__).'/font/'; + else + $this->fontpath = ''; + // Core fonts + $this->CoreFonts = array('courier', 'helvetica', 'times', 'symbol', 'zapfdingbats'); + // Scale factor + if($unit=='pt') + $this->k = 1; + elseif($unit=='mm') + $this->k = 72/25.4; + elseif($unit=='cm') + $this->k = 72/2.54; + elseif($unit=='in') + $this->k = 72; + else + $this->Error('Incorrect unit: '.$unit); + // Page sizes + $this->StdPageSizes = array('a3'=>array(841.89,1190.55), 'a4'=>array(595.28,841.89), 'a5'=>array(420.94,595.28), + 'letter'=>array(612,792), 'legal'=>array(612,1008)); + $size = $this->_getpagesize($size); + $this->DefPageSize = $size; + $this->CurPageSize = $size; + // Page orientation + $orientation = strtolower($orientation); + if($orientation=='p' || $orientation=='portrait') + { + $this->DefOrientation = 'P'; + $this->w = $size[0]; + $this->h = $size[1]; + } + elseif($orientation=='l' || $orientation=='landscape') + { + $this->DefOrientation = 'L'; + $this->w = $size[1]; + $this->h = $size[0]; + } + else + $this->Error('Incorrect orientation: '.$orientation); + $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); + // Interior cell margin (1 mm) + $this->cMargin = $margin/10; + // Line width (0.2 mm) + $this->LineWidth = .567/$this->k; + // Automatic page break + $this->SetAutoPageBreak(true,2*$margin); + // Default display mode + $this->SetDisplayMode('default'); + // Enable compression + $this->SetCompression(true); + // Set default PDF version number + $this->PDFVersion = '1.3'; } + +function SetMargins($left, $top, $right=null) +{ + // Set left, top and right margins + $this->lMargin = $left; + $this->tMargin = $top; + if($right===null) + $right = $left; + $this->rMargin = $right; +} + +function SetLeftMargin($margin) +{ + // Set left margin + $this->lMargin = $margin; + if($this->page>0 && $this->x<$margin) + $this->x = $margin; +} + +function SetTopMargin($margin) +{ + // Set top margin + $this->tMargin = $margin; +} + +function SetRightMargin($margin) +{ + // Set right margin + $this->rMargin = $margin; +} + +function SetAutoPageBreak($auto, $margin=0) +{ + // Set auto page break mode and triggering margin + $this->AutoPageBreak = $auto; + $this->bMargin = $margin; + $this->PageBreakTrigger = $this->h-$margin; +} + +function SetDisplayMode($zoom, $layout='default') +{ + // Set display mode in viewer + if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom)) + $this->ZoomMode = $zoom; + else + $this->Error('Incorrect zoom display mode: '.$zoom); + if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default') + $this->LayoutMode = $layout; + else + $this->Error('Incorrect layout display mode: '.$layout); +} + +function SetCompression($compress) +{ + // Set page compression + if(function_exists('gzcompress')) + $this->compress = $compress; + else + $this->compress = false; +} + +function SetTitle($title, $isUTF8=false) +{ + // Title of document + $this->metadata['Title'] = $isUTF8 ? $title : utf8_encode($title); +} + +function SetAuthor($author, $isUTF8=false) +{ + // Author of document + $this->metadata['Author'] = $isUTF8 ? $author : utf8_encode($author); +} + +function SetSubject($subject, $isUTF8=false) +{ + // Subject of document + $this->metadata['Subject'] = $isUTF8 ? $subject : utf8_encode($subject); +} + +function SetKeywords($keywords, $isUTF8=false) +{ + // Keywords of document + $this->metadata['Keywords'] = $isUTF8 ? $keywords : utf8_encode($keywords); +} + +function SetCreator($creator, $isUTF8=false) +{ + // Creator of document + $this->metadata['Creator'] = $isUTF8 ? $creator : utf8_encode($creator); +} + +function AliasNbPages($alias='{nb}') +{ + // Define an alias for total number of pages + $this->AliasNbPages = $alias; +} + +function Error($msg) +{ + // Fatal error + throw new Exception('FPDF error: '.$msg); +} + +function Close() +{ + // Terminate document + if($this->state==3) + return; + if($this->page==0) + $this->AddPage(); + // Page footer + $this->InFooter = true; + $this->Footer(); + $this->InFooter = false; + // Close page + $this->_endpage(); + // Close document + $this->_enddoc(); +} + +function AddPage($orientation='', $size='', $rotation=0) +{ + // Start a new page + if($this->state==3) + $this->Error('The document is closed'); + $family = $this->FontFamily; + $style = $this->FontStyle.($this->underline ? 'U' : ''); + $fontsize = $this->FontSizePt; + $lw = $this->LineWidth; + $dc = $this->DrawColor; + $fc = $this->FillColor; + $tc = $this->TextColor; + $cf = $this->ColorFlag; + if($this->page>0) + { + // Page footer + $this->InFooter = true; + $this->Footer(); + $this->InFooter = false; + // Close page + $this->_endpage(); + } + // Start new page + $this->_beginpage($orientation,$size,$rotation); + // Set line cap style to square + $this->_out('2 J'); + // Set line width + $this->LineWidth = $lw; + $this->_out(sprintf('%.2F w',$lw*$this->k)); + // Set font + if($family) + $this->SetFont($family,$style,$fontsize); + // Set colors + $this->DrawColor = $dc; + if($dc!='0 G') + $this->_out($dc); + $this->FillColor = $fc; + if($fc!='0 g') + $this->_out($fc); + $this->TextColor = $tc; + $this->ColorFlag = $cf; + // Page header + $this->InHeader = true; + $this->Header(); + $this->InHeader = false; + // Restore line width + if($this->LineWidth!=$lw) + { + $this->LineWidth = $lw; + $this->_out(sprintf('%.2F w',$lw*$this->k)); + } + // Restore font + if($family) + $this->SetFont($family,$style,$fontsize); + // Restore colors + if($this->DrawColor!=$dc) + { + $this->DrawColor = $dc; + $this->_out($dc); + } + if($this->FillColor!=$fc) + { + $this->FillColor = $fc; + $this->_out($fc); + } + $this->TextColor = $tc; + $this->ColorFlag = $cf; +} + +function Header() +{ + // To be implemented in your own inherited class +} + +function Footer() +{ + // To be implemented in your own inherited class +} + +function PageNo() +{ + // Get current page number + return $this->page; +} + +function SetDrawColor($r, $g=null, $b=null) +{ + // Set color for all stroking operations + if(($r==0 && $g==0 && $b==0) || $g===null) + $this->DrawColor = sprintf('%.3F G',$r/255); + else + $this->DrawColor = sprintf('%.3F %.3F %.3F RG',$r/255,$g/255,$b/255); + if($this->page>0) + $this->_out($this->DrawColor); +} + +function SetFillColor($r, $g=null, $b=null) +{ + // Set color for all filling operations + if(($r==0 && $g==0 && $b==0) || $g===null) + $this->FillColor = sprintf('%.3F g',$r/255); + else + $this->FillColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255); + $this->ColorFlag = ($this->FillColor!=$this->TextColor); + if($this->page>0) + $this->_out($this->FillColor); +} + +function SetTextColor($r, $g=null, $b=null) +{ + // Set color for text + if(($r==0 && $g==0 && $b==0) || $g===null) + $this->TextColor = sprintf('%.3F g',$r/255); + else + $this->TextColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255); + $this->ColorFlag = ($this->FillColor!=$this->TextColor); +} + +function GetStringWidth($s) +{ + // Get width of a string in the current font + $s = (string)$s; + $cw = &$this->CurrentFont['cw']; + $w=0; + if ($this->unifontSubset) { + $unicode = $this->UTF8StringToArray($s); + foreach($unicode as $char) { + 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']; } + else { $w += 500; } + } + } + else { + $l = strlen($s); + for($i=0;$i<$l;$i++) + $w += $cw[$s[$i]]; + } + return $w*$this->FontSize/1000; +} + +function SetLineWidth($width) +{ + // Set line width + $this->LineWidth = $width; + if($this->page>0) + $this->_out(sprintf('%.2F w',$width*$this->k)); +} + +function Line($x1, $y1, $x2, $y2) +{ + // Draw a line + $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)); +} + +function Rect($x, $y, $w, $h, $style='') +{ + // Draw a rectangle + if($style=='F') + $op = 'f'; + elseif($style=='FD' || $style=='DF') + $op = 'B'; + else + $op = 'S'; + $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op)); +} + +function AddFont($family, $style='', $file='', $uni=false) +{ + // Add a TrueType, OpenType or Type1 font + $family = strtolower($family); + $style = strtoupper($style); + if($style=='IB') + $style = 'BI'; + if($file=='') { + if ($uni) { + $file = str_replace(' ','',$family).strtolower($style).'.ttf'; + } + else { + $file = str_replace(' ','',$family).strtolower($style).'.php'; + } + } + $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->fontpath.'unifont/'.$file ; } + $unifilename = $this->fontpath.'unifont/'.strtolower(substr($file ,0,(strpos($file ,'.')))); + $name = ''; + $originalsize = 0; + $ttfstat = stat($ttffilename); + if (file_exists($unifilename.'.mtx.php')) { + include($unifilename.'.mtx.php'); + } + if (!isset($type) || !isset($name) || $originalsize != $ttfstat['size']) { + $ttffile = $ttffilename; +// require_once($this->fontpath.'unifont/ttfonts.php'); + $ttf = new TTFontFile(); + $ttf->getMetrics($ttffile); + $cw = $ttf->charWidths; + $name = preg_replace('/[ ()]/','',$ttf->fullName); + + $desc= array('Ascent'=>round($ttf->ascent), + 'Descent'=>round($ttf->descent), + 'CapHeight'=>round($ttf->capHeight), + 'Flags'=>$ttf->flags, + 'FontBBox'=>'['.round($ttf->bbox[0])." ".round($ttf->bbox[1])." ".round($ttf->bbox[2])." ".round($ttf->bbox[3]).']', + 'ItalicAngle'=>$ttf->italicAngle, + 'StemV'=>round($ttf->stemV), + 'MissingWidth'=>round($ttf->defaultWidth)); + $up = round($ttf->underlinePosition); + $ut = round($ttf->underlineThickness); + $originalsize = $ttfstat['size']+0; + $type = 'TTF'; + // Generate metrics .php file + $s='"; + if (is_writable(dirname($this->fontpath.'unifont/'.'x'))) { + $fh = fopen($unifilename.'.mtx.php',"w"); + fwrite($fh,$s,strlen($s)); + fclose($fh); + $fh = fopen($unifilename.'.cw.dat',"wb"); + fwrite($fh,$cw,strlen($cw)); + fclose($fh); + @unlink($unifilename.'.cw127.php'); + } + unset($ttf); + } + else { + $cw = @file_get_contents($unifilename.'.cw.dat'); + } + $i = count($this->fonts)+1; + if(!empty($this->AliasNbPages)) + $sbarr = range(0,57); + else + $sbarr = range(0,32); + $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'ttffile'=>$ttffile, 'fontkey'=>$fontkey, 'subset'=>$sbarr, 'unifilename'=>$unifilename); + + $this->FontFiles[$fontkey]=array('length1'=>$originalsize, 'type'=>"TTF", 'ttffile'=>$ttffile); + $this->FontFiles[$file]=array('type'=>"TTF"); + unset($cw); + } + else { + $info = $this->_loadfont($file); + $info['i'] = count($this->fonts)+1; + if(!empty($info['file'])) + { + // Embedded font + if($info['type']=='TrueType') + $this->FontFiles[$info['file']] = array('length1'=>$info['originalsize']); + else + $this->FontFiles[$info['file']] = array('length1'=>$info['size1'], 'length2'=>$info['size2']); + } + $this->fonts[$fontkey] = $info; + } +} + +function SetFont($family, $style='', $size=0) +{ + // Select a font; size given in points + if($family=='') + $family = $this->FontFamily; + else + $family = strtolower($family); + $style = strtoupper($style); + if(strpos($style,'U')!==false) + { + $this->underline = true; + $style = str_replace('U','',$style); + } + else + $this->underline = false; + if($style=='IB') + $style = 'BI'; + if($size==0) + $size = $this->FontSizePt; + // 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])) + { + // Test if one of the core fonts + if($family=='arial') + $family = 'helvetica'; + if(in_array($family,$this->CoreFonts)) + { + if($family=='symbol' || $family=='zapfdingbats') + $style = ''; + $fontkey = $family.$style; + if(!isset($this->fonts[$fontkey])) + $this->AddFont($family,$style); + } + else + $this->Error('Undefined font: '.$family.' '.$style); + } + // Select it + $this->FontFamily = $family; + $this->FontStyle = $style; + $this->FontSizePt = $size; + $this->FontSize = $size/$this->k; + $this->CurrentFont = &$this->fonts[$fontkey]; + if ($this->fonts[$fontkey]['type']=='TTF') { $this->unifontSubset = true; } + else { $this->unifontSubset = false; } + if($this->page>0) + $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); +} + +function SetFontSize($size) +{ + // Set font size in points + if($this->FontSizePt==$size) + return; + $this->FontSizePt = $size; + $this->FontSize = $size/$this->k; + if($this->page>0) + $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); +} + +function AddLink() +{ + // Create a new internal link + $n = count($this->links)+1; + $this->links[$n] = array(0, 0); + return $n; +} + +function SetLink($link, $y=0, $page=-1) +{ + // Set destination of internal link + if($y==-1) + $y = $this->y; + if($page==-1) + $page = $this->page; + $this->links[$link] = array($page, $y); +} + +function Link($x, $y, $w, $h, $link) +{ + // Put a link on the page + $this->PageLinks[$this->page][] = array($x*$this->k, $this->hPt-$y*$this->k, $w*$this->k, $h*$this->k, $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)).')'; + foreach($this->UTF8StringToArray($txt) as $uni) + $this->CurrentFont['subset'][$uni] = $uni; + } + else + $txt2 = '('.$this->_escape($txt).')'; + $s = sprintf('BT %.2F %.2F Td %s Tj ET',$x*$this->k,($this->h-$y)*$this->k,$txt2); + if($this->underline && $txt!='') + $s .= ' '.$this->_dounderline($x,$y,$txt); + if($this->ColorFlag) + $s = 'q '.$this->TextColor.' '.$s.' Q'; + $this->_out($s); +} + +function AcceptPageBreak() +{ + // Accept automatic page break or not + return $this->AutoPageBreak; +} + +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()) + { + // Automatic page break + $x = $this->x; + $ws = $this->ws; + if($ws>0) + { + $this->ws = 0; + $this->_out('0 Tw'); + } + $this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation); + $this->x = $x; + if($ws>0) + { + $this->ws = $ws; + $this->_out(sprintf('%.3F Tw',$ws*$k)); + } + } + if($w==0) + $w = $this->w-$this->rMargin-$this->x; + $s = ''; + if($fill || $border==1) + { + if($fill) + $op = ($border==1) ? 'B' : 'f'; + else + $op = 'S'; + $s = sprintf('%.2F %.2F %.2F %.2F re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op); + } + if(is_string($border)) + { + $x = $this->x; + $y = $this->y; + if(strpos($border,'L')!==false) + $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k); + if(strpos($border,'T')!==false) + $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k); + if(strpos($border,'R')!==false) + $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k); + if(strpos($border,'B')!==false) + $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k); + } + 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') + $dx = ($w-$this->GetStringWidth($txt))/2; + else + $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) + $this->CurrentFont['subset'][$uni] = $uni; + $space = $this->_escape($this->UTF8ToUTF16BE(' ', false)); + $s .= sprintf('BT 0 Tw %.2F %.2F Td [',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k); + $t = explode(' ',$txt); + $numt = count($t); + for($i=0;$i<$numt;$i++) { + $tx = $t[$i]; + $tx = '('.$this->_escape($this->UTF8ToUTF16BE($tx, false)).')'; + $s .= sprintf('%s ',$tx); + if (($i+1)<$numt) { + $adj = -($this->ws*$this->k)*1000/$this->FontSizePt; + $s .= sprintf('%d(%s) ',$adj,$space); + } + } + $s .= '] TJ'; + $s .= ' ET'; + } + else { + if ($this->unifontSubset) + { + $txt2 = '('.$this->_escape($this->UTF8ToUTF16BE($txt, false)).')'; + foreach($this->UTF8StringToArray($txt) as $uni) + $this->CurrentFont['subset'][$uni] = $uni; + } + else + $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) + $s .= ' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt); + if($this->ColorFlag) + $s .= ' Q'; + if($link) + $this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link); + } + if($s) + $this->_out($s); + $this->lasth = $h; + if($ln>0) + { + // Go to next line + $this->y += $h; + if($ln==1) + $this->x = $this->lMargin; + } + else + $this->x += $w; +} + +function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false) +{ + // Output text with automatic or explicit line breaks + 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); + //$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--; + } + else { + $nb = strlen($s); + if($nb>0 && $s[$nb-1]=="\n") + $nb--; + } + $b = 0; + if($border) + { + if($border==1) + { + $border = 'LTRB'; + $b = 'LRT'; + $b2 = 'LR'; + } + else + { + $b2 = ''; + if(strpos($border,'L')!==false) + $b2 .= 'L'; + if(strpos($border,'R')!==false) + $b2 .= 'R'; + $b = (strpos($border,'T')!==false) ? $b2.'T' : $b2; + } + } + $sep = -1; + $i = 0; + $j = 0; + $l = 0; + $ns = 0; + $nl = 1; + while($i<$nb) + { + // Get next character + if ($this->unifontSubset) { + $c = mb_substr($s,$i,1,'UTF-8'); + } + else { + $c=$s[$i]; + } + if($c=="\n") + { + // Explicit line break + if($this->ws>0) + { + $this->ws = 0; + $this->_out('0 Tw'); + } + if ($this->unifontSubset) { + $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill); + } + else { + $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); + } + $i++; + $sep = -1; + $j = $i; + $l = 0; + $ns = 0; + $nl++; + if($border && $nl==2) + $b = $b2; + continue; + } + if($c==' ') + { + $sep = $i; + $ls = $l; + $ns++; + } + + if ($this->unifontSubset) { $l += $this->GetStringWidth($c); } + else { $l += $cw[$c]*$this->FontSize/1000; } + + if($l>$wmax) + { + // Automatic line break + if($sep==-1) + { + if($i==$j) + $i++; + if($this->ws>0) + { + $this->ws = 0; + $this->_out('0 Tw'); + } + if ($this->unifontSubset) { + $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill); + } + else { + $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); + } + } + else + { + if($align=='J') + { + $this->ws = ($ns>1) ? ($wmax-$ls)/($ns-1) : 0; + $this->_out(sprintf('%.3F Tw',$this->ws*$this->k)); + } + if ($this->unifontSubset) { + $this->Cell($w,$h,mb_substr($s,$j,$sep-$j,'UTF-8'),$b,2,$align,$fill); + } + else { + $this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill); + } + $i = $sep+1; + } + $sep = -1; + $j = $i; + $l = 0; + $ns = 0; + $nl++; + if($border && $nl==2) + $b = $b2; + } + else + $i++; + } + // Last chunk + if($this->ws>0) + { + $this->ws = 0; + $this->_out('0 Tw'); + } + if($border && strpos($border,'B')!==false) + $b .= 'B'; + if ($this->unifontSubset) { + $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill); + } + else { + $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); + } + $this->x = $this->lMargin; +} + +function Write($h, $txt, $link='') +{ + // Output text in flowing mode + 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",'',(string)$txt); + if ($this->unifontSubset) { + $nb = mb_strlen($s, 'UTF-8'); + if($nb==1 && $s==" ") { + $this->x += $this->GetStringWidth($s); + return; + } + } + else { + $nb = strlen($s); + } + $sep = -1; + $i = 0; + $j = 0; + $l = 0; + $nl = 1; + while($i<$nb) + { + // Get next character + if ($this->unifontSubset) { + $c = mb_substr($s,$i,1,'UTF-8'); + } + else { + $c = $s[$i]; + } + if($c=="\n") + { + // Explicit line break + if ($this->unifontSubset) { + $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,'',false,$link); + } + $i++; + $sep = -1; + $j = $i; + $l = 0; + if($nl==1) + { + $this->x = $this->lMargin; + $w = $this->w-$this->rMargin-$this->x; + $wmax = ($w-2*$this->cMargin); + } + $nl++; + continue; + } + if($c==' ') + $sep = $i; + + if ($this->unifontSubset) { $l += $this->GetStringWidth($c); } + else { $l += $cw[$c]*$this->FontSize/1000; } + + if($l>$wmax) + { + // Automatic line break + if($sep==-1) + { + if($this->x>$this->lMargin) + { + // Move to next line + $this->x = $this->lMargin; + $this->y += $h; + $w = $this->w-$this->rMargin-$this->x; + $wmax = ($w-2*$this->cMargin); + $i++; + $nl++; + continue; + } + if($i==$j) + $i++; + if ($this->unifontSubset) { + $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,'',false,$link); + } + } + else + { + if ($this->unifontSubset) { + $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,'',false,$link); + } + $i = $sep+1; + } + $sep = -1; + $j = $i; + $l = 0; + if($nl==1) + { + $this->x = $this->lMargin; + $w = $this->w-$this->rMargin-$this->x; + $wmax = ($w-2*$this->cMargin); + } + $nl++; + } + else + $i++; + } + // Last chunk + if($i!=$j) { + if ($this->unifontSubset) { + $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,'',false,$link); + } + } +} + +function Ln($h=null) +{ + // Line feed; default value is the last cell height + $this->x = $this->lMargin; + if($h===null) + $this->y += $this->lasth; + else + $this->y += $h; +} + +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 + if($type=='') + { + $pos = strrpos($file,'.'); + if(!$pos) + $this->Error('Image file has no extension and no type was specified: '.$file); + $type = substr($file,$pos+1); + } + $type = strtolower($type); + if($type=='jpeg') + $type = 'jpg'; + $mtd = '_parse'.$type; + if(!method_exists($this,$mtd)) + $this->Error('Unsupported image type: '.$type); + $info = $this->$mtd($file); + $info['i'] = count($this->images)+1; + $this->images[$file] = $info; + } + else + $info = $this->images[$file]; + + // Automatic width and height calculation if needed + if($w==0 && $h==0) + { + // Put image at 96 dpi + $w = -96; + $h = -96; + } + if($w<0) + $w = -$info['w']*72/$w/$this->k; + if($h<0) + $h = -$info['h']*72/$h/$this->k; + if($w==0) + $w = $h*$info['w']/$info['h']; + if($h==0) + $h = $w*$info['h']/$info['w']; + + // Flowing mode + if($y===null) + { + if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak()) + { + // Automatic page break + $x2 = $this->x; + $this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation); + $this->x = $x2; + } + $y = $this->y; + $this->y += $h; + } + + if($x===null) + $x = $this->x; + $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'])); + if($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 + return $this->x; +} + +function SetX($x) +{ + // Set x position + if($x>=0) + $this->x = $x; + else + $this->x = $this->w+$x; +} + +function GetY() +{ + // Get y position + return $this->y; +} + +function SetY($y, $resetX=true) +{ + // 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->SetX($x); + $this->SetY($y,false); +} + +function Output($dest='', $name='', $isUTF8=false) +{ + // Output PDF to some destination + $this->Close(); + if(strlen($name)==1 && strlen($dest)!=1) + { + // Fix parameter order + $tmp = $dest; + $dest = $name; + $name = $tmp; + } + if($dest=='') + $dest = 'I'; + if($name=='') + $name = 'doc.pdf'; + switch(strtoupper($dest)) + { + case 'I': + // Send to standard output + $this->_checkoutput(); + if(PHP_SAPI!='cli') + { + // We send to a browser + header('Content-Type: application/pdf'); + header('Content-Disposition: inline; '.$this->_httpencode('filename',$name,$isUTF8)); + header('Cache-Control: private, max-age=0, must-revalidate'); + header('Pragma: public'); + } + echo $this->buffer; + break; + case 'D': + // Download file + $this->_checkoutput(); + header('Content-Type: application/x-download'); + 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 + if(!file_put_contents($name,$this->buffer)) + $this->Error('Unable to create output file: '.$name); + break; + case 'S': + // Return as a string + return $this->buffer; + default: + $this->Error('Incorrect output destination: '.$dest); + } + return ''; +} + +/******************************************************************************* +* Protected methods * +*******************************************************************************/ + +protected function _dochecks() +{ + // 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'); +} + +protected function _checkoutput() +{ + if(PHP_SAPI!='cli') + { + if(headers_sent($file,$line)) + $this->Error("Some data has already been output, can't send PDF file (output started at $file:$line)"); + } + if(ob_get_length()) + { + // The output buffer is not empty + 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_clean(); + } + else + $this->Error("Some data has already been output, can't send PDF file"); + } +} + +protected function _getpagesize($size) +{ + if(is_string($size)) + { + $size = strtolower($size); + if(!isset($this->StdPageSizes[$size])) + $this->Error('Unknown page size: '.$size); + $a = $this->StdPageSizes[$size]; + return array($a[0]/$this->k, $a[1]/$this->k); + } + else + { + if($size[0]>$size[1]) + return array($size[1], $size[0]); + else + return $size; + } +} + +protected function _beginpage($orientation, $size, $rotation) +{ + $this->page++; + $this->pages[$this->page] = ''; + $this->state = 2; + $this->x = $this->lMargin; + $this->y = $this->tMargin; + $this->FontFamily = ''; + // Check page size and orientation + if($orientation=='') + $orientation = $this->DefOrientation; + else + $orientation = strtoupper($orientation[0]); + if($size=='') + $size = $this->DefPageSize; + else + $size = $this->_getpagesize($size); + if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1]) + { + // New size or orientation + if($orientation=='P') + { + $this->w = $size[0]; + $this->h = $size[1]; + } + else + { + $this->w = $size[1]; + $this->h = $size[0]; + } + $this->wPt = $this->w*$this->k; + $this->hPt = $this->h*$this->k; + $this->PageBreakTrigger = $this->h-$this->bMargin; + $this->CurOrientation = $orientation; + $this->CurPageSize = $size; + } + if($orientation!=$this->DefOrientation || $size[0]!=$this->DefPageSize[0] || $size[1]!=$this->DefPageSize[1]) + $this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt); + if($rotation!=0) + { + if($rotation%90!=0) + $this->Error('Incorrect rotation value: '.$rotation); + $this->CurRotation = $rotation; + $this->PageInfo[$this->page]['rotation'] = $rotation; + } +} + +protected function _endpage() +{ + $this->state = 1; +} + +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); + if(!isset($name)) + $this->Error('Could not include font definition file'); + if(isset($enc)) + $enc = strtolower($enc); + if(!isset($subsetted)) + $subsetted = false; + return get_defined_vars(); +} + +protected function _isascii($s) +{ + // Test if string is ASCII + $nb = strlen($s); + for($i=0;$i<$nb;$i++) + { + if(ord($s[$i])>127) + return false; + } + return true; +} + +protected function _httpencode($param, $value, $isUTF8) +{ + // Encode HTTP header field parameter + if($this->_isascii($value)) + return $param.'="'.$value.'"'; + if(!$isUTF8) + $value = utf8_encode($value); + if(strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')!==false) + return $param.'="'.rawurlencode($value).'"'; + else + return $param."*=UTF-8''".rawurlencode($value); +} + +protected 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; +} + +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).')'; +} + +protected function _dounderline($x, $y, $txt) +{ + // Underline text + $up = $this->CurrentFont['up']; + $ut = $this->CurrentFont['ut']; + $w = $this->GetStringWidth($txt)+$this->ws*substr_count($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); +} + +protected function _parsejpg($file) +{ + // Extract info from a JPEG file + $a = getimagesize($file); + if(!$a) + $this->Error('Missing or incorrect image file: '.$file); + if($a[2]!=2) + $this->Error('Not a JPEG file: '.$file); + if(!isset($a['channels']) || $a['channels']==3) + $colspace = 'DeviceRGB'; + elseif($a['channels']==4) + $colspace = 'DeviceCMYK'; + else + $colspace = 'DeviceGray'; + $bpc = isset($a['bits']) ? $a['bits'] : 8; + $data = file_get_contents($file); + return array('w'=>$a[0], 'h'=>$a[1], 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'DCTDecode', 'data'=>$data); +} + +protected function _parsepng($file) +{ + // Extract info from a PNG file + $f = fopen($file,'rb'); + if(!$f) + $this->Error('Can\'t open image file: '.$file); + $info = $this->_parsepngstream($f,$file); + fclose($f); + return $info; +} + +protected function _parsepngstream($f, $file) +{ + // Check signature + if($this->_readstream($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) + $this->Error('Not a PNG file: '.$file); + + // Read header chunk + $this->_readstream($f,4); + if($this->_readstream($f,4)!='IHDR') + $this->Error('Incorrect PNG file: '.$file); + $w = $this->_readint($f); + $h = $this->_readint($f); + $bpc = ord($this->_readstream($f,1)); + if($bpc>8) + $this->Error('16-bit depth not supported: '.$file); + $ct = ord($this->_readstream($f,1)); + if($ct==0 || $ct==4) + $colspace = 'DeviceGray'; + elseif($ct==2 || $ct==6) + $colspace = 'DeviceRGB'; + elseif($ct==3) + $colspace = 'Indexed'; + else + $this->Error('Unknown color type: '.$file); + if(ord($this->_readstream($f,1))!=0) + $this->Error('Unknown compression method: '.$file); + if(ord($this->_readstream($f,1))!=0) + $this->Error('Unknown filter method: '.$file); + if(ord($this->_readstream($f,1))!=0) + $this->Error('Interlacing not supported: '.$file); + $this->_readstream($f,4); + $dp = '/Predictor 15 /Colors '.($colspace=='DeviceRGB' ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w; + + // Scan chunks looking for palette, transparency and image data + $pal = ''; + $trns = ''; + $data = ''; + do + { + $n = $this->_readint($f); + $type = $this->_readstream($f,4); + if($type=='PLTE') + { + // Read palette + $pal = $this->_readstream($f,$n); + $this->_readstream($f,4); + } + elseif($type=='tRNS') + { + // Read transparency info + $t = $this->_readstream($f,$n); + if($ct==0) + $trns = array(ord(substr($t,1,1))); + elseif($ct==2) + $trns = array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1))); + else + { + $pos = strpos($t,chr(0)); + if($pos!==false) + $trns = array($pos); + } + $this->_readstream($f,4); + } + elseif($type=='IDAT') + { + // Read image data block + $data .= $this->_readstream($f,$n); + $this->_readstream($f,4); + } + elseif($type=='IEND') + break; + else + $this->_readstream($f,$n+4); + } + while($n); + + if($colspace=='Indexed' && empty($pal)) + $this->Error('Missing palette in '.$file); + $info = array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'dp'=>$dp, 'pal'=>$pal, 'trns'=>$trns); + if($ct>=4) + { + // Extract alpha channel + if(!function_exists('gzuncompress')) + $this->Error('Zlib not available, can\'t handle alpha channel: '.$file); + $data = gzuncompress($data); + $color = ''; + $alpha = ''; + if($ct==4) + { + // Gray image + $len = 2*$w; + for($i=0;$i<$h;$i++) + { + $pos = (1+$len)*$i; + $color .= $data[$pos]; + $alpha .= $data[$pos]; + $line = substr($data,$pos+1,$len); + $color .= preg_replace('/(.)./s','$1',$line); + $alpha .= preg_replace('/.(.)/s','$1',$line); + } + } + else + { + // RGB image + $len = 4*$w; + for($i=0;$i<$h;$i++) + { + $pos = (1+$len)*$i; + $color .= $data[$pos]; + $alpha .= $data[$pos]; + $line = substr($data,$pos+1,$len); + $color .= preg_replace('/(.{3})./s','$1',$line); + $alpha .= preg_replace('/.{3}(.)/s','$1',$line); + } + } + unset($data); + $data = gzcompress($color); + $info['smask'] = gzcompress($alpha); + $this->WithAlpha = true; + if($this->PDFVersion<'1.4') + $this->PDFVersion = '1.4'; + } + $info['data'] = $data; + return $info; +} + +protected function _readstream($f, $n) +{ + // Read n bytes from stream + $res = ''; + while($n>0 && !feof($f)) + { + $s = fread($f,$n); + if($s===false) + $this->Error('Error while reading stream'); + $n -= strlen($s); + $res .= $s; + } + if($n>0) + $this->Error('Unexpected end of stream'); + return $res; +} + +protected function _readint($f) +{ + // Read a 4-byte integer from stream + $a = unpack('Ni',$this->_readstream($f,4)); + return $a['i']; +} + +protected function _parsegif($file) +{ + // Extract info from a GIF file (via PNG conversion) + if(!function_exists('imagepng')) + $this->Error('GD extension is required for GIF support'); + if(!function_exists('imagecreatefromgif')) + $this->Error('GD has no GIF read support'); + $im = imagecreatefromgif($file); + if(!$im) + $this->Error('Missing or incorrect image file: '.$file); + imageinterlace($im,0); + 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; +} + +protected function _out($s) +{ + // Add a line to the document + if($this->state==2) + $this->pages[$this->page] .= $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'); +} + +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 _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(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]]; + 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; + $annots .= sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>',$this->PageInfo[$l[0]]['n'],$h-$l[1]*$this->k); + } + } + $this->_put($annots.']'); + } + 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]); +} + +protected function _putpages() +{ + $nb = $this->page; + for($n=1;$n<=$nb;$n++) + $this->PageInfo[$n]['n'] = $this->n+1+2*($n-1); + for($n=1;$n<=$nb;$n++) + $this->_putpage($n); + // Pages root + $this->_newobj(1); + $this->_put('<PageInfo[$n]['n'].' 0 R '; + $this->_put($kids.']'); + $this->_put('/Count '.$nb); + if($this->DefOrientation=='P') + { + $w = $this->DefPageSize[0]; + $h = $this->DefPageSize[1]; + } + else + { + $w = $this->DefPageSize[1]; + $h = $this->DefPageSize[0]; + } + $this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$w*$this->k,$h*$this->k)); + $this->_put('>>'); + $this->_put('endobj'); +} + +protected function _putfonts() +{ + 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 = 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'); + } + } + foreach($this->fonts as $k=>$font) + { + // 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') + { + // Core font + $this->fonts[$k]['n'] = $this->n+1; + $this->_newobj(); + $this->_put('<_put('/BaseFont /'.$name); + $this->_put('/Subtype /Type1'); + if($name!='Symbol' && $name!='ZapfDingbats') + $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/OpenType font + if(isset($font['subsetted']) && $font['subsetted']) + $name = 'AAAAAA+'.$name; + $this->fonts[$k]['n'] = $this->n+1; + $this->_newobj(); + $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->_put('/Encoding '.$this->encodings[$font['enc']].' 0 R'); + else + $this->_put('/Encoding /WinAnsiEncoding'); + } + + if(isset($font['uv'])) + $this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R'); + $this->_put('>>'); + $this->_put('endobj'); + // Widths + $this->_newobj(); + $cw = &$font['cw']; + $s = '['; + for($i=32;$i<=255;$i++) + $s .= $cw[chr($i)].' '; + $this->_put($s.']'); + $this->_put('endobj'); + // Descriptor + $this->_newobj(); + $s = '<$v) + $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->fontpath.'unifont/ttfonts.php'); + $ttf = new TTFontFile(); + $fontname = 'MPDFAA'.'+'.$font['name']; + $subset = $font['subset']; + unset($subset[0]); + $ttfontstream = $ttf->makeSubset($font['ttffile'], $subset); + $ttfontsize = strlen($ttfontstream); + $fontstream = gzcompress($ttfontstream); + $codeToGlyph = $ttf->codeToGlyph; + unset($codeToGlyph[0]); + + // Type0 Font + // A composite font - a font composed of other fonts, organized hierarchically + $this->_newobj(); + $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->_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->_put('/CIDToGIDMap '.($this->n + 4).' 0 R'); + $this->_put('>>'); + $this->_put('endobj'); + + // ToUnicode + $this->_newobj(); + $toUni = "/CIDInit /ProcSet findresource begin\n"; + $toUni .= "12 dict begin\n"; + $toUni .= "begincmap\n"; + $toUni .= "/CIDSystemInfo\n"; + $toUni .= "<_put('<>'); + $this->_putstream($toUni); + $this->_put('endobj'); + + // CIDSystemInfo dictionary + $this->_newobj(); + $this->_put('<_put('/Ordering (UCS)'); + $this->_put('/Supplement 0'); + $this->_put('>>'); + $this->_put('endobj'); + + // Font descriptor + $this->_newobj(); + $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->_put('/FontFile2 '.($this->n + 2).' 0 R'); + $this->_put('>>'); + $this->_put('endobj'); + + // Embed CIDToGIDMap + // A specification of the mapping from CIDs to glyph indices + $cidtogidmap = ''; + $cidtogidmap = str_pad('', 256*256*2, "\x00"); + foreach($codeToGlyph as $cc=>$glyph) { + $cidtogidmap[$cc*2] = chr($glyph >> 8); + $cidtogidmap[$cc*2 + 1] = chr($glyph & 0xFF); + } + $cidtogidmap = gzcompress($cidtogidmap); + $this->_newobj(); + $this->_put('<_put('/Filter /FlateDecode'); + $this->_put('>>'); + $this->_putstream($cidtogidmap); + $this->_put('endobj'); + + //Font file + $this->_newobj(); + $this->_put('<_put('/Filter /FlateDecode'); + $this->_put('/Length1 '.$ttfontsize); + $this->_put('>>'); + $this->_putstream($fontstream); + $this->_put('endobj'); + unset($ttf); + } + else + { + // Allow for additional types + $this->fonts[$k]['n'] = $this->n+1; + $mtd = '_put'.strtolower($type); + if(!method_exists($this,$mtd)) + $this->Error('Unsupported font type: '.$type); + $this->$mtd($font); + } + } +} + +protected function _putTTfontwidths(&$font, $maxUni) { + if (file_exists($font['unifilename'].'.cw127.php')) { + include($font['unifilename'].'.cw127.php') ; + $startcid = 128; + } + else { + $rangeid = 0; + $range = array(); + $prevcid = -2; + $prevwidth = -1; + $interval = false; + $startcid = 1; + } + $cwlen = $maxUni + 1; + + // for each character + for ($cid=$startcid; $cid<$cwlen; $cid++) { + if ($cid==128 && (!file_exists($font['unifilename'].'.cw127.php'))) { + if (is_writable(dirname($this->fontpath.'unifont/x'))) { + $fh = fopen($font['unifilename'].'.cw127.php',"wb"); + $cw127='"; + fwrite($fh,$cw127,strlen($cw127)); + fclose($fh); + } + } + if ((!isset($font['cw'][$cid*2]) || !isset($font['cw'][$cid*2+1])) || + ($font['cw'][$cid*2] == "\00" && $font['cw'][$cid*2+1] == "\00")) { continue; } + + $width = (ord($font['cw'][$cid*2]) << 8) + ord($font['cw'][$cid*2+1]); + if ($width == 65535) { $width = 0; } + if ($cid > 255 && (!isset($font['subset'][$cid]) || !$font['subset'][$cid])) { continue; } + if (!isset($font['dw']) || (isset($font['dw']) && $width != $font['dw'])) { + if ($cid == ($prevcid + 1)) { + if ($width == $prevwidth) { + if ($width == $range[$rangeid][0]) { + $range[$rangeid][] = $width; + } + else { + array_pop($range[$rangeid]); + // new range + $rangeid = $prevcid; + $range[$rangeid] = array(); + $range[$rangeid][] = $prevwidth; + $range[$rangeid][] = $width; + } + $interval = true; + $range[$rangeid]['interval'] = true; + } else { + if ($interval) { + // new range + $rangeid = $cid; + $range[$rangeid] = array(); + $range[$rangeid][] = $width; + } + else { $range[$rangeid][] = $width; } + $interval = false; + } + } else { + $rangeid = $cid; + $range[$rangeid] = array(); + $range[$rangeid][] = $width; + $interval = false; + } + $prevcid = $cid; + $prevwidth = $width; + } + } + $prevk = -1; + $nextk = -1; + $prevint = false; + foreach ($range as $k => $ws) { + $cws = count($ws); + if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) { + if (isset($range[$k]['interval'])) { unset($range[$k]['interval']); } + $range[$prevk] = array_merge($range[$prevk], $range[$k]); + unset($range[$k]); + } + else { $prevk = $k; } + $nextk = $k + $cws; + if (isset($ws['interval'])) { + if ($cws > 3) { $prevint = true; } + else { $prevint = false; } + unset($range[$k]['interval']); + --$nextk; + } + else { $prevint = false; } + } + $w = ''; + foreach ($range as $k => $ws) { + if (count(array_count_values($ws)) == 1) { $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0]; } + else { $w .= ' '.$k.' [ '.implode(' ', $ws).' ]' . "\n"; } + } + $this->_out('/W ['.$w.' ]'); +} + +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) + { + $this->_putimage($this->images[$file]); + unset($this->images[$file]['data']); + unset($this->images[$file]['smask']); + } +} + +protected function _putimage(&$info) +{ + $this->_newobj(); + $info['n'] = $this->n; + $this->_put('<_put('/Subtype /Image'); + $this->_put('/Width '.$info['w']); + $this->_put('/Height '.$info['h']); + if($info['cs']=='Indexed') + $this->_put('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]'); + else + { + $this->_put('/ColorSpace /'.$info['cs']); + if($info['cs']=='DeviceCMYK') + $this->_put('/Decode [1 0 1 0 1 0 1 0]'); + } + $this->_put('/BitsPerComponent '.$info['bpc']); + if(isset($info['f'])) + $this->_put('/Filter /'.$info['f']); + if(isset($info['dp'])) + $this->_put('/DecodeParms <<'.$info['dp'].'>>'); + if(isset($info['trns']) && is_array($info['trns'])) + { + $trns = ''; + for($i=0;$i_put('/Mask ['.$trns.']'); + } + if(isset($info['smask'])) + $this->_put('/SMask '.($this->n+1).' 0 R'); + $this->_put('/Length '.strlen($info['data']).'>>'); + $this->_putstream($info['data']); + $this->_put('endobj'); + // Soft mask + if(isset($info['smask'])) + { + $dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns '.$info['w']; + $smask = array('w'=>$info['w'], 'h'=>$info['h'], 'cs'=>'DeviceGray', 'bpc'=>8, 'f'=>$info['f'], 'dp'=>$dp, 'data'=>$info['smask']); + $this->_putimage($smask); + } + // Palette + if($info['cs']=='Indexed') + $this->_putstreamobject($info['pal']); +} + +protected function _putxobjectdict() +{ + foreach($this->images as $image) + $this->_put('/I'.$image['i'].' '.$image['n'].' 0 R'); +} + +protected function _putresourcedict() +{ + $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->_put('>>'); +} + +protected function _putresources() +{ + $this->_putfonts(); + $this->_putimages(); + // Resource dictionary + $this->_newobj(2); + $this->_put('<<'); + $this->_putresourcedict(); + $this->_put('>>'); + $this->_put('endobj'); +} + +protected function _putinfo() +{ + $this->metadata['Producer'] = 'tFPDF '.tFPDF_VERSION; + $this->metadata['CreationDate'] = 'D:'.@date('YmdHis'); + foreach($this->metadata as $key=>$value) + $this->_put('/'.$key.' '.$this->_textstring($value)); +} + +protected function _putcatalog() +{ + $n = $this->PageInfo[1]['n']; + $this->_put('/Type /Catalog'); + $this->_put('/Pages 1 0 R'); + if($this->ZoomMode=='fullpage') + $this->_put('/OpenAction ['.$n.' 0 R /Fit]'); + elseif($this->ZoomMode=='fullwidth') + $this->_put('/OpenAction ['.$n.' 0 R /FitH null]'); + elseif($this->ZoomMode=='real') + $this->_put('/OpenAction ['.$n.' 0 R /XYZ null null 1]'); + elseif(!is_string($this->ZoomMode)) + $this->_put('/OpenAction ['.$n.' 0 R /XYZ null null '.sprintf('%.2F',$this->ZoomMode/100).']'); + if($this->LayoutMode=='single') + $this->_put('/PageLayout /SinglePage'); + elseif($this->LayoutMode=='continuous') + $this->_put('/PageLayout /OneColumn'); + elseif($this->LayoutMode=='two') + $this->_put('/PageLayout /TwoColumnLeft'); +} + +protected function _putheader() +{ + $this->_put('%PDF-'.$this->PDFVersion); +} + +protected function _puttrailer() +{ + $this->_put('/Size '.($this->n+1)); + $this->_put('/Root '.$this->n.' 0 R'); + $this->_put('/Info '.($this->n-1).' 0 R'); +} + +protected function _enddoc() +{ + $this->_putheader(); + $this->_putpages(); + $this->_putresources(); + // Info + $this->_newobj(); + $this->_put('<<'); + $this->_putinfo(); + $this->_put('>>'); + $this->_put('endobj'); + // Catalog + $this->_newobj(); + $this->_put('<<'); + $this->_putcatalog(); + $this->_put('>>'); + $this->_put('endobj'); + // Cross-ref + $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->_put(sprintf('%010d 00000 n ',$this->offsets[$i])); + // Trailer + $this->_put('trailer'); + $this->_put('<<'); + $this->_puttrailer(); + $this->_put('>>'); + $this->_put('startxref'); + $this->_put($offset); + $this->_put('%%EOF'); + $this->state = 3; +} + +// ********* NEW FUNCTIONS ********* +// Converts UTF-8 strings to UTF16-BE. +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'); + return $outstr; +} + +// Converts UTF-8 strings to codepoints array +protected function UTF8StringToArray($str) { + $out = array(); + $len = strlen($str); + for ($i = 0; $i < $len; $i++) { + $uni = -1; + $h = ord($str[$i]); + if ( $h <= 0x7F ) + $uni = $h; + elseif ( $h >= 0xC2 ) { + if ( ($h <= 0xDF) && ($i < $len -1) ) + $uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F); + elseif ( ($h <= 0xEF) && ($i < $len -2) ) + $uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6 + | (ord($str[++$i]) & 0x3F); + elseif ( ($h <= 0xF4) && ($i < $len -3) ) + $uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12 + | (ord($str[++$i]) & 0x3F) << 6 + | (ord($str[++$i]) & 0x3F); + } + if ($uni >= 0) { + $out[] = $uni; + } + } + return $out; +} + + + +} +?>