Ignore:
Timestamp:
11/17/09 09:02:41 (15 years ago)
Author:
amuller
Message:

Ticket #597 - melhoria no modulo gerenciador de arquivos

File:
1 edited

Legend:

Unmodified
Added
Removed
  • sandbox/filemanager/tp/dompdf/lib/class.pdf.php

    r1575 r1654  
    1111   * simplify the creation of documents. 
    1212   * 
     13   * Extended by Orion Richardson to support Unicode / UTF-8 characters using 
     14   * TCPDF and others as a guide. 
     15   * 
    1316   * IMPORTANT NOTE 
    1417   * there is no warranty, implied or otherwise with this software. 
     
    1821   * 
    1922   * @author       Wayne Munro <pdf@ros.co.nz> 
     23   * @contributor  Orion Richardson <orionr@yahoo.com> 
     24   * @contributor  Helmut Tischer <htischer@weihenstephan.org> 
    2025   * @version  009 
    2126   * @package  Cpdf 
     27   * 
     28   * Changes 
     29   * @contributor Helmut Tischer <htischer@weihenstephan.org> 
     30   * @version 0.5.1.htischer.20090507 
     31   * - On multiple identical png and jpg images, put only one copy into the pdf file and refer to it. 
     32   *   This reduces file size and rendering time. 
     33   * - Allow font metrics cache to be a different folder as the font metrics. This allows a read only installation. 
     34   * - Allow adding images directly from a gd object. This increases performance by avoiding temporary files. 
     35   * - On png image files remove alpa channel to allow display of typical png files in pdf. 
     36   * - On addImage avoid temporary file. Todo: Duplicate Image (currently not used) 
     37   * - Add a check function, whether image is already cached, This avoids double creation by caller which saves 
     38   *   CPU time and memory. 
     39   * @contributor Helmut Tischer <htischer@weihenstephan.org> 
     40   * @version dompdf_trunk_with_helmut_mods.20090524 
     41   * - Allow temp and fontcache folders to be passed in by class creator 
     42   * @version dompdf_trunk_with_helmut_mods.20090528 
     43   * - typo 'decent' instead of 'descent' at various locations made getFontDescender worthless 
    2244   */ 
    2345class  Cpdf { 
     
    187209   */ 
    188210  public  $fontFamilies =  array(); 
     211  
     212  /** 
     213   * folder for php serialized formats of font metrics files. 
     214   * If empty string, use same folder as original metrics files. 
     215   * This can be passed in from class creator. 
     216   * If this folder does not exist or is not writable, Cpdf will be **much** slower. 
     217   * Because of potential trouble with php safe mode, folder cannot be created at runtime. 
     218   */ 
     219  public  $fontcache = ''; 
     220   
     221  /** 
     222   * temporary folder. 
     223   * If empty string, will attempty system tmp folder. 
     224   * This can be passed in from class creator. 
     225   * Only used for conversion of gd images to jpeg images. 
     226   */ 
     227  public  $tmp = ''; 
    189228 
    190229  /** 
     
    246285  public  $checkpoint =  ''; 
    247286 
     287  /* Table of Image origin filenames and image labels which were already added with o_image(). 
     288   * Allows to merge identical images 
     289   */ 
     290  public  $imagelist = array(); 
     291 
     292  /** 
     293   * whether the text passed in should be treated as Unicode or just local character set. 
     294   */ 
     295  public  $isUnicode = false; 
     296 
    248297  /** 
    249298   * class constructor 
    250299   * this will start a new document 
    251300   * @var array array of 4 numbers, defining the bottom left and upper right corner of the page. first two are normally zero. 
    252    */ 
    253   function  Cpdf ($pageSize = array(0, 0, 612, 792)) { 
     301   * @var boolean whether text will be treated as Unicode or not. 
     302   */ 
     303  function  Cpdf ($pageSize = array(0, 0, 612, 792), $isUnicode = false, $fontcache = '', $tmp = '') { 
     304 
     305    $this->isUnicode = $isUnicode; 
     306 
     307    $this->fontcache = $fontcache; 
     308 
     309    $this->tmp = $tmp; 
    254310 
    255311    $this->newDocument($pageSize); 
     
    333389      $tmp =  $o['info']; 
    334390 
    335       $res = "\n".$id." 0 obj\n".'['.$tmp['page'].' 0 R /'.$tmp['string']."]\nendobj\n"; 
     391      $res = "\n".$id." 0 obj\n".'['.$tmp['page'].' 0 R /'.$tmp['string']."]\nendobj"; 
    336392 
    337393      return  $res; 
     
    638694            } 
    639695 
    640             $res.= " >>"; 
     696            $res.= "\n>>"; 
    641697          } 
    642698 
     
    650706            } 
    651707 
    652             $res.= " >>"; 
     708            $res.= "\n>>"; 
    653709          } 
    654710 
     
    662718            } 
    663719 
    664             $res.=  " >>"; 
     720            $res.=  "\n>>"; 
    665721          } 
    666722 
     
    672728            $tmp = $o['info']['mediaBox']; 
    673729 
    674             $res.= "\n/MediaBox [".sprintf('%.3f', $tmp[0]) .' '.sprintf('%.3f', $tmp[1]) .' '.sprintf('%.3f', $tmp[2]) .' '.sprintf('%.3f', $tmp[3]) .']'; 
     730            $res.= "\n/MediaBox [".sprintf('%.3F', $tmp[0]) .' '.sprintf('%.3F', $tmp[1]) .' '.sprintf('%.3F', $tmp[2]) .' '.sprintf('%.3F', $tmp[3]) .']'; 
    675731          } 
    676732        } 
     
    753809    case  'new': 
    754810 
    755       $this->objects[$id] =  array('t' => 'font', 'info' => array('name' => $options['name'], 'SubType' => 'Type1')); 
     811      $this->objects[$id] =  array('t' => 'font', 'info' => array('name' => $options['name'], 'fontFileName' => $options['fontFileName'], 'SubType' => 'Type1')); 
    756812 
    757813      $fontNum =  $this->numFonts; 
     
    798854      } 
    799855 
     856      if ($this->isUnicode) { 
     857 
     858        // For Unicode fonts, we need to incorporate font data into 
     859        // sub-sections that are linked from the primary font section. 
     860        // Look at o_fontGIDtoCID and o_fontDescendentCID functions 
     861        // for more informaiton. 
     862        // 
     863        // All of this code is adapted from the excellent changes made to 
     864        // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/) 
     865 
     866        $toUnicodeId = ++$this->numObj; 
     867        $this->o_contents($toUnicodeId, 'new', 'raw'); 
     868        $this->objects[$id]['info']['toUnicode'] = $toUnicodeId; 
     869         
     870        $stream =  "/CIDInit /ProcSet findresource begin\n"; 
     871        $stream.=  "12 dict begin\n"; 
     872        $stream.=  "begincmap\n"; 
     873        $stream.=  "/CIDSystemInfo\n"; 
     874        $stream.=  "<</Registry (Adobe)\n"; 
     875        $stream.=  "/Ordering (UCS)\n"; 
     876        $stream.=  "/Supplement 0\n"; 
     877        $stream.=  ">> def\n"; 
     878        $stream.=  "/CMapName /Adobe-Identity-UCS def\n"; 
     879        $stream.=  "/CMapType 2 def\n"; 
     880        $stream.=  "1 begincodespacerange\n"; 
     881        $stream.=  "<0000> <FFFF>\n"; 
     882        $stream.=  "endcodespacerange\n"; 
     883        $stream.=  "1 beginbfrange\n"; 
     884        $stream.=  "<0000> <FFFF> <0000>\n"; 
     885        $stream.=  "endbfrange\n"; 
     886        $stream.=  "endcmap\n"; 
     887        $stream.=  "CMapName currentdict /CMap defineresource pop\n"; 
     888        $stream.=  "end\n"; 
     889        $stream.=  "end\n"; 
     890 
     891        $res =   "<</Length " . mb_strlen($stream) . " >>\n"; 
     892        $res .=  "stream\n" . $stream . "endstream"; 
     893 
     894        $this->objects[$toUnicodeId]['c'] = $res; 
     895 
     896        $cidFontId = ++$this->numObj; 
     897        $this->o_fontDescendentCID($cidFontId, 'new', $options); 
     898        $this->objects[$id]['info']['cidFont'] = $cidFontId; 
     899      } 
     900       
    800901      // also tell the pages node about the new font 
    801902      $this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id)); 
     
    834935      } 
    835936 
     937      // pass values down to descendent font 
     938      if (isset($o['info']['cidFont'])) { 
     939 
     940        $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options); 
     941      } 
     942         
    836943      break; 
    837944 
     
    839946    case  'out': 
    840947 
     948      if ($this->isUnicode) { 
     949 
     950        // For Unicode fonts, we need to incorporate font data into 
     951        // sub-sections that are linked from the primary font section. 
     952        // Look at o_fontGIDtoCID and o_fontDescendentCID functions 
     953        // for more informaiton. 
     954        // 
     955        // All of this code is adapted from the excellent changes made to 
     956        // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/) 
     957 
     958        $res =  "\n".$id." 0 obj\n<</Type /Font\n/Subtype /Type0\n"; 
     959        $res.=  "/BaseFont /".$o['info']['name']."\n"; 
     960 
     961        // The horizontal identity mapping for 2-byte CIDs; may be used 
     962        // with CIDFonts using any Registry, Ordering, and Supplement values. 
     963        $res.=  "/Encoding /Identity-H\n"; 
     964        $res.=  "/DescendantFonts [".$o['info']['cidFont']." 0 R]\n"; 
     965        $res.=  "/ToUnicode ".$o['info']['toUnicode']." 0 R\n"; 
     966        $res.=  ">>\n"; 
     967        $res.=  "endobj"; 
     968 
     969      } else { 
    841970      $res =  "\n".$id." 0 obj\n<< /Type /Font\n/Subtype /".$o['info']['SubType']."\n"; 
    842971 
     
    8751004      } 
    8761005 
    877       $res.=  ">>\nendobj"; 
     1006        $res.=  ">>\n"; 
     1007        $res.=  "endobj"; 
     1008         
     1009      } 
    8781010 
    8791011      return  $res; 
     
    9361068        case  'CharSet': 
    9371069 
    938           if  (strlen($value)) { 
     1070          if  (mb_strlen($value)) { 
    9391071 
    9401072            $res.= '/'.$label.' '.$value."\n"; 
     
    10371169 
    10381170  /** 
     1171   * a descendent cid font,  needed for unicode fonts 
     1172   */ 
     1173  function  o_fontDescendentCID($id, $action, $options = '') { 
     1174 
     1175    if  ($action != 'new') { 
     1176 
     1177      $o = & $this->objects[$id]; 
     1178    } 
     1179 
     1180    switch  ($action) { 
     1181 
     1182    case  'new': 
     1183 
     1184      $this->objects[$id] =  array('t'=>'fontDescendentCID', 'info'=>$options); 
     1185 
     1186      // we need a CID system info section 
     1187      $cidSystemInfoId = ++$this->numObj; 
     1188      $this->o_contents($cidSystemInfoId, 'new', 'raw'); 
     1189      $this->objects[$id]['info']['cidSystemInfo'] = $cidSystemInfoId; 
     1190      $res=   "<</Registry (Adobe)\n"; // A string identifying an issuer of character collections 
     1191      $res.=  "/Ordering (UCS)\n"; // A string that uniquely names a character collection issued by a specific registry 
     1192      $res.=  "/Supplement 0\n"; // The supplement number of the character collection. 
     1193      $res.=  ">>"; 
     1194      $this->objects[$cidSystemInfoId]['c'] = $res; 
     1195 
     1196      // and a CID to GID map 
     1197      $cidToGidMapId = ++$this->numObj; 
     1198      $this->o_fontGIDtoCIDMap($cidToGidMapId, 'new', $options); 
     1199      $this->objects[$id]['info']['cidToGidMap'] = $cidToGidMapId; 
     1200       
     1201      break; 
     1202 
     1203    case  'add': 
     1204 
     1205      foreach ($options as  $k => $v) { 
     1206        switch  ($k) { 
     1207        case  'BaseFont': 
     1208          $o['info']['name'] =  $v; 
     1209          break; 
     1210 
     1211        case  'FirstChar': 
     1212        case  'LastChar': 
     1213        case  'MissingWidth': 
     1214        case  'FontDescriptor': 
     1215        case  'SubType': 
     1216          $this->addMessage('o_fontDescendentCID '.$k." : ".$v); 
     1217          $o['info'][$k] =  $v; 
     1218          break; 
     1219        } 
     1220      } 
     1221 
     1222      // pass values down to cid to gid map 
     1223      $this->o_fontGIDtoCIDMap($o['info']['cidToGidMap'], 'add', $options); 
     1224       
     1225      break; 
     1226 
     1227    case  'out': 
     1228 
     1229      $res =  "\n".$id." 0 obj\n"; 
     1230      $res.=  "<</Type /Font\n"; 
     1231      $res.=  "/Subtype /CIDFontType2\n"; 
     1232      $res.=  "/BaseFont /".$o['info']['name']."\n"; 
     1233      $res.=  "/CIDSystemInfo ".$o['info']['cidSystemInfo']." 0 R\n"; 
     1234//      if  (isset($o['info']['FirstChar'])) { 
     1235// 
     1236//        $res.=  "/FirstChar ".$o['info']['FirstChar']."\n"; 
     1237//      } 
     1238 
     1239//      if  (isset($o['info']['LastChar'])) { 
     1240// 
     1241//        $res.=  "/LastChar ".$o['info']['LastChar']."\n"; 
     1242//      } 
     1243      if  (isset($o['info']['FontDescriptor'])) { 
     1244 
     1245        $res.=  "/FontDescriptor ".$o['info']['FontDescriptor']." 0 R\n"; 
     1246      } 
     1247 
     1248      if  (isset($o['info']['MissingWidth'])) { 
     1249        $res.=  "/DW ".$o['info']['MissingWidth']."\n"; 
     1250      } 
     1251 
     1252      if  (isset($o['info']['fontFileName']) && isset($this->fonts[$o['info']['fontFileName']]['CIDWidths'])) { 
     1253        $cid_widths = &$this->fonts[$o['info']['fontFileName']]['CIDWidths']; 
     1254        $w = ''; 
     1255        foreach ($cid_widths as $cid => $width) { 
     1256          $w .= $cid.' ['.$width.'] '; 
     1257        } 
     1258        $res.=  "/W [".$w."]\n"; 
     1259      } 
     1260 
     1261      $res.=  "/CIDToGIDMap ".$o['info']['cidToGidMap']." 0 R\n"; 
     1262      $res.=  ">>\n"; 
     1263      $res.=  "endobj"; 
     1264 
     1265      return  $res; 
     1266 
     1267      break; 
     1268    } 
     1269  } 
     1270   
     1271 
     1272  /** 
     1273   * a font glyph to character map,  needed for unicode fonts 
     1274   */ 
     1275  function  o_fontGIDtoCIDMap($id, $action, $options = '') { 
     1276 
     1277    if  ($action != 'new') { 
     1278 
     1279      $o = & $this->objects[$id]; 
     1280    } 
     1281 
     1282    switch  ($action) { 
     1283 
     1284    case  'new': 
     1285 
     1286      $this->objects[$id] =  array('t'=>'fontGIDtoCIDMap', 'info'=>$options); 
     1287 
     1288      break; 
     1289 
     1290    case  'out': 
     1291        
     1292      $res = "\n".$id." 0 obj\n"; 
     1293      $tmp = $this->fonts[$o['info']['fontFileName']]['CIDtoGID'] = base64_decode($this->fonts[$o['info']['fontFileName']]['CIDtoGID']); 
     1294      $compressed = isset($this->fonts[$o['info']['fontFileName']]['CIDtoGID_Compressed']) && 
     1295                    $this->fonts[$o['info']['fontFileName']]['CIDtoGID_Compressed']; 
     1296 
     1297      if  (!$compressed && isset($o['raw'])) { 
     1298 
     1299        $res.= $tmp; 
     1300      } else { 
     1301 
     1302        $res.=  "<<"; 
     1303 
     1304        if  (!$compressed && function_exists('gzcompress') &&  $this->options['compression']) { 
     1305 
     1306          // then implement ZLIB based compression on this content stream 
     1307          $compressed = true; 
     1308 
     1309          $tmp =  gzcompress($tmp,  6); 
     1310        } 
     1311        if ($compressed) { 
     1312 
     1313          $res.= "\n/Filter /FlateDecode"; 
     1314        } 
     1315 
     1316        $res.= "\n/Length ".mb_strlen($tmp) .">>\nstream\n".$tmp."\nendstream"; 
     1317      } 
     1318 
     1319      $res.= "\nendobj"; 
     1320 
     1321      return  $res; 
     1322 
     1323      break; 
     1324    } 
     1325  } 
     1326   
     1327 
     1328  /** 
    10391329   * the document procset, solves some problems with printing to old PS printers 
    10401330   */ 
     
    11521442        $res.= '/'.$k.' ('; 
    11531443 
     1444        // dates must be outputted as-is, without Unicode transformations 
     1445        $raw = ($k == 'CreationDate' || $k == 'ModDate'); 
     1446        $c = $v; 
     1447 
    11541448        if  ($this->encrypted) { 
    11551449 
    1156           $res.= $this->filterText($this->ARC4($v)); 
    1157         } else { 
    1158  
    1159           $res.= $this->filterText($v); 
     1450          $c = $this->ARC4($c); 
    11601451        } 
     1452 
     1453        $res.= ($raw) ? $c : $this->filterText($c); 
    11611454 
    11621455        $res.= ")\n"; 
     
    13191612      foreach($o['info']['rect'] as  $v) { 
    13201613 
    1321         $res.=  sprintf("%.4f ", $v); 
     1614        $res.=  sprintf("%.4F ", $v); 
    13221615      } 
    13231616 
     
    14691762      $this->objects[$id] = array('t'=>'contents', 'c'=>'', 'info'=>array()); 
    14701763 
    1471       if  (strlen($options) &&  intval($options)) { 
     1764      if  (mb_strlen($options) &&  intval($options)) { 
    14721765 
    14731766        // then this contents is the primary for a page 
     
    14901783 
    14911784    case  'out': 
    1492  
    14931785      $tmp = $o['c']; 
    14941786 
     
    15221814        } 
    15231815 
    1524         $res.= "\n/Length ".strlen($tmp) ." >>\nstream\n".$tmp."\nendstream"; 
    1525       } 
    1526  
    1527       $res.= "\nendobj\n"; 
     1816        $res.= "\n/Length ".mb_strlen($tmp) ." >>\nstream\n".$tmp."\nendstream"; 
     1817      } 
     1818 
     1819      $res.= "\nendobj"; 
    15281820 
    15291821      return  $res; 
     
    15401832 
    15411833    if  ($action != 'new') { 
    1542  
    15431834      $o = & $this->objects[$id]; 
    15441835    } 
     
    15891880 
    15901881        $this->objects[$id]['info']['DecodeParms'] = '<< /Predictor 15 /Colors '.$options['ncolor'].' /Columns '.$options['iw'].' /BitsPerComponent '.$options['bitsPerComponent'].'>>'; 
    1591         if  (strlen($options['pdata'])) { 
    1592  
    1593           $tmp =  ' [ /Indexed /DeviceRGB '.(strlen($options['pdata']) /3-1) .' '; 
     1882        if  (mb_strlen($options['pdata'])) { 
     1883 
     1884          $tmp =  ' [ /Indexed /DeviceRGB '.(mb_strlen($options['pdata']) /3-1) .' '; 
    15941885 
    15951886          $this->numObj++; 
     
    16261917              pre_r($tmp); 
    16271918              break; 
    1628                  
     1919               
    16291920            } 
    16301921          } 
     
    16511942              $this->objects[$id]['info']['Mask'] = $tmp; 
    16521943              break; 
    1653                  
     1944               
    16541945            } 
    16551946          } 
     
    16871978      } 
    16881979 
    1689       $res.= "\n/Length ".strlen($tmp) ." >>\nstream\n".$tmp."\nendstream\nendobj\n"; 
     1980      $res.= "\n/Length ".mb_strlen($tmp, '8bit') .">>\nstream\n".$tmp."\nendstream\nendobj"; 
    16901981 
    16911982      return  $res; 
     
    17652056      $pad =  chr(0x28) .chr(0xBF) .chr(0x4E) .chr(0x5E) .chr(0x4E) .chr(0x75) .chr(0x8A) .chr(0x41) .chr(0x64) .chr(0x00) .chr(0x4E) .chr(0x56) .chr(0xFF) .chr(0xFA) .chr(0x01) .chr(0x08) .chr(0x2E) .chr(0x2E) .chr(0x00) .chr(0xB6) .chr(0xD0) .chr(0x68) .chr(0x3E) .chr(0x80) .chr(0x2F) .chr(0x0C) .chr(0xA9) .chr(0xFE) .chr(0x64) .chr(0x53) .chr(0x69) .chr(0x7A); 
    17662057 
    1767       $len =  strlen($options['owner']); 
     2058      $len =  mb_strlen($options['owner']); 
    17682059 
    17692060      if  ($len>32) { 
     
    17782069      } 
    17792070 
    1780       $len =  strlen($options['user']); 
     2071      $len =  mb_strlen($options['user']); 
    17812072 
    17822073      if  ($len>32) { 
     
    18432134      $res.= "\n/P ".($o['info']['p']); 
    18442135 
    1845       $res.= "\n>>\nendobj\n"; 
     2136      $res.= "\n>>\nendobj"; 
    18462137 
    18472138 
     
    18852176    $hex =  dechex($id); 
    18862177 
    1887     if  (strlen($hex) <6) { 
    1888  
    1889       $hex =  substr('000000', 0, 6-strlen($hex)) .$hex; 
     2178    if  (mb_strlen($hex) <6) { 
     2179 
     2180      $hex =  substr('000000', 0, 6-mb_strlen($hex)) .$hex; 
    18902181    } 
    18912182 
     
    19062197 
    19072198    // setup the control array 
    1908     if  (strlen($key) == 0) { 
     2199    if  (mb_strlen($key) == 0) { 
    19092200 
    19102201      return; 
     
    19132204    $k =  ''; 
    19142205 
    1915     while (strlen($k) <256) { 
     2206    while (mb_strlen($k) <256) { 
    19162207 
    19172208      $k.= $key; 
     
    19452236  function  ARC4($text) { 
    19462237 
    1947     $len = strlen($text); 
     2238    $len = mb_strlen($text); 
    19482239 
    19492240    $a = 0; 
     
    20372328      $this->numObj++; 
    20382329 
    2039       if  (strlen($ownerPass) == 0) { 
     2330      if  (mb_strlen($ownerPass) == 0) { 
    20402331 
    20412332        $ownerPass = $userPass; 
     
    20612352 
    20622353    if  ($debug) { 
    2063  
    20642354      // turn compression off 
    20652355      $this->options['compression'] = 0; 
     
    20782368    $xref = array(); 
    20792369 
    2080     $content = "%PDF-1.3\n%âãÏÓ\n"; 
    2081  
    2082     //  $content="%PDF-1.3\n"; 
    2083     $pos = strlen($content); 
     2370    $content = '%PDF-1.3'; 
     2371    $pos = mb_strlen($content); 
    20842372 
    20852373    foreach($this->objects as  $k=>$v) { 
     
    20932381      $xref[] = $pos; 
    20942382 
    2095       $pos+= strlen($cont); 
     2383      $pos+= mb_strlen($cont); 
    20962384    } 
    20972385 
     
    21112399    } 
    21122400 
    2113     if  (strlen($this->fileIdentifier)) { 
     2401    if  (mb_strlen($this->fileIdentifier)) { 
    21142402 
    21152403      $content.=  "/ID[<".$this->fileIdentifier."><".$this->fileIdentifier.">]\n"; 
     
    21882476  function  openFont($font) { 
    21892477 
    2190     // assume that $font contains both the path and perhaps the extension to the file, split them 
     2478    // assume that $font contains the path and file but not the extension 
    21912479    $pos = strrpos($font, '/'); 
    21922480 
     
    22022490      $name = substr($font, $pos+1); 
    22032491    } 
    2204  
    2205  
    2206     if  (substr($name, -4) == '.afm') { 
    2207  
    2208       $name = substr($name, 0, strlen($name) -4); 
    2209     } 
    2210  
     2492     
     2493    $fontcache = $this->fontcache; 
     2494    if ($fontcache == '') { 
     2495        $fontcache = $dir; 
     2496    } 
     2497     
     2498    //$name       filename without folder and extension of font metrics 
     2499    //$dir                folder of font metrics 
     2500    //$fontcache  folder of runtime created php serialized version of font metrics. 
     2501    //            If this is not given, the same folder as the font metrics will be used. 
     2502    //            Storing and reusing serialized versions improves speed much 
     2503     
    22112504    $this->addMessage('openFont: '.$font.' - '.$name); 
    22122505 
    2213     if  (file_exists($dir . 'php_' . $name . '.afm')) { 
    2214  
    2215       $this->addMessage('openFont: php file exists ' . $dir . 'php_' . $name.'.afm'); 
    2216  
    2217       $tmp =  file_get_contents($dir.'php_'.$name.'.afm'); 
     2506    $metrics_name = $name . (($this->isUnicode) ? '.ufm' : '.afm'); 
     2507    $cache_name = 'php_' . $metrics_name; 
     2508    $this->addMessage('metrics: '.$metrics_name.', cache: '.$cache_name); 
     2509    if  (file_exists($fontcache . $cache_name)) { 
     2510 
     2511      $this->addMessage('openFont: php file exists ' . $fontcache . $cache_name); 
     2512 
     2513      $tmp =  file_get_contents($fontcache . $cache_name); 
    22182514 
    22192515      eval($tmp); 
     
    22282524    } 
    22292525 
    2230     if  (!isset($this->fonts[$font]) &&  file_exists($dir.$name.'.afm')) { 
     2526    if  (!isset($this->fonts[$font]) &&  file_exists($dir . $metrics_name)) { 
    22312527 
    22322528      // then rebuild the php_<font>.afm file from the <font>.afm file 
    2233       $this->addMessage('openFont: build php file from '.$dir.$name.'.afm'); 
     2529      $this->addMessage('openFont: build php file from ' . $dir . $metrics_name); 
    22342530 
    22352531      $data =  array(); 
    2236  
    2237       $file =  file($dir.$name.'.afm'); 
     2532      $cidtogid = ''; 
     2533      if ($this->isUnicode) { 
     2534        $cidtogid = str_pad('', 256*256*2, "\x00"); 
     2535      } 
     2536 
     2537      $file =  file($dir . $metrics_name); 
    22382538 
    22392539      foreach ($file as  $rowA) { 
     
    22862586          case  'StartCharMetrics': 
    22872587 
     2588          case  'FontHeightOffset': // OAR - Added so we can offset the height calculation of a Windows font.  Otherwise it's too big. 
     2589 
    22882590            $data[$key] = trim(substr($row, $pos)); 
    22892591 
     
    22962598            break; 
    22972599 
    2298           case  'C': 
     2600          case  'C': // Found in AFM files 
    22992601 
    23002602            //C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ; 
     
    23072609              $bits2 =  explode(' ', trim($bit)); 
    23082610 
    2309               if  (strlen($bits2[0])) { 
     2611              if  (mb_strlen($bits2[0])) { 
    23102612 
    23112613                if  (count($bits2) >2) { 
     
    23242626            } 
    23252627 
    2326             if  ($dtmp['C'] >= 0) { 
     2628            $cc = (int)$dtmp['C']; 
     2629            if  ($cc >= 0) { 
    23272630 
    23282631              $data['C'][$dtmp['C']] = $dtmp; 
     
    23322635 
    23332636              $data['C'][$dtmp['N']] = $dtmp; 
     2637            } 
     2638 
     2639            if  (!isset($data['MissingWidth']) && $cc == -1 && $dtmp['N'] == '.notdef') { 
     2640 
     2641              $data['MissingWidth'] = $width; 
     2642            } 
     2643             
     2644            break; 
     2645 
     2646          case  'U': // Found in UFM files 
     2647            if ($this->isUnicode) { 
     2648              // U 827 ; WX 0 ; N squaresubnosp ; G 675 ; 
     2649              $bits = explode(';', trim($row)); 
     2650               
     2651              $dtmp = array(); 
     2652 
     2653              foreach($bits as  $bit) { 
     2654 
     2655                $bits2 =  explode(' ', trim($bit)); 
     2656 
     2657                if  (mb_strlen($bits2[0])) { 
     2658 
     2659                  if  (count($bits2) >2) { 
     2660 
     2661                    $dtmp[$bits2[0]] = array(); 
     2662 
     2663                    for  ($i = 1;$i<count($bits2);$i++) { 
     2664 
     2665                      $dtmp[$bits2[0]][] = $bits2[$i]; 
     2666                    } 
     2667                  } else  if  (count($bits2) == 2) { 
     2668 
     2669                    $dtmp[$bits2[0]] = $bits2[1]; 
     2670                  } 
     2671                } 
     2672              } 
     2673 
     2674              $cc = (int)$dtmp['U']; 
     2675              $glyph = $dtmp['G']; 
     2676              $width = $dtmp['WX']; 
     2677              if  ($cc >= 0) { 
     2678                // Set values in CID to GID map 
     2679                if ($cc >= 0 && $cc < 0xFFFF && $glyph) { 
     2680                  $cidtogid{$cc*2} = chr($glyph >> 8); 
     2681                  $cidtogid{$cc*2 + 1} = chr($glyph & 0xFF); 
     2682                } 
     2683 
     2684                $data['C'][$dtmp['U']] = $dtmp; 
     2685 
     2686                $data['C'][$dtmp['N']] = $dtmp; 
     2687              } else { 
     2688 
     2689                $data['C'][$dtmp['N']] = $dtmp; 
     2690              } 
     2691               
     2692              if  (!isset($data['MissingWidth']) && $cc == -1 && $dtmp['N'] == '.notdef') { 
     2693 
     2694                $data['MissingWidth'] = $width; 
     2695              } 
    23342696            } 
    23352697 
     
    23482710      } 
    23492711 
     2712      //    echo $cidtogid; die("CIDtoGID Displayed!"); 
     2713 
     2714      if  (function_exists('gzcompress') &&  $this->options['compression']) { 
     2715 
     2716        // then implement ZLIB based compression on CIDtoGID string 
     2717        $data['CIDtoGID_Compressed'] = true; 
     2718 
     2719        $cidtogid =  gzcompress($cidtogid,  6); 
     2720      } 
     2721      $data['CIDtoGID'] = base64_encode($cidtogid); 
     2722       
    23502723      $data['_version_'] = 1; 
    23512724 
    23522725      $this->fonts[$font] = $data; 
    23532726 
    2354       file_put_contents($dir . 'php_' . $name . '.afm',  '$this->fonts[$font]=' . var_export($data,  true)  . ';'); 
    2355     } else  if  (!isset($this->fonts[$font])) { 
    2356  
    2357       $this->addMessage('openFont: no font file found'); 
    2358  
    2359       //    echo 'Font not Found '.$font; 
    2360  
    2361     } 
     2727      //Because of potential trouble with php safe mode, expect that the folder already exists. 
     2728      //If not existing, this will hit performance because of missing cached results. 
     2729      if ( is_dir(substr($fontcache,0,-1)) ) { 
     2730        file_put_contents($fontcache . $cache_name,  '$this->fonts[$font]=' . var_export($data,  true)  . ';'); 
     2731      } 
     2732    } 
     2733     
     2734    if  (!isset($this->fonts[$font])) { 
     2735      $this->addMessage("openFont: no font file found for $font.  Do you need to run load_font.php?"); 
     2736      //echo 'Font not Found '.$font; 
     2737    } 
     2738 
     2739    //pre_r($this->messages); 
    23622740  } 
    23632741 
     
    23712749   * 
    23722750   */ 
    2373   function  selectFont($fontName, $encoding =  '', $set =  1) { 
     2751  function  selectFont($fontName, $encoding =  '', $set =  true) { 
     2752 
     2753    $ext = substr($fontName, -4); 
     2754    if  ($ext == '.afm' || $ext == '.ufm') { 
     2755      $fontName = substr($fontName, 0, mb_strlen($fontName)-4); 
     2756    } 
    23742757 
    23752758    if  (!isset($this->fonts[$fontName])) { 
     2759      $this->addMessage("selectFont: selecting - $fontName - $encoding, $set"); 
    23762760 
    23772761      // load the file 
     
    23902774        $name =  substr($fontName, $pos+1); 
    23912775 
    2392         if  (substr($name, -4) ==  '.afm') { 
    2393  
    2394           $name =  substr($name, 0, strlen($name) -4); 
    2395         } 
    2396  
    2397  
    2398         $options =  array('name' => $name); 
     2776        $options =  array('name' => $name, 'fontFileName' => $fontName); 
    23992777 
    24002778        if  (is_array($encoding)) { 
     
    24102788            $options['differences'] =  $encoding['differences']; 
    24112789          } 
    2412         } else  if  (strlen($encoding)) { 
     2790        } else  if  (mb_strlen($encoding)) { 
    24132791 
    24142792          // then perhaps only the encoding has been set 
     
    24272805        // should be for all non-basic fonts), then load it into an object and put the 
    24282806        // references into the font object 
    2429         $basefile =  substr($fontName, 0, strlen($fontName) -4); 
    2430  
     2807        $basefile =  $fontName; 
    24312808        if  (file_exists($basefile.'.pfb')) { 
    24322809 
     
    24482825 
    24492826 
    2450         if  (substr($fontName, -4) ==  '.afm' &&  strlen($fbtype)) { 
    2451  
     2827        // OAR - I don't understand this old check 
     2828        // if  (substr($fontName, -4) ==  '.afm' &&  strlen($fbtype)) { 
     2829        if  (mb_strlen($fbtype)) { 
    24522830          $adobeFontName =  $this->fonts[$fontName]['FontName']; 
    24532831 
     
    24552833          $this->addMessage('selectFont: adding font file - '.$fbfile.' - '.$adobeFontName); 
    24562834 
    2457           // find the array of fond widths, and put that into an object. 
     2835          // find the array of font widths, and put that into an object. 
    24582836          $firstChar =  -1; 
    24592837 
     
    24612839 
    24622840          $widths =  array(); 
     2841          $cid_widths = array(); 
    24632842 
    24642843          foreach ($this->fonts[$fontName]['C'] as  $num => $d) { 
     
    24662845            if  (intval($num) >0 ||  $num ==  '0') { 
    24672846 
    2468               if  ($lastChar>0 &&  $num>$lastChar+1) { 
    2469  
    2470                 for ($i =  $lastChar+1;$i<$num;$i++) { 
    2471  
    2472                   $widths[] =  0; 
     2847              if (!$this->isUnicode) { 
     2848                // With Unicode, widths array isn't used 
     2849                if  ($lastChar>0 &&  $num>$lastChar+1) { 
     2850 
     2851                  for ($i =  $lastChar+1;$i<$num;$i++) { 
     2852 
     2853                    $widths[] =  0; 
     2854                  } 
    24732855                } 
    24742856              } 
     
    24762858              $widths[] =  $d['WX']; 
    24772859 
     2860              if ($this->isUnicode) { 
     2861                $cid_widths[$num] =  $d['WX']; 
     2862              } 
     2863 
    24782864              if  ($firstChar ==  -1) { 
    2479  
    24802865                $firstChar =  $num; 
    24812866              } 
     
    24922877              if  ($charNum > $lastChar) { 
    24932878 
    2494                 for ($i =  $lastChar + 1; $i <=  $charNum; $i++) { 
    2495  
    2496                   $widths[] =  0; 
     2879                if (!$this->isUnicode) { 
     2880                  // With Unicode, widths array isn't used 
     2881                  for ($i =  $lastChar + 1; $i <=  $charNum; $i++) { 
     2882 
     2883                    $widths[] =  0; 
     2884                  } 
    24972885                } 
    24982886 
     
    25032891 
    25042892                $widths[$charNum-$firstChar] =  $this->fonts[$fontName]['C'][$charName]['WX']; 
     2893                if ($this->isUnicode) { 
     2894                  $cid_widths[$charName] =  $this->fonts[$fontName]['C'][$charName]['WX']; 
     2895                } 
    25052896              } 
    25062897            } 
    25072898          } 
    25082899 
     2900          if ($this->isUnicode) { 
     2901            $this->fonts[$fontName]['CIDWidths'] = $cid_widths; 
     2902          } 
     2903 
    25092904          $this->addMessage('selectFont: FirstChar = '.$firstChar); 
    25102905 
    25112906          $this->addMessage('selectFont: LastChar = '.$lastChar); 
    25122907 
    2513           $this->numObj++; 
    2514  
    2515           $this->o_contents($this->numObj, 'new', 'raw'); 
    2516  
    2517           $this->objects[$this->numObj]['c'].=  '['; 
    2518  
    2519           foreach($widths as  $width) { 
    2520  
    2521             $this->objects[$this->numObj]['c'].=  ' '.$width; 
     2908          $widthid = -1; 
     2909 
     2910          if (!$this->isUnicode) { 
     2911            // With Unicode, widths array isn't used 
     2912 
     2913            $this->numObj++; 
     2914 
     2915            $this->o_contents($this->numObj, 'new', 'raw'); 
     2916 
     2917            $this->objects[$this->numObj]['c'].=  '['; 
     2918 
     2919            foreach($widths as  $width) { 
     2920              $this->objects[$this->numObj]['c'].=  ' '.$width; 
     2921            } 
     2922 
     2923            $this->objects[$this->numObj]['c'].=  ' ]'; 
     2924 
     2925            $widthid =  $this->numObj; 
    25222926          } 
    25232927 
    2524           $this->objects[$this->numObj]['c'].=  ' ]'; 
    2525  
    2526           $widthid =  $this->numObj; 
    2527  
     2928          $missing_width = 500; 
     2929          $stemV = 70; 
     2930 
     2931          if (isset($this->fonts[$fontName]['MissingWidth'])) { 
     2932 
     2933            $missing_width =  $this->fonts[$fontName]['MissingWidth']; 
     2934          } 
     2935          if (isset($this->fonts[$fontName]['StdVW'])) { 
     2936 
     2937            $stemV = $this->fonts[$fontName]['StdVW']; 
     2938          } elseif (isset($this->fonts[$fontName]['Weight']) && preg_match('!(bold|black)!i', $this->fonts[$fontName]['Weight'])) { 
     2939 
     2940            $stemV = 120; 
     2941          } 
    25282942 
    25292943          // load the pfb file, and put that into an object too. 
    25302944          // note that pdf supports only binary format type 1 font files, though there is a 
    25312945          // simple utility to convert them from pfa to pfb. 
    2532           $tmp =  get_magic_quotes_runtime(); 
    2533  
    2534           set_magic_quotes_runtime(0); 
    2535  
    25362946          $data =  file_get_contents($fbfile); 
    2537  
    2538           set_magic_quotes_runtime($tmp); 
    25392947 
    25402948 
     
    25592967          } 
    25602968 
    2561           $flags+=  pow(2, 5); 
    2562           // assume non-sybolic 
    2563  
    2564           $list =  array('Ascent' => 'Ascender', 'CapHeight' => 'CapHeight', 'Descent' => 'Descender', 'FontBBox' => 'FontBBox', 'ItalicAngle' => 'ItalicAngle'); 
    2565  
    2566           $fdopt =  array( 
    2567                           'Flags' => $flags, 'FontName' => $adobeFontName, 'StemV' => 100  // don't know what the value for this should be! 
    2568                           ); 
     2969          $flags+=  pow(2, 5); // assume non-sybolic 
     2970 
     2971          $list =  array('Ascent' => 'Ascender', 'CapHeight' => 'CapHeight', 'MissingWidth' => 'MissingWidth', 'Descent' => 'Descender', 'FontBBox' => 'FontBBox', 'ItalicAngle' => 'ItalicAngle'); 
     2972 
     2973          $fdopt =  array('Flags' => $flags, 'FontName' => $adobeFontName, 'StemV' => $stemV); 
    25692974 
    25702975          foreach($list as  $k => $v) { 
     
    26003005            $l2 =  strpos($data, '00000000') -$l1; 
    26013006 
    2602             $l3 =  strlen($data) -$l2-$l1; 
     3007            $l3 =  mb_strlen($data) -$l2-$l1; 
    26033008 
    26043009            $this->o_contents($this->numObj, 'add', array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3)); 
    26053010          } else  if  ($fbtype ==  'ttf') { 
    26063011 
    2607             $l1 =  strlen($data); 
     3012            $l1 =  mb_strlen($data); 
    26083013 
    26093014            $this->o_contents($this->numObj, 'add', array('Length1' => $l1)); 
     
    26133018 
    26143019          // tell the font object about all this new stuff 
    2615           $tmp =  array('BaseFont' => $adobeFontName, 'Widths' => $widthid, 'FirstChar' => $firstChar, 'LastChar' => $lastChar, 'FontDescriptor' => $fontDescriptorId); 
     3020          $tmp =  array('BaseFont' => $adobeFontName, 'MissingWidth' => $missing_width, 'Widths' => $widthid, 'FirstChar' => $firstChar, 'LastChar' => $lastChar, 'FontDescriptor' => $fontDescriptorId); 
    26163021 
    26173022          if  ($fbtype ==  'ttf') { 
     
    27353140    if  ($r >=  0 &&  ($force ||  $r !=  $this->currentColour['r'] ||  $g !=  $this->currentColour['g'] ||  $b !=  $this->currentColour['b'])) { 
    27363141 
    2737       $this->objects[$this->currentContents]['c'].=  "\n".sprintf('%.3f', $r) .' '.sprintf('%.3f', $g) .' '.sprintf('%.3f', $b) .' rg'; 
     3142      $this->objects[$this->currentContents]['c'].=  "\n".sprintf('%.3F', $r) .' '.sprintf('%.3F', $g) .' '.sprintf('%.3F', $b) .' rg'; 
    27383143 
    27393144      $this->currentColour =  array('r' => $r, 'g' => $g, 'b' => $b); 
     
    27493154    if  ($r >=  0 &&  ($force ||  $r !=  $this->currentStrokeColour['r'] ||  $g !=  $this->currentStrokeColour['g'] ||  $b !=  $this->currentStrokeColour['b'])) { 
    27503155 
    2751       $this->objects[$this->currentContents]['c'].=  "\n".sprintf('%.3f', $r) .' '.sprintf('%.3f', $g) .' '.sprintf('%.3f', $b) .' RG'; 
     3156      $this->objects[$this->currentContents]['c'].=  "\n".sprintf('%.3F', $r) .' '.sprintf('%.3F', $g) .' '.sprintf('%.3F', $b) .' RG'; 
    27523157 
    27533158      $this->currentStrokeColour =  array('r' => $r, 'g' => $g, 'b' => $b); 
     
    28133218   * 
    28143219   * @param string $mode the blend mode to use 
    2815    * @param float $opacity 0.0 fully transparent, 1.0 fully opaque    
     3220   * @param float $opacity 0.0 fully transparent, 1.0 fully opaque 
    28163221   */ 
    28173222  function setFillTransparency($mode, $opacity) { 
     
    28403245 
    28413246    $this->objects[$this->currentContents]['c'] .= 
    2842       "\n".sprintf('%.3f', $x1) .' '.sprintf('%.3f', $y1) .' m '.sprintf('%.3f', $x2) .' '.sprintf('%.3f', $y2) .' l S'; 
     3247      "\n".sprintf('%.3F', $x1) .' '.sprintf('%.3F', $y1) .' m '.sprintf('%.3F', $x2) .' '.sprintf('%.3F', $y2) .' l S'; 
    28433248  } 
    28443249 
     
    28523257    // as the control points for the curve. 
    28533258    $this->objects[$this->currentContents]['c'] .= 
    2854       "\n".sprintf('%.3f', $x0) .' '.sprintf('%.3f', $y0) .' m '.sprintf('%.3f', $x1) .' '.sprintf('%.3f', $y1); 
     3259      "\n".sprintf('%.3F', $x0) .' '.sprintf('%.3F', $y0) .' m '.sprintf('%.3F', $x1) .' '.sprintf('%.3F', $y1); 
    28553260 
    28563261    $this->objects[$this->currentContents]['c'] .= 
    2857       ' '.sprintf('%.3f', $x2) .' '.sprintf('%.3f', $y2) .' '.sprintf('%.3f', $x3) .' '.sprintf('%.3f', $y3) .' c S'; 
     3262      ' '.sprintf('%.3F', $x2) .' '.sprintf('%.3F', $y2) .' '.sprintf('%.3F', $x3) .' '.sprintf('%.3F', $y3) .' c S'; 
    28583263  } 
    28593264 
     
    29193324 
    29203325      $tmp  =  "\n q "; 
    2921       $tmp .=  sprintf('%.3f', cos($a)) .' '.sprintf('%.3f', (-1.0*sin($a))) .' '.sprintf('%.3f', sin($a)) .' '.sprintf('%.3f', cos($a)) .' '; 
    2922       $tmp .=  sprintf('%.3f', $x0) .' '.sprintf('%.3f', $y0) .' cm'; 
     3326      $tmp .=  sprintf('%.3F', cos($a)) .' '.sprintf('%.3F', (-1.0*sin($a))) .' '.sprintf('%.3F', sin($a)) .' '.sprintf('%.3F', cos($a)) .' '; 
     3327      $tmp .=  sprintf('%.3F', $x0) .' '.sprintf('%.3F', $y0) .' cm'; 
    29233328 
    29243329      $this->objects[$this->currentContents]['c'].=  $tmp; 
     
    29363341 
    29373342 
    2938     $this->objects[$this->currentContents]['c'] .=  "\n".sprintf('%.3f', $a0) .' '.sprintf('%.3f', $b0) .' m '; 
     3343    $this->objects[$this->currentContents]['c'] .=  "\n".sprintf('%.3F', $a0) .' '.sprintf('%.3F', $b0) .' m '; 
    29393344 
    29403345    for  ($i = 1; $i <=  $nSeg; $i++) { 
     
    29523357 
    29533358      $this->objects[$this->currentContents]['c'] 
    2954         .=  "\n".sprintf('%.3f', ($a0+$c0*$dtm)) .' '.sprintf('%.3f', ($b0 + $d0 * $dtm)); 
     3359        .=  "\n".sprintf('%.3F', ($a0+$c0*$dtm)) .' '.sprintf('%.3F', ($b0 + $d0 * $dtm)); 
    29553360 
    29563361      $this->objects[$this->currentContents]['c'] .= 
    2957         ' '.sprintf('%.3f', ($a1-$c1*$dtm)) .' '.sprintf('%.3f', ($b1-$d1*$dtm)) .' '.sprintf('%.3f', $a1) .' '.sprintf('%.3f', $b1) .' c'; 
     3362        ' '.sprintf('%.3F', ($a1-$c1*$dtm)) .' '.sprintf('%.3F', ($b1-$d1*$dtm)) .' '.sprintf('%.3F', $a1) .' '.sprintf('%.3F', $b1) .' c'; 
    29583363 
    29593364      $a0 =  $a1; 
     
    30513456    $this->objects[$this->currentContents]['c'].=  "\n"; 
    30523457 
    3053     $this->objects[$this->currentContents]['c'].=  sprintf('%.3f', $p[0]) .' '.sprintf('%.3f', $p[1]) .' m '; 
     3458    $this->objects[$this->currentContents]['c'].=  sprintf('%.3F', $p[0]) .' '.sprintf('%.3F', $p[1]) .' m '; 
    30543459 
    30553460    for  ($i =  2; $i < $np * 2; $i =  $i + 2) { 
    30563461 
    3057       $this->objects[$this->currentContents]['c'].=  sprintf('%.3f', $p[$i]) .' '.sprintf('%.3f', $p[$i+1]) .' l '; 
     3462      $this->objects[$this->currentContents]['c'].=  sprintf('%.3F', $p[$i]) .' '.sprintf('%.3F', $p[$i+1]) .' l '; 
    30583463    } 
    30593464 
     
    30743479  function  filledRectangle($x1, $y1, $width, $height) { 
    30753480 
    3076     $this->objects[$this->currentContents]['c'].=  "\n".sprintf('%.3f', $x1) .' '.sprintf('%.3f', $y1) .' '.sprintf('%.3f', $width) .' '.sprintf('%.3f', $height) .' re f'; 
     3481    $this->objects[$this->currentContents]['c'].=  "\n".sprintf('%.3F', $x1) .' '.sprintf('%.3F', $y1) .' '.sprintf('%.3F', $width) .' '.sprintf('%.3F', $height) .' re f'; 
    30773482  } 
    30783483 
     
    30843489  function  rectangle($x1, $y1, $width, $height) { 
    30853490 
    3086     $this->objects[$this->currentContents]['c'].=  "\n".sprintf('%.3f', $x1) .' '.sprintf('%.3f', $y1) .' '.sprintf('%.3f', $width) .' '.sprintf('%.3f', $height) .' re S'; 
     3491    $this->objects[$this->currentContents]['c'].=  "\n".sprintf('%.3F', $x1) .' '.sprintf('%.3F', $y1) .' '.sprintf('%.3F', $width) .' '.sprintf('%.3F', $height) .' re S'; 
    30873492  } 
    30883493 
     
    31453550 
    31463551    // if there is a line style set, then put this in too 
    3147     if  (strlen($this->currentLineStyle)) { 
     3552    if  (mb_strlen($this->currentLineStyle)) { 
    31483553 
    31493554      $this->objects[$this->currentContents]['c'].=  "\n".$this->currentLineStyle; 
     
    31933598    header("Content-type: application/pdf"); 
    31943599 
    3195     //header("Content-Length: " . strlen($tmp)); 
     3600    //FIXME: I don't know that this is sufficient for determining content length (i.e. what about transport compression?) 
     3601    //header("Content-Length: " . mb_strlen($tmp)); 
    31963602    $fileName =  (isset($options['Content-Disposition']) ?  $options['Content-Disposition'] :  'file.pdf'); 
    31973603 
     
    32073613 
    32083614    if  (isset($options['Accept-Ranges']) &&  $options['Accept-Ranges'] ==  1) { 
    3209  
    3210       header("Accept-Ranges: " . strlen($tmp)); 
     3615      //FIXME: Is this the correct value ... spec says 1#range-unit 
     3616      header("Accept-Ranges: " . mb_strlen($tmp)); 
    32113617    } 
    32123618 
     
    32233629 
    32243630    if  (!$this->numFonts) { 
    3225  
    32263631      $this->selectFont('./fonts/Helvetica'); 
    32273632    } 
    3228  
     3633     
    32293634    // for the current font, and the given size, what is the height of the font in user units 
    32303635    $h =  $this->fonts[$this->currentFont]['FontBBox'][3]-$this->fonts[$this->currentFont]['FontBBox'][1]; 
    32313636 
     3637    // have to adjust by a font offset for Windows fonts.  unfortunately it looks like 
     3638    // the bounding box calculations are wrong and I don't know why. 
     3639    if (isset($this->fonts[$this->currentFont]['FontHeightOffset'])) { 
     3640 
     3641      // For CourierNew from Windows this needs to be -646 to match the 
     3642      // Adobe native Courier font. 
     3643      // 
     3644      // For FreeMono from GNU this needs to be -337 to match the 
     3645      // Courier font. 
     3646      // 
     3647      // Both have been added manually to the .afm and .ufm files. 
     3648      $h += (int)$this->fonts[$this->currentFont]['FontHeightOffset']; 
     3649    } 
     3650 
    32323651    return  $size*$h/1000; 
    32333652  } 
     
    32353654 
    32363655  /** 
    3237    * return the font decender, this will normally return a negative number 
     3656   * return the font descender, this will normally return a negative number 
    32383657   * if you add this number to the baseline, you get the level of the bottom of the font 
    32393658   * it is in the pdf user units 
    32403659   */ 
    3241   function  getFontDecender($size) { 
     3660  function  getFontDescender($size) { 
    32423661 
    32433662    // note that this will most likely return a negative value 
    32443663    if  (!$this->numFonts) { 
    3245  
    32463664      $this->selectFont('./fonts/Helvetica'); 
    32473665    } 
    32483666 
    3249     $h =  $this->fonts[$this->currentFont]['FontBBox'][1]; 
     3667    //$h = $this->fonts[$this->currentFont]['FontBBox'][1]; 
     3668    $h = $this->fonts[$this->currentFont]['Descender']; 
    32503669 
    32513670    return  $size*$h/1000; 
     
    32593678   * @access private 
    32603679   */ 
    3261   function  filterText($text) { 
    3262  
    3263     $search =  array("\\",  "(",  ")",  "&lt;",  "&gt;",  "&#039;",  "&quot;",  "&amp;"); 
    3264  
    3265     $replace =  array("\\\\",  "\(",  "\)",  "<",  ">",  "\'",  '"',  "&"); 
    3266  
    3267     $text =  str_replace($search,  $replace,  $text); 
    3268  
    3269  
     3680  function  filterText($text, $bom = true) { 
     3681    if ($this->isUnicode) { 
     3682      $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8'); 
     3683      $text =  $this->utf8toUtf16BE($text, $bom); 
     3684    } else { 
     3685      if (in_array('Windows-1252', mb_list_encodings())) { 
     3686        $text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8'); 
     3687      } else { 
     3688        $text = mb_convert_encoding($text, 'iso-8859-1', 'UTF-8'); 
     3689      } 
     3690      $text = html_entity_decode($text, ENT_QUOTES); 
     3691    } 
     3692 
     3693    // the chr(13) substitution fixes a bug seen in TCPDF (bug #1421290) 
     3694    $text = strtr($text, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r')); 
    32703695    return  $text; 
     3696  } 
     3697 
     3698  /** 
     3699   * return array containing codepoints (UTF-8 character values) for the 
     3700   * string passed in. 
     3701   * 
     3702   * based on the excellent TCPDF code by Nicola Asuni and the 
     3703   * RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html 
     3704   * 
     3705   * @access private 
     3706   * @author Orion Richardson 
     3707   * @since January 5, 2008 
     3708   * @param string $text UTF-8 string to process 
     3709   * @return array UTF-8 codepoints array for the string 
     3710   */ 
     3711  function  utf8toCodePointsArray(&$text) { 
     3712    $length = mb_strlen($text,'8bit'); // http://www.php.net/manual/en/function.mb-strlen.php#77040 
     3713    $unicode = array(); // array containing unicode values 
     3714    $bytes = array(); // array containing single character byte sequences 
     3715    $numbytes = 1; // number of octetc needed to represent the UTF-8 character 
     3716     
     3717    for ($i = 0; $i < $length; $i++) { 
     3718      $c = ord($text{$i}); // get one string character at time 
     3719      if (count($bytes) == 0) { // get starting octect 
     3720        if ($c <= 0x7F) { 
     3721          $unicode[] = $c; // use the character "as is" because is ASCII 
     3722          $numbytes = 1; 
     3723        } elseif (($c >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN) 
     3724          $bytes[] = ($c - 0xC0) << 0x06; 
     3725          $numbytes = 2; 
     3726        } elseif (($c >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN) 
     3727          $bytes[] = ($c - 0xE0) << 0x0C; 
     3728          $numbytes = 3; 
     3729        } elseif (($c >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN) 
     3730          $bytes[] = ($c - 0xF0) << 0x12; 
     3731          $numbytes = 4; 
     3732        } else { 
     3733          // use replacement character for other invalid sequences 
     3734          $unicode[] = 0xFFFD; 
     3735          $bytes = array(); 
     3736          $numbytes = 1; 
     3737        } 
     3738      } elseif (($c >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN 
     3739        $bytes[] = $c - 0x80; 
     3740        if (count($bytes) == $numbytes) { 
     3741          // compose UTF-8 bytes to a single unicode value 
     3742          $c = $bytes[0]; 
     3743          for ($j = 1; $j < $numbytes; $j++) { 
     3744            $c += ($bytes[$j] << (($numbytes - $j - 1) * 0x06)); 
     3745          } 
     3746          if ((($c >= 0xD800) AND ($c <= 0xDFFF)) OR ($c >= 0x10FFFF)) { 
     3747            // The definition of UTF-8 prohibits encoding character numbers between 
     3748            // U+D800 and U+DFFF, which are reserved for use with the UTF-16 
     3749            // encoding form (as surrogate pairs) and do not directly represent 
     3750            // characters. 
     3751            $unicode[] = 0xFFFD; // use replacement character 
     3752          } else { 
     3753            $unicode[] = $c; // add char to array 
     3754          } 
     3755          // reset data for next char 
     3756          $bytes = array(); 
     3757          $numbytes = 1; 
     3758        } 
     3759      } else { 
     3760        // use replacement character for other invalid sequences 
     3761        $unicode[] = 0xFFFD; 
     3762        $bytes = array(); 
     3763        $numbytes = 1; 
     3764      } 
     3765    } 
     3766    return $unicode; 
     3767  } 
     3768 
     3769  /** 
     3770   * convert UTF-8 to UTF-16 with an additional byte order marker 
     3771   * at the front if required. 
     3772   * 
     3773   * based on the excellent TCPDF code by Nicola Asuni and the 
     3774   * RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html 
     3775   * 
     3776   * @access private 
     3777   * @author Orion Richardson 
     3778   * @since January 5, 2008 
     3779   * @param string $text UTF-8 string to process 
     3780   * @param boolean $bom whether to add the byte order marker 
     3781   * @return string UTF-16 result string 
     3782   */ 
     3783  function  utf8toUtf16BE(&$text, $bom = true) { 
     3784    if (!$this->isUnicode) return $text; 
     3785    $out = $bom ? "\xFE\xFF" : ''; 
     3786     
     3787    $unicode = $this->utf8toCodePointsArray($text); 
     3788    foreach ($unicode as $c) { 
     3789      if ($c == 0xFFFD) { 
     3790        $out .= "\xFF\xFD"; // replacement character 
     3791      } elseif ($c < 0x10000) { 
     3792        $out .= chr($c >> 0x08); 
     3793        $out .= chr($c & 0xFF); 
     3794       } else { 
     3795        $c -= 0x10000; 
     3796        $w1 = 0xD800 | ($c >> 0x10); 
     3797        $w2 = 0xDC00 | ($c & 0x3FF); 
     3798        $out .= chr($w1 >> 0x08); 
     3799        $out .= chr($w1 & 0xFF); 
     3800        $out .= chr($w2 >> 0x08); 
     3801        $out .= chr($w2 & 0xFF); 
     3802      } 
     3803    } 
     3804    return $out; 
    32713805  } 
    32723806 
     
    33403874        $j++; 
    33413875 
    3342         if  (strlen($text) <=  $j) { 
     3876        if  (mb_strlen($text) <=  $j) { 
    33433877 
    33443878          return  $directive; 
     
    33553889          if  ($text[$j] ==  '>') { 
    33563890 
    3357             $p =  strrpos($this->currentTextState, $text[$j-1]); 
     3891            $p =  mb_strrpos($this->currentTextState, $text[$j-1]); 
    33583892 
    33593893            if  ($p !==  false) { 
    33603894 
    33613895              // then there is one to remove 
    3362               $this->currentTextState =  substr($this->currentTextState, 0, $p) .substr($this->currentTextState, $p+1); 
     3896              $this->currentTextState =  mb_substr($this->currentTextState, 0, $p) .substr($this->currentTextState, $p+1); 
    33633897            } 
    33643898 
     
    33733907          $j++; 
    33743908 
    3375           $k =  strpos($text, '>', $j); 
     3909          $k =  mb_strpos($text, '>', $j); 
    33763910 
    33773911          if  ($k !==  false &&  $text[$j] ==  ':') { 
     
    33833917 
    33843918            // split the remainder on colons to get the function name and the paramater 
    3385             $tmp =  substr($text, $j+1, $k-$j-1); 
    3386  
    3387             $b1 =  strpos($tmp, ':'); 
     3919            $tmp =  mb_substr($text, $j+1, $k-$j-1); 
     3920 
     3921            $b1 =  mb_strpos($tmp, ':'); 
    33883922 
    33893923            if  ($b1 !==  false) { 
    33903924 
    3391               $func =  substr($tmp, 0, $b1); 
    3392  
    3393               $parm =  substr($tmp, $b1+1); 
     3925              $func =  mb_substr($tmp, 0, $b1); 
     3926 
     3927              $parm =  mb_substr($tmp, $b1+1); 
    33943928            } else { 
    33953929 
     
    33993933            } 
    34003934 
    3401             if  (!isset($func) ||  !strlen(trim($func))) { 
     3935            if  (!isset($func) ||  !mb_strlen(trim($func))) { 
    34023936 
    34033937              $directive =  0; 
     
    34093943                // need to assess the text position, calculate the text width to this point 
    34103944                // can use getTextWidth to find the text width I think 
    3411                 $tmp =  $this->PRVTgetTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, substr($text, 0, $i)); 
     3945                $tmp =  $this->PRVTgetTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, mb_substr($text, 0, $i)); 
    34123946 
    34133947                $info =  array('x' => $tmp[0], 'y' => $tmp[1], 'angle' => $angle, 'status' => 'end', 'p' => $parm, 'nCallback' => $this->nCallback); 
     
    34784012        $j++; 
    34794013 
    3480         $k =  strpos($text, '>', $j); 
     4014        $k =  mb_strpos($text, '>', $j); 
    34814015 
    34824016        if  ($k !==  false &&  $text[$j] ==  ':') { 
     
    34894023          // split the remainder on colons to get the function name and the paramater 
    34904024          //          $bits = explode(':',substr($text,$j+1,$k-$j-1)); 
    3491           $tmp =  substr($text, $j+1, $k-$j-1); 
    3492  
    3493           $b1 =  strpos($tmp, ':'); 
     4025          $tmp =  mb_substr($text, $j+1, $k-$j-1); 
     4026 
     4027          $b1 =  mb_strpos($tmp, ':'); 
    34944028 
    34954029          if  ($b1 !==  false) { 
    34964030 
    3497             $func =  substr($tmp, 0, $b1); 
    3498  
    3499             $parm =  substr($tmp, $b1+1); 
     4031            $func =  mb_substr($tmp, 0, $b1); 
     4032 
     4033            $parm =  mb_substr($tmp, $b1+1); 
    35004034          } else { 
    35014035 
     
    35054039          } 
    35064040 
    3507           if  (!isset($func) ||  !strlen(trim($func))) { 
     4041          if  (!isset($func) ||  !mb_strlen(trim($func))) { 
    35084042 
    35094043            $directive =  0; 
     
    35154049              // need to assess the text position, calculate the text width to this point 
    35164050              // can use getTextWidth to find the text width I think 
    3517               // also add the text height and decender 
    3518               $tmp =  $this->PRVTgetTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, substr($text, 0, $i)); 
    3519  
    3520               $info =  array('x' => $tmp[0], 'y' => $tmp[1], 'angle' => $angle, 'status' => 'start', 'p' => $parm, 'f' => $func, 'height' => $this->getFontHeight($size), 'decender' => $this->getFontDecender($size)); 
     4051              // also add the text height and descender 
     4052              $tmp =  $this->PRVTgetTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, mb_substr($text, 0, $i)); 
     4053 
     4054              $info =  array('x' => $tmp[0], 'y' => $tmp[1], 'angle' => $angle, 'status' => 'start', 'p' => $parm, 'f' => $func, 'height' => $this->getFontHeight($size), 'descender' => $this->getFontDescender($size)); 
    35214055 
    35224056              $x =  $tmp[0]; 
     
    35694103   */ 
    35704104  function  addText($x, $y, $size, $text, $angle =  0, $wordSpaceAdjust =  0) { 
    3571  
    35724105    if  (!$this->numFonts) { 
    35734106      $this->selectFont('./fonts/Helvetica'); 
     
    35884121                       'nCallback' => $this->callback[$i]['nCallback'], 
    35894122                       'height' => $this->callback[$i]['height'], 
    3590                        'decender' => $this->callback[$i]['decender']); 
     4123                       'descender' => $this->callback[$i]['descender']); 
    35914124 
    35924125        $func =  $this->callback[$i]['f']; 
     
    35984131    if  ($angle ==  0) { 
    35994132 
    3600       $this->objects[$this->currentContents]['c'].=  "\n".'BT '.sprintf('%.3f', $x) .' '.sprintf('%.3f', $y) .' Td'; 
     4133      $this->objects[$this->currentContents]['c'].=  "\n".'BT '.sprintf('%.3F', $x) .' '.sprintf('%.3F', $y) .' Td'; 
    36014134 
    36024135    } else { 
     
    36064139      $tmp =  "\n".'BT '; 
    36074140 
    3608       $tmp.=  sprintf('%.3f', cos($a)) .' '.sprintf('%.3f', (-1.0*sin($a))) .' '.sprintf('%.3f', sin($a)) .' '.sprintf('%.3f', cos($a)) .' '; 
    3609  
    3610       $tmp.=  sprintf('%.3f', $x) .' '.sprintf('%.3f', $y) .' Tm'; 
     4141      $tmp.=  sprintf('%.3F', cos($a)) .' '.sprintf('%.3F', (-1.0*sin($a))) .' '.sprintf('%.3F', sin($a)) .' '.sprintf('%.3F', cos($a)) .' '; 
     4142 
     4143      $tmp.=  sprintf('%.3F', $x) .' '.sprintf('%.3F', $y) .' Tm'; 
    36114144 
    36124145      $this->objects[$this->currentContents]['c'].=  $tmp; 
     
    36174150      $this->wordSpaceAdjust =  $wordSpaceAdjust; 
    36184151 
    3619       $this->objects[$this->currentContents]['c'].=  ' '.sprintf('%.3f', $wordSpaceAdjust) .' Tw'; 
    3620     } 
    3621  
    3622     $len =  strlen($text); 
     4152      $this->objects[$this->currentContents]['c'].=  ' '.sprintf('%.3F', $wordSpaceAdjust) .' Tw'; 
     4153    } 
     4154 
     4155    $len =  mb_strlen($text); 
    36234156 
    36244157    $start =  0; 
     
    36314164     // then we should write what we need to 
    36324165     if ($i>$start){ 
    3633      $part = substr($text,$start,$i-$start); 
    3634      $this->objects[$this->currentContents]['c'] .= ' /F'.$this->currentFontNum.' '.sprintf('%.1f',$size).' Tf '; 
    3635      $this->objects[$this->currentContents]['c'] .= ' ('.$this->filterText($part).') Tj'; 
     4166     $part = mb_substr($text,$start,$i-$start); 
     4167     $this->objects[$this->currentContents]['c'] .= ' /F'.$this->currentFontNum.' '.sprintf('%.1F',$size).' Tf '; 
     4168     $this->objects[$this->currentContents]['c'] .= ' ('.$this->filterText($part, false).') Tj'; 
    36364169     } 
    36374170     if ($f){ 
     
    36474180     // restart the text object 
    36484181     if ($angle == 0){ 
    3649      $this->objects[$this->currentContents]['c'] .= "\n".'BT '.sprintf('%.3f',$xp).' '.sprintf('%.3f',$yp).' Td'; 
     4182     $this->objects[$this->currentContents]['c'] .= "\n".'BT '.sprintf('%.3F',$xp).' '.sprintf('%.3F',$yp).' Td'; 
    36504183     } else { 
    36514184     $a = deg2rad((float)$angle); 
    36524185     $tmp = "\n".'BT '; 
    3653      $tmp .= sprintf('%.3f',cos($a)).' '.sprintf('%.3f',(-1.0*sin($a))).' '.sprintf('%.3f',sin($a)).' '.sprintf('%.3f',cos($a)).' '; 
    3654      $tmp .= sprintf('%.3f',$xp).' '.sprintf('%.3f',$yp).' Tm'; 
     4186     $tmp .= sprintf('%.3F',cos($a)).' '.sprintf('%.3F',(-1.0*sin($a))).' '.sprintf('%.3F',sin($a)).' '.sprintf('%.3F',cos($a)).' '; 
     4187     $tmp .= sprintf('%.3F',$xp).' '.sprintf('%.3F',$yp).' Tm'; 
    36554188     $this->objects[$this->currentContents]['c'] .= $tmp; 
    36564189     } 
    36574190     if ($wordSpaceAdjust != 0 || $wordSpaceAdjust != $this->wordSpaceAdjust){ 
    36584191     $this->wordSpaceAdjust = $wordSpaceAdjust; 
    3659      $this->objects[$this->currentContents]['c'] .= ' '.sprintf('%.3f',$wordSpaceAdjust).' Tw'; 
     4192     $this->objects[$this->currentContents]['c'] .= ' '.sprintf('%.3F',$wordSpaceAdjust).' Tw'; 
    36604193     } 
    36614194     } 
     
    36694202    if  ($start < $len) { 
    36704203 
    3671       $part =  substr($text, $start); 
    3672  
    3673       $this->objects[$this->currentContents]['c'].=  ' /F'.$this->currentFontNum.' '.sprintf('%.1f', $size) .' Tf '; 
    3674  
    3675       $this->objects[$this->currentContents]['c'].=  ' ('.$this->filterText($part) .') Tj'; 
     4204      $part =  $text; // OAR - Don't need this anymore, given that $start always equals zero.  substr($text, $start); 
     4205 
     4206      $this->objects[$this->currentContents]['c'].=  ' /F'.$this->currentFontNum.' '.sprintf('%.1F', $size) .' Tf '; 
     4207 
     4208      $this->objects[$this->currentContents]['c'].=  ' ('.$this->filterText($part, false) .') Tj'; 
    36764209    } 
    36774210 
     
    36874220        $tmp =  $this->PRVTgetTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text); 
    36884221 
    3689         $info =  array('x' => $tmp[0], 'y' => $tmp[1], 'angle' => $angle, 'status' => 'eol', 'p' => $this->callback[$i]['p'], 'nCallback' => $this->callback[$i]['nCallback'], 'height' => $this->callback[$i]['height'], 'decender' => $this->callback[$i]['decender']); 
     4222        $info =  array('x' => $tmp[0], 'y' => $tmp[1], 'angle' => $angle, 'status' => 'eol', 'p' => $this->callback[$i]['p'], 'nCallback' => $this->callback[$i]['nCallback'], 'height' => $this->callback[$i]['height'], 'descender' => $this->callback[$i]['descender']); 
    36904223 
    36914224        $func =  $this->callback[$i]['f']; 
     
    37104243 
    37114244    if  (!$this->numFonts) { 
    3712  
    37134245      $this->selectFont('./fonts/Helvetica'); 
    37144246    } 
     
    37174249    // converts a number or a float to a string so it can get the width 
    37184250    $text =  "$text"; 
    3719  
    37204251 
    37214252    // hmm, this is where it all starts to get tricky - use the font information to 
     
    37234254    $w =  0; 
    37244255 
    3725     $len =  strlen($text); 
    3726  
    37274256    $cf =  $this->currentFont; 
    37284257 
    37294258    $space_scale =  1000 / $size; 
    37304259 
    3731     for  ($i =  0; $i < $len; $i++) { 
    3732  
    3733       //     $f = 1; 
    3734       //     $directive = 0; //$this->PRVTcheckTextDirective($text,$i,$f); 
    3735       //     if ($directive){ 
    3736       //       if ($f){ 
    3737       //         $this->setCurrentFont(); 
    3738       //         $cf = $this->currentFont; 
    3739       //       } 
    3740       //       $i = $i+$directive-1; 
    3741       //     } else { 
    3742       $char =  ord($text{$i}); 
    3743  
     4260    if ( $this->isUnicode) { 
     4261      // for Unicode, use the code points array to calculate width rather 
     4262      // than just the string itself 
     4263      $unicode =  $this->utf8toCodePointsArray($text); 
     4264 
     4265      foreach ($unicode as $char) { 
     4266        // check if we have to replace character 
    37444267      if  ( isset($this->fonts[$cf]['differences'][$char])) { 
    3745  
    3746  
    3747         // then this character is being replaced by another 
    3748         $name =  $this->fonts[$cf]['differences'][$char]; 
    3749  
    3750  
    3751         if  ( isset($this->fonts[$cf]['C'][$name]['WX'])) 
    3752           $w+=  $this->fonts[$cf]['C'][$name]['WX']; 
    3753       } else  if  (isset($this->fonts[$cf]['C'][$char]['WX'])) 
     4268          $char =  $this->fonts[$cf]['differences'][$char]; 
     4269        } 
     4270        // add the character width 
     4271        if  ( isset($this->fonts[$cf]['C'][$char]['WX'])) { 
     4272          $w+=  $this->fonts[$cf]['C'][$char]['WX']; 
     4273        } 
     4274        // add additional padding for space 
     4275        if  ( $char ==  32) {  // Space 
     4276          $w+=  $spacing * $space_scale; 
     4277        } 
     4278      } 
     4279 
     4280    } else { 
     4281 
     4282      $len =  mb_strlen($text); 
     4283 
     4284      for  ($i =  0; $i < $len; $i++) { 
     4285        $char =  ord($text{$i}); 
     4286        // check if we have to replace character 
     4287        if  ( isset($this->fonts[$cf]['differences'][$char])) { 
     4288          $char =  $this->fonts[$cf]['differences'][$char]; 
     4289        } 
     4290        // add the character width 
     4291        if  ( isset($this->fonts[$cf]['C'][$char]['WX'])) { 
    37544292        $w+=  $this->fonts[$cf]['C'][$char]['WX']; 
    3755  
    3756  
    3757       if  ( $char ==  32)  // Space 
     4293        } 
     4294        // add additional padding for space 
     4295        if  ( $char ==  32) {  // Space 
    37584296        $w+=  $spacing * $space_scale; 
    37594297    } 
    3760  
     4298      } 
     4299    } 
    37614300 
    37624301 
     
    38264365   */ 
    38274366  function  addTextWrap($x, $y, $width, $size, $text, $justification =  'left', $angle =  0, $test =  0) { 
     4367        // TODO - need to support Unicode 
     4368    if ($this->isUnicode) { 
     4369        die("addTextWrap does not support Unicode yet!"); 
     4370    } 
    38284371 
    38294372    // this will display the text, and if it goes beyond the width $width, will backtrack to the 
     
    38534396    $breakWidth =  0; 
    38544397 
    3855     $len =  strlen($text); 
     4398    $len =  mb_strlen($text); 
    38564399 
    38574400    $cf =  $this->currentFont; 
     
    39024445            if  ($text[$break] ==  ' ') { 
    39034446 
    3904               $tmp =  substr($text, 0, $break); 
     4447              $tmp =  mb_substr($text, 0, $break); 
    39054448            } else { 
    39064449 
    3907               $tmp =  substr($text, 0, $break+1); 
     4450              $tmp =  mb_substr($text, 0, $break+1); 
    39084451            } 
    39094452 
     
    39234466            } 
    39244467 
    3925             return  substr($text, $break+1); 
     4468            return  mb_substr($text, $break+1); 
    39264469          } else { 
    39274470 
    39284471            // just split before the current character 
    3929             $tmp =  substr($text, 0, $i); 
     4472            $tmp =  mb_substr($text, 0, $i); 
    39304473 
    39314474            $adjust =  0; 
     
    39524495            } 
    39534496 
    3954             return  substr($text, $i); 
     4497            return  mb_substr($text, $i); 
    39554498          } 
    39564499        } 
     
    42314774   */ 
    42324775  function  serializeObject($id) { 
    4233  
    42344776    if  ( array_key_exists($id,  $this->objects)) 
    42354777      return  var_export($this->objects[$id],  true); 
     
    43204862 
    43214863  /** 
     4864   * add a PNG image into the document, from a GD object 
     4865   * this should work with remote files 
     4866   */ 
     4867  function addImagePng($file, $x, $y, $w =  0, $h =  0, &$img) { 
     4868    //if already cached, need not to read again 
     4869        if ( isset($this->imagelist[$file]) ) { 
     4870          $data = null; 
     4871        } else { 
     4872          // Example for transparency handling on new image. Retain for current image 
     4873      // $tIndex = imagecolortransparent($img); 
     4874      // if ($tIndex > 0) { 
     4875      //   $tColor    = imagecolorsforindex($img, $tIndex); 
     4876      //   $new_tIndex    = imagecolorallocate($new_img, $tColor['red'], $tColor['green'], $tColor['blue']); 
     4877      //   imagefill($new_img, 0, 0, $new_tIndex); 
     4878      //   imagecolortransparent($new_img, $new_tIndex); 
     4879      // } 
     4880          // blending mode (literal/blending) on drawing into current image. not relevant when not saved or not drawn 
     4881          //imagealphablending($img, true); 
     4882          //default, but explicitely set to ensure pdf compatibility 
     4883      imagesavealpha($img, false); 
     4884       
     4885      $error =  0; 
     4886      //DEBUG_IMG_TEMP 
     4887      //debugpng 
     4888      if (DEBUGPNG) print '[addImagePng '.$file.']'; 
     4889 
     4890      ob_start(); 
     4891      @imagepng($img); 
     4892      //$data = ob_get_contents(); ob_end_clean(); 
     4893      $data = ob_get_clean(); 
     4894 
     4895      if ($data == '') { 
     4896        $error = 1; 
     4897        $errormsg = 'trouble writing file from GD'; 
     4898        //DEBUG_IMG_TEMP 
     4899        //debugpng 
     4900        if (DEBUGPNG) print 'trouble writing file from GD'; 
     4901          } 
     4902 
     4903      if  ($error) { 
     4904        $this->addMessage('PNG error - ('.$file.') '.$errormsg); 
     4905        return; 
     4906      } 
     4907    }  //End isset($this->imagelist[$file]) (png Duplicate removal) 
     4908 
     4909    $this->addPngFromBuf($file, $x, $y, $w, $h, $data); 
     4910  } 
     4911 
     4912 
     4913  /** 
    43224914   * add a PNG image into the document, from a file 
    43234915   * this should work with remote files 
    43244916   */ 
    43254917  function  addPngFromFile($file, $x, $y, $w =  0, $h =  0) { 
    4326  
    4327     // read in a png file, interpret it, then add to the system 
     4918    //if already cached, need not to read again 
     4919        if ( isset($this->imagelist[$file]) ) { 
     4920          $img = null; 
     4921        } else { 
     4922      //png files typically contain an alpha channel. 
     4923      //pdf file format or class.pdf does not support alpha blending. 
     4924      //on alpha blended images, more transparent areas have a color near black. 
     4925      //This appears in the result on not storing the alpha channel. 
     4926      //Correct would be the box background image or its parent when transparent. 
     4927      //But this would make the image dependent on the background. 
     4928      //Therefore create an image with white background and copy in 
     4929      //A more natural background than black is white. 
     4930      //Therefore create an empty image with white background and merge the 
     4931      //image in with alpha blending. 
     4932      $imgtmp = @imagecreatefrompng($file); 
     4933      if (!$imgtmp) { 
     4934        return; 
     4935      } 
     4936      $sx = imagesx($imgtmp); 
     4937      $sy = imagesy($imgtmp); 
     4938      $img = imagecreatetruecolor($sx,$sy); 
     4939      imagealphablending($img, true); 
     4940          $ti = imagecolortransparent($imgtmp); 
     4941          if ($ti >= 0) { 
     4942            $tc = imagecolorsforindex($imgtmp,$ti); 
     4943        $ti = imagecolorallocate($img,$tc['red'],$tc['green'],$tc['blue']); 
     4944        imagefill($img,0,0,$ti); 
     4945        imagecolortransparent($img, $ti); 
     4946      } else { 
     4947        imagefill($img,1,1,imagecolorallocate($img,255,255,255)); 
     4948      } 
     4949      imagecopy($img,$imgtmp,0,0,0,0,$sx,$sy); 
     4950      imagedestroy($imgtmp); 
     4951    } 
     4952    $this->addImagePng($file, $x, $y, $w, $h, $img); 
     4953  } 
     4954 
     4955 
     4956  /** 
     4957   * add a PNG image into the document, from a memory buffer of the file 
     4958   */ 
     4959  function  addPngFromBuf($file, $x, $y, $w =  0, $h =  0, &$data) { 
     4960 
     4961        if ( isset($this->imagelist[$file]) ) { 
     4962      //debugpng 
     4963      //if (DEBUGPNG) print '[addPngFromBuf Duplicate '.$file.']'; 
     4964          $data = null; 
     4965      $info['width'] = $this->imagelist[$file]['w']; 
     4966      $info['height'] = $this->imagelist[$file]['h']; 
     4967      $label = $this->imagelist[$file]['label']; 
     4968 
     4969        } else { 
     4970       
     4971      if ($data == null) { 
     4972        $this->addMessage('addPngFromBuf error - ('.$imgname.') data not present!'); 
     4973        return; 
     4974      } 
     4975      //debugpng 
     4976      //if (DEBUGPNG) print '[addPngFromBuf file='.$file.']'; 
    43284977    $error =  0; 
    43294978 
    4330     $tmp =  get_magic_quotes_runtime(); 
    4331  
    4332     set_magic_quotes_runtime(0); 
    4333  
    4334     if  ( ($data =  file_get_contents($file)) ===  false) { 
    4335  
    4336       //   $fp = @fopen($file,'rb'); 
    4337       //   if ($fp){ 
    4338       //     $data = ''; 
    4339       //     while(!feof($fp)){ 
    4340       //       $data .= fread($fp,1024); 
    4341       //     } 
    4342       //     fclose($fp); 
    4343       $error =  1; 
    4344  
    4345       $errormsg =  'trouble opening file: '.$file; 
    4346     } 
    4347  
    4348     set_magic_quotes_runtime($tmp); 
    4349  
    4350  
    43514979    if  (!$error) { 
    43524980 
    43534981      $header =  chr(137) .chr(80) .chr(78) .chr(71) .chr(13) .chr(10) .chr(26) .chr(10); 
    43544982 
    4355       if  (substr($data, 0, 8) !=  $header) { 
     4983      if  (mb_substr($data, 0, 8, '8bit') !=  $header) { 
    43564984 
    43574985        $error =  1; 
     4986 
     4987        //debugpng 
     4988        if (DEBUGPNG) print '[addPngFromFile this file does not have a valid header '.$file.']'; 
    43584989 
    43594990        $errormsg =  'this file does not have a valid header'; 
     
    43674998      $p =  8; 
    43684999 
    4369       $len =  strlen($data); 
     5000      $len =  mb_strlen($data, '8bit'); 
    43705001 
    43715002      // cycle through the file, identifying chunks 
     
    43825013        $chunkLen =  $this->PRVT_getBytes($data, $p, 4); 
    43835014 
    4384         $chunkType =  substr($data, $p+4, 4); 
     5015        $chunkType =  mb_substr($data, $p+4, 4, '8bit'); 
    43855016 
    43865017        //      echo $chunkType.' - '.$chunkLen.'<br>'; 
     
    44125043            $error =  1; 
    44135044 
     5045            //debugpng 
     5046            if (DEBUGPNG) print '[addPngFromFile unsupported compression method '.$file.']'; 
     5047 
    44145048            $errormsg =  'unsupported compression method'; 
    44155049          } 
     
    44195053            $error =  1; 
    44205054 
     5055            //debugpng 
     5056            if (DEBUGPNG) print '[addPngFromFile unsupported filter method '.$file.']'; 
     5057 
    44215058            $errormsg =  'unsupported filter method'; 
    44225059          } 
     
    44265063        case  'PLTE': 
    44275064 
    4428           $pdata.=  substr($data, $p+8, $chunkLen); 
     5065          $pdata.=  mb_substr($data, $p+8, $chunkLen, '8bit'); 
    44295066 
    44305067          break; 
     
    44325069        case  'IDAT': 
    44335070 
    4434           $idata.=  substr($data, $p+8, $chunkLen); 
     5071          $idata.=  mb_substr($data, $p+8, $chunkLen, '8bit'); 
    44355072 
    44365073          break; 
     
    44535090            $transparency['type'] =  'indexed'; 
    44545091 
    4455             $numPalette =  strlen($pdata) /3; 
     5092            $numPalette =  mb_strlen($pdata, '8bit') /3; 
    44565093 
    44575094            $trans =  0; 
     
    44955132            //unsupported transparency type 
    44965133 
     5134            //debugpng 
     5135            if (DEBUGPNG) print '[addPngFromFile unsupported transparency type '.$file.']'; 
    44975136          } 
    44985137 
     
    45145153        $error =  1; 
    45155154 
     5155        //debugpng 
     5156        if (DEBUGPNG) print '[addPngFromFile information header is missing '.$file.']'; 
     5157 
    45165158        $errormsg =  'information header is missing'; 
    45175159      } 
     
    45215163        $error =  1; 
    45225164 
     5165        //debugpng 
     5166        if (DEBUGPNG) print '[addPngFromFile no support for interlaced images in pdf '.$file.']'; 
     5167 
    45235168        $errormsg =  'There appears to be no support for interlaced images in pdf.'; 
    45245169      } 
     
    45305175      $error =  1; 
    45315176 
     5177      //debugpng 
     5178      if (DEBUGPNG) print '[addPngFromFile bit depth of 8 or less is supported '.$file.']'; 
     5179 
    45325180      $errormsg =  'only bit depth of 8 or less is supported'; 
    45335181    } 
     
    45395187 
    45405188        $error =  1; 
     5189 
     5190        //debugpng 
     5191        if (DEBUGPNG) print '[addPngFromFile alpha channel not supported: '.$info['colorType'].' '.$file.']'; 
    45415192 
    45425193        $errormsg =  'transparancey alpha channel not supported, transparency only supported for palette images.'; 
     
    45795230    } 
    45805231 
    4581     if  ($w ==  0) { 
     5232      //print_r($info); 
     5233      // so this image is ok... add it in. 
     5234      $this->numImages++; 
     5235 
     5236      $im =  $this->numImages; 
     5237 
     5238      $label =  'I'.$im; 
     5239 
     5240      $this->numObj++; 
     5241 
     5242      //  $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width'])); 
     5243      $options =  array('label' => $label, 'data' => $idata, 'bitsPerComponent' => $info['bitDepth'], 'pdata' => $pdata, 'iw' => $info['width'], 'ih' => $info['height'], 'type' => 'png', 'color' => $color, 'ncolor' => $ncolor); 
     5244 
     5245      if  (isset($transparency)) { 
     5246 
     5247        $options['transparency'] =  $transparency; 
     5248      } 
     5249 
     5250      $this->o_image($this->numObj, 'new', $options); 
     5251 
     5252      $this->imagelist[$file] = array('label' =>$label, 'w' => $info['width'], 'h' => $info['height']); 
     5253    } 
     5254 
     5255    if  ($w <=  0 && $h <=  0) { 
     5256      $w =  $info['width']; 
     5257      $h =  $info['height']; 
     5258    } 
     5259 
     5260    if  ($w <=  0) { 
    45825261 
    45835262      $w =  $h/$info['height']*$info['width']; 
    45845263    } 
    45855264 
    4586     if  ($h ==  0) { 
     5265    if  ($h <=  0) { 
    45875266 
    45885267      $h =  $w*$info['height']/$info['width']; 
    45895268    } 
    45905269 
    4591     //print_r($info); 
    4592     // so this image is ok... add it in. 
    4593     $this->numImages++; 
    4594  
    4595     $im =  $this->numImages; 
    4596  
    4597     $label =  'I'.$im; 
    4598  
    4599     $this->numObj++; 
    4600  
    4601     //  $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width'])); 
    4602     $options =  array('label' => $label, 'data' => $idata, 'bitsPerComponent' => $info['bitDepth'], 'pdata' => $pdata, 'iw' => $info['width'], 'ih' => $info['height'], 'type' => 'png', 'color' => $color, 'ncolor' => $ncolor); 
    4603  
    4604     if  (isset($transparency)) { 
    4605  
    4606       $options['transparency'] =  $transparency; 
    4607     } 
    4608  
    4609     $this->o_image($this->numObj, 'new', $options); 
    4610  
    4611  
    46125270    $this->objects[$this->currentContents]['c'].=  "\nq"; 
    46135271 
    4614     $this->objects[$this->currentContents]['c'].=  "\n".sprintf('%.3f', $w) ." 0 0 ".sprintf('%.3f', $h) ." ".sprintf('%.3f', $x) ." ".sprintf('%.3f', $y) ." cm"; 
     5272    $this->objects[$this->currentContents]['c'].=  "\n".sprintf('%.3F', $w) ." 0 0 ".sprintf('%.3F', $h) ." ".sprintf('%.3F', $x) ." ".sprintf('%.3F', $y) ." cm"; 
    46155273 
    46165274    $this->objects[$this->currentContents]['c'].=  "\n/".$label.' Do'; 
     
    46335291    } 
    46345292 
    4635  
    4636     $tmp =  getimagesize($img); 
    4637  
    4638     $imageWidth =  $tmp[0]; 
    4639  
    4640     $imageHeight =  $tmp[1]; 
    4641  
    4642  
    4643     if  (isset($tmp['channels'])) { 
    4644  
    4645       $channels =  $tmp['channels']; 
    4646     } else { 
    4647  
    4648       $channels =  3; 
    4649     } 
    4650  
     5293        if ( isset($this->imagelist[$img]) ) { 
     5294          $data = null; 
     5295      $imageWidth = $this->imagelist[$img]['w']; 
     5296      $imageHeight = $this->imagelist[$img]['h']; 
     5297      $channels =  $this->imagelist[$img]['c']; 
     5298        } else { 
     5299 
     5300      $tmp =  getimagesize($img); 
     5301 
     5302      $imageWidth =  $tmp[0]; 
     5303 
     5304      $imageHeight =  $tmp[1]; 
     5305 
     5306 
     5307      if  (isset($tmp['channels'])) { 
     5308 
     5309        $channels =  $tmp['channels']; 
     5310      } else { 
     5311 
     5312        $channels =  3; 
     5313      } 
     5314 
     5315 
     5316      //$fp = fopen($img,'rb'); 
     5317 
     5318      $data =  file_get_contents($img); 
     5319 
     5320      //fread($fp,filesize($img)); 
     5321 
     5322      //fclose($fp); 
     5323    } 
    46515324 
    46525325    if  ($w <=  0 &&  $h <=  0) { 
     
    46655338    } 
    46665339 
    4667  
    4668     //$fp = fopen($img,'rb'); 
    4669  
    4670     $tmp =  get_magic_quotes_runtime(); 
    4671  
    4672     set_magic_quotes_runtime(0); 
    4673  
    4674     $data =  file_get_contents($img); 
    4675  
    4676     //fread($fp,filesize($img)); 
    4677     set_magic_quotes_runtime($tmp); 
    4678  
    4679  
    4680     //fclose($fp); 
    4681  
    4682     $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels); 
     5340    $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels, $img); 
    46835341  } 
    46845342 
     
    46915349  function  addImage(&$img, $x, $y, $w =  0, $h =  0, $quality =  75) { 
    46925350 
     5351    /* Todo: 
     5352     * Pass in original filename as $imgname 
     5353     * If already cached like image_iscached(), allow empty $img 
     5354     * How to get w  and h in this case? 
     5355     * Then caller can check with image_iscached() whether generation of image is needed. 
     5356     * 
     5357     * But anyway, this function is not used! 
     5358     */ 
     5359    $imgname = tempnam($this->tmp, "cpdf_img_").'.jpeg'; 
     5360 
    46935361    // add a new image into the current location, as an external object 
    46945362    // add the image at $x,$y, and with width and height as defined by $w & $h 
     
    47275395    } 
    47285396 
    4729  
    47305397    // gotta get the data out of the img.. 
    4731  
    4732     // so I write to a temp file, and then read it back.. soo ugly, my apologies. 
    4733     $tmpDir =  '/tmp'; 
    4734  
    4735     $tmpName =  tempnam($tmpDir, 'img'); 
    4736  
    4737     imagejpeg($img, $tmpName, $quality); 
    4738  
    4739     //$fp = fopen($tmpName,'rb'); 
    4740  
    4741     $tmp =  get_magic_quotes_runtime(); 
    4742  
    4743     set_magic_quotes_runtime(0); 
    4744  
    4745     if  ( ($data =  file_get_contents($tmpName)) ===  false) { 
    4746  
    4747       //   $fp = @fopen($tmpName,'rb'); 
    4748       //   if ($fp){ 
    4749       //     $data = ''; 
    4750       //     while(!feof($fp)){ 
    4751       //       $data .= fread($fp,1024); 
    4752       //     } 
    4753       //     fclose($fp); 
    4754       $error =  1; 
    4755  
    4756       $errormsg =  'trouble opening file'; 
    4757     } 
    4758  
    4759     //  $data = fread($fp,filesize($tmpName)); 
    4760     set_magic_quotes_runtime($tmp); 
    4761  
    4762     //  fclose($fp); 
    4763     unlink($tmpName); 
    4764  
    4765     $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight); 
     5398    ob_start(); 
     5399    imagejpeg($img, '', $quality); 
     5400    //$data = ob_get_contents(); ob_end_clean(); 
     5401    $data = ob_get_clean(); 
     5402 
     5403    $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $imgname); 
     5404  } 
     5405 
     5406 
     5407  /* Check if image already added to pdf image directory. 
     5408   * If yes, need not to create again (pass empty data) 
     5409   */ 
     5410  function  image_iscached($imgname) { 
     5411    return isset($this->imagelist[$imgname]); 
    47665412  } 
    47675413 
     
    47725418   * @access private 
    47735419   */ 
    4774   function  addJpegImage_common(&$data, $x, $y, $w =  0, $h =  0, $imageWidth, $imageHeight, $channels =  3) { 
    4775  
    4776     // note that this function is not to be called externally 
    4777     // it is just the common code between the GD and the file options 
    4778     $this->numImages++; 
    4779  
    4780     $im =  $this->numImages; 
    4781  
    4782     $label =  'I'.$im; 
    4783  
    4784     $this->numObj++; 
    4785  
    4786     $this->o_image($this->numObj, 'new', array('label' => $label, 'data' => &$data, 'iw' => $imageWidth, 'ih' => $imageHeight, 'channels' => $channels)); 
     5420  function  addJpegImage_common(&$data, $x, $y, $w =  0, $h =  0, $imageWidth, $imageHeight, $channels =  3, $imgname) { 
     5421 
     5422    if ( isset($this->imagelist[$imgname]) ) { 
     5423      $label = $this->imagelist[$imgname]['label']; 
     5424      //debugpng 
     5425      //if (DEBUGPNG) print '[addJpegImage_common Duplicate '.$imgname.']'; 
     5426 
     5427    } else { 
     5428 
     5429      if ($data == null) { 
     5430        $this->addMessage('addJpegImage_common error - ('.$imgname.') data not present!'); 
     5431        return; 
     5432      } 
     5433 
     5434      // note that this function is not to be called externally 
     5435      // it is just the common code between the GD and the file options 
     5436      $this->numImages++; 
     5437 
     5438      $im =  $this->numImages; 
     5439 
     5440      $label =  'I'.$im; 
     5441 
     5442      $this->numObj++; 
     5443 
     5444      $this->o_image($this->numObj, 'new', array('label' => $label, 'data' => &$data, 'iw' => $imageWidth, 'ih' => $imageHeight, 'channels' => $channels)); 
     5445 
     5446      $this->imagelist[$imgname] = array('label' =>$label, 'w' => $imageWidth, 'h' => $imageHeight, 'c'=> $channels ); 
     5447    } 
    47875448 
    47885449 
    47895450    $this->objects[$this->currentContents]['c'].=  "\nq"; 
    47905451 
    4791     $this->objects[$this->currentContents]['c'].=  "\n".sprintf('%.3f', $w) ." 0 0 ".sprintf('%.3f', $h) ." ".sprintf('%.3f', $x) ." ".sprintf('%.3f', $y) ." cm"; 
     5452    $this->objects[$this->currentContents]['c'].=  "\n".sprintf('%.3F', $w) ." 0 0 ".sprintf('%.3F', $h) ." ".sprintf('%.3F', $x) ." ".sprintf('%.3F', $y) ." cm"; 
    47925453 
    47935454    $this->objects[$this->currentContents]['c'].=  "\n/".$label.' Do'; 
     
    48775538      // the user is trying to set a font family 
    48785539      // note that this can also be used to set the base ones to something else 
    4879       if  (strlen($family)) { 
     5540      if  (mb_strlen($family)) { 
    48805541 
    48815542        $this->fontFamilies[$family] =  $options; 
Note: See TracChangeset for help on using the changeset viewer.