Changeset 1654 for sandbox/filemanager/tp/dompdf/lib/class.pdf.php
- Timestamp:
- 11/17/09 09:02:41 (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
sandbox/filemanager/tp/dompdf/lib/class.pdf.php
r1575 r1654 11 11 * simplify the creation of documents. 12 12 * 13 * Extended by Orion Richardson to support Unicode / UTF-8 characters using 14 * TCPDF and others as a guide. 15 * 13 16 * IMPORTANT NOTE 14 17 * there is no warranty, implied or otherwise with this software. … … 18 21 * 19 22 * @author Wayne Munro <pdf@ros.co.nz> 23 * @contributor Orion Richardson <orionr@yahoo.com> 24 * @contributor Helmut Tischer <htischer@weihenstephan.org> 20 25 * @version 009 21 26 * @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 22 44 */ 23 45 class Cpdf { … … 187 209 */ 188 210 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 = ''; 189 228 190 229 /** … … 246 285 public $checkpoint = ''; 247 286 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 248 297 /** 249 298 * class constructor 250 299 * this will start a new document 251 300 * @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; 254 310 255 311 $this->newDocument($pageSize); … … 333 389 $tmp = $o['info']; 334 390 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"; 336 392 337 393 return $res; … … 638 694 } 639 695 640 $res.= " 696 $res.= "\n>>"; 641 697 } 642 698 … … 650 706 } 651 707 652 $res.= " 708 $res.= "\n>>"; 653 709 } 654 710 … … 662 718 } 663 719 664 $res.= " 720 $res.= "\n>>"; 665 721 } 666 722 … … 672 728 $tmp = $o['info']['mediaBox']; 673 729 674 $res.= "\n/MediaBox [".sprintf('%.3 f', $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]) .']'; 675 731 } 676 732 } … … 753 809 case 'new': 754 810 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')); 756 812 757 813 $fontNum = $this->numFonts; … … 798 854 } 799 855 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 800 901 // also tell the pages node about the new font 801 902 $this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id)); … … 834 935 } 835 936 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 836 943 break; 837 944 … … 839 946 case 'out': 840 947 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 { 841 970 $res = "\n".$id." 0 obj\n<< /Type /Font\n/Subtype /".$o['info']['SubType']."\n"; 842 971 … … 875 1004 } 876 1005 877 $res.= ">>\nendobj"; 1006 $res.= ">>\n"; 1007 $res.= "endobj"; 1008 1009 } 878 1010 879 1011 return $res; … … 936 1068 case 'CharSet': 937 1069 938 if ( strlen($value)) {1070 if (mb_strlen($value)) { 939 1071 940 1072 $res.= '/'.$label.' '.$value."\n"; … … 1037 1169 1038 1170 /** 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 /** 1039 1329 * the document procset, solves some problems with printing to old PS printers 1040 1330 */ … … 1152 1442 $res.= '/'.$k.' ('; 1153 1443 1444 // dates must be outputted as-is, without Unicode transformations 1445 $raw = ($k == 'CreationDate' || $k == 'ModDate'); 1446 $c = $v; 1447 1154 1448 if ($this->encrypted) { 1155 1449 1156 $res.= $this->filterText($this->ARC4($v)); 1157 } else { 1158 1159 $res.= $this->filterText($v); 1450 $c = $this->ARC4($c); 1160 1451 } 1452 1453 $res.= ($raw) ? $c : $this->filterText($c); 1161 1454 1162 1455 $res.= ")\n"; … … 1319 1612 foreach($o['info']['rect'] as $v) { 1320 1613 1321 $res.= sprintf("%.4 f", $v);1614 $res.= sprintf("%.4F ", $v); 1322 1615 } 1323 1616 … … 1469 1762 $this->objects[$id] = array('t'=>'contents', 'c'=>'', 'info'=>array()); 1470 1763 1471 if ( strlen($options) && intval($options)) {1764 if (mb_strlen($options) && intval($options)) { 1472 1765 1473 1766 // then this contents is the primary for a page … … 1490 1783 1491 1784 case 'out': 1492 1493 1785 $tmp = $o['c']; 1494 1786 … … 1522 1814 } 1523 1815 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"; 1528 1820 1529 1821 return $res; … … 1540 1832 1541 1833 if ($action != 'new') { 1542 1543 1834 $o = & $this->objects[$id]; 1544 1835 } … … 1589 1880 1590 1881 $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) .' '; 1594 1885 1595 1886 $this->numObj++; … … 1626 1917 pre_r($tmp); 1627 1918 break; 1628 1919 1629 1920 } 1630 1921 } … … 1651 1942 $this->objects[$id]['info']['Mask'] = $tmp; 1652 1943 break; 1653 1944 1654 1945 } 1655 1946 } … … 1687 1978 } 1688 1979 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"; 1690 1981 1691 1982 return $res; … … 1765 2056 $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); 1766 2057 1767 $len = strlen($options['owner']);2058 $len = mb_strlen($options['owner']); 1768 2059 1769 2060 if ($len>32) { … … 1778 2069 } 1779 2070 1780 $len = strlen($options['user']);2071 $len = mb_strlen($options['user']); 1781 2072 1782 2073 if ($len>32) { … … 1843 2134 $res.= "\n/P ".($o['info']['p']); 1844 2135 1845 $res.= "\n>>\nendobj \n";2136 $res.= "\n>>\nendobj"; 1846 2137 1847 2138 … … 1885 2176 $hex = dechex($id); 1886 2177 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; 1890 2181 } 1891 2182 … … 1906 2197 1907 2198 // setup the control array 1908 if ( strlen($key) == 0) {2199 if (mb_strlen($key) == 0) { 1909 2200 1910 2201 return; … … 1913 2204 $k = ''; 1914 2205 1915 while ( strlen($k) <256) {2206 while (mb_strlen($k) <256) { 1916 2207 1917 2208 $k.= $key; … … 1945 2236 function ARC4($text) { 1946 2237 1947 $len = strlen($text);2238 $len = mb_strlen($text); 1948 2239 1949 2240 $a = 0; … … 2037 2328 $this->numObj++; 2038 2329 2039 if ( strlen($ownerPass) == 0) {2330 if (mb_strlen($ownerPass) == 0) { 2040 2331 2041 2332 $ownerPass = $userPass; … … 2061 2352 2062 2353 if ($debug) { 2063 2064 2354 // turn compression off 2065 2355 $this->options['compression'] = 0; … … 2078 2368 $xref = array(); 2079 2369 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); 2084 2372 2085 2373 foreach($this->objects as $k=>$v) { … … 2093 2381 $xref[] = $pos; 2094 2382 2095 $pos+= strlen($cont);2383 $pos+= mb_strlen($cont); 2096 2384 } 2097 2385 … … 2111 2399 } 2112 2400 2113 if ( strlen($this->fileIdentifier)) {2401 if (mb_strlen($this->fileIdentifier)) { 2114 2402 2115 2403 $content.= "/ID[<".$this->fileIdentifier."><".$this->fileIdentifier.">]\n"; … … 2188 2476 function openFont($font) { 2189 2477 2190 // assume that $font contains both the path and perhaps the extension to the file, split them2478 // assume that $font contains the path and file but not the extension 2191 2479 $pos = strrpos($font, '/'); 2192 2480 … … 2202 2490 $name = substr($font, $pos+1); 2203 2491 } 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 2211 2504 $this->addMessage('openFont: '.$font.' - '.$name); 2212 2505 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); 2218 2514 2219 2515 eval($tmp); … … 2228 2524 } 2229 2525 2230 if (!isset($this->fonts[$font]) && file_exists($dir .$name.'.afm')) {2526 if (!isset($this->fonts[$font]) && file_exists($dir . $metrics_name)) { 2231 2527 2232 2528 // 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); 2234 2530 2235 2531 $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); 2238 2538 2239 2539 foreach ($file as $rowA) { … … 2286 2586 case 'StartCharMetrics': 2287 2587 2588 case 'FontHeightOffset': // OAR - Added so we can offset the height calculation of a Windows font. Otherwise it's too big. 2589 2288 2590 $data[$key] = trim(substr($row, $pos)); 2289 2591 … … 2296 2598 break; 2297 2599 2298 case 'C': 2600 case 'C': // Found in AFM files 2299 2601 2300 2602 //C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ; … … 2307 2609 $bits2 = explode(' ', trim($bit)); 2308 2610 2309 if ( strlen($bits2[0])) {2611 if (mb_strlen($bits2[0])) { 2310 2612 2311 2613 if (count($bits2) >2) { … … 2324 2626 } 2325 2627 2326 if ($dtmp['C'] >= 0) { 2628 $cc = (int)$dtmp['C']; 2629 if ($cc >= 0) { 2327 2630 2328 2631 $data['C'][$dtmp['C']] = $dtmp; … … 2332 2635 2333 2636 $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 } 2334 2696 } 2335 2697 … … 2348 2710 } 2349 2711 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 2350 2723 $data['_version_'] = 1; 2351 2724 2352 2725 $this->fonts[$font] = $data; 2353 2726 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); 2362 2740 } 2363 2741 … … 2371 2749 * 2372 2750 */ 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 } 2374 2757 2375 2758 if (!isset($this->fonts[$fontName])) { 2759 $this->addMessage("selectFont: selecting - $fontName - $encoding, $set"); 2376 2760 2377 2761 // load the file … … 2390 2774 $name = substr($fontName, $pos+1); 2391 2775 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); 2399 2777 2400 2778 if (is_array($encoding)) { … … 2410 2788 $options['differences'] = $encoding['differences']; 2411 2789 } 2412 } else if ( strlen($encoding)) {2790 } else if (mb_strlen($encoding)) { 2413 2791 2414 2792 // then perhaps only the encoding has been set … … 2427 2805 // should be for all non-basic fonts), then load it into an object and put the 2428 2806 // references into the font object 2429 $basefile = substr($fontName, 0, strlen($fontName) -4); 2430 2807 $basefile = $fontName; 2431 2808 if (file_exists($basefile.'.pfb')) { 2432 2809 … … 2448 2825 2449 2826 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)) { 2452 2830 $adobeFontName = $this->fonts[$fontName]['FontName']; 2453 2831 … … 2455 2833 $this->addMessage('selectFont: adding font file - '.$fbfile.' - '.$adobeFontName); 2456 2834 2457 // find the array of fon dwidths, and put that into an object.2835 // find the array of font widths, and put that into an object. 2458 2836 $firstChar = -1; 2459 2837 … … 2461 2839 2462 2840 $widths = array(); 2841 $cid_widths = array(); 2463 2842 2464 2843 foreach ($this->fonts[$fontName]['C'] as $num => $d) { … … 2466 2845 if (intval($num) >0 || $num == '0') { 2467 2846 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 } 2473 2855 } 2474 2856 } … … 2476 2858 $widths[] = $d['WX']; 2477 2859 2860 if ($this->isUnicode) { 2861 $cid_widths[$num] = $d['WX']; 2862 } 2863 2478 2864 if ($firstChar == -1) { 2479 2480 2865 $firstChar = $num; 2481 2866 } … … 2492 2877 if ($charNum > $lastChar) { 2493 2878 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 } 2497 2885 } 2498 2886 … … 2503 2891 2504 2892 $widths[$charNum-$firstChar] = $this->fonts[$fontName]['C'][$charName]['WX']; 2893 if ($this->isUnicode) { 2894 $cid_widths[$charName] = $this->fonts[$fontName]['C'][$charName]['WX']; 2895 } 2505 2896 } 2506 2897 } 2507 2898 } 2508 2899 2900 if ($this->isUnicode) { 2901 $this->fonts[$fontName]['CIDWidths'] = $cid_widths; 2902 } 2903 2509 2904 $this->addMessage('selectFont: FirstChar = '.$firstChar); 2510 2905 2511 2906 $this->addMessage('selectFont: LastChar = '.$lastChar); 2512 2907 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; 2522 2926 } 2523 2927 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 } 2528 2942 2529 2943 // load the pfb file, and put that into an object too. 2530 2944 // note that pdf supports only binary format type 1 font files, though there is a 2531 2945 // simple utility to convert them from pfa to pfb. 2532 $tmp = get_magic_quotes_runtime();2533 2534 set_magic_quotes_runtime(0);2535 2536 2946 $data = file_get_contents($fbfile); 2537 2538 set_magic_quotes_runtime($tmp);2539 2947 2540 2948 … … 2559 2967 } 2560 2968 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); 2569 2974 2570 2975 foreach($list as $k => $v) { … … 2600 3005 $l2 = strpos($data, '00000000') -$l1; 2601 3006 2602 $l3 = strlen($data) -$l2-$l1;3007 $l3 = mb_strlen($data) -$l2-$l1; 2603 3008 2604 3009 $this->o_contents($this->numObj, 'add', array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3)); 2605 3010 } else if ($fbtype == 'ttf') { 2606 3011 2607 $l1 = strlen($data);3012 $l1 = mb_strlen($data); 2608 3013 2609 3014 $this->o_contents($this->numObj, 'add', array('Length1' => $l1)); … … 2613 3018 2614 3019 // 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); 2616 3021 2617 3022 if ($fbtype == 'ttf') { … … 2735 3140 if ($r >= 0 && ($force || $r != $this->currentColour['r'] || $g != $this->currentColour['g'] || $b != $this->currentColour['b'])) { 2736 3141 2737 $this->objects[$this->currentContents]['c'].= "\n".sprintf('%.3 f', $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'; 2738 3143 2739 3144 $this->currentColour = array('r' => $r, 'g' => $g, 'b' => $b); … … 2749 3154 if ($r >= 0 && ($force || $r != $this->currentStrokeColour['r'] || $g != $this->currentStrokeColour['g'] || $b != $this->currentStrokeColour['b'])) { 2750 3155 2751 $this->objects[$this->currentContents]['c'].= "\n".sprintf('%.3 f', $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'; 2752 3157 2753 3158 $this->currentStrokeColour = array('r' => $r, 'g' => $g, 'b' => $b); … … 2813 3218 * 2814 3219 * @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 2816 3221 */ 2817 3222 function setFillTransparency($mode, $opacity) { … … 2840 3245 2841 3246 $this->objects[$this->currentContents]['c'] .= 2842 "\n".sprintf('%.3 f', $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'; 2843 3248 } 2844 3249 … … 2852 3257 // as the control points for the curve. 2853 3258 $this->objects[$this->currentContents]['c'] .= 2854 "\n".sprintf('%.3 f', $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); 2855 3260 2856 3261 $this->objects[$this->currentContents]['c'] .= 2857 ' '.sprintf('%.3 f', $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'; 2858 3263 } 2859 3264 … … 2919 3324 2920 3325 $tmp = "\n q "; 2921 $tmp .= sprintf('%.3 f', cos($a)) .' '.sprintf('%.3f', (-1.0*sin($a))) .' '.sprintf('%.3f', sin($a)) .' '.sprintf('%.3f', cos($a)) .' ';2922 $tmp .= sprintf('%.3 f', $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'; 2923 3328 2924 3329 $this->objects[$this->currentContents]['c'].= $tmp; … … 2936 3341 2937 3342 2938 $this->objects[$this->currentContents]['c'] .= "\n".sprintf('%.3 f', $a0) .' '.sprintf('%.3f', $b0) .' m ';3343 $this->objects[$this->currentContents]['c'] .= "\n".sprintf('%.3F', $a0) .' '.sprintf('%.3F', $b0) .' m '; 2939 3344 2940 3345 for ($i = 1; $i <= $nSeg; $i++) { … … 2952 3357 2953 3358 $this->objects[$this->currentContents]['c'] 2954 .= "\n".sprintf('%.3 f', ($a0+$c0*$dtm)) .' '.sprintf('%.3f', ($b0 + $d0 * $dtm));3359 .= "\n".sprintf('%.3F', ($a0+$c0*$dtm)) .' '.sprintf('%.3F', ($b0 + $d0 * $dtm)); 2955 3360 2956 3361 $this->objects[$this->currentContents]['c'] .= 2957 ' '.sprintf('%.3 f', ($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'; 2958 3363 2959 3364 $a0 = $a1; … … 3051 3456 $this->objects[$this->currentContents]['c'].= "\n"; 3052 3457 3053 $this->objects[$this->currentContents]['c'].= sprintf('%.3 f', $p[0]) .' '.sprintf('%.3f', $p[1]) .' m ';3458 $this->objects[$this->currentContents]['c'].= sprintf('%.3F', $p[0]) .' '.sprintf('%.3F', $p[1]) .' m '; 3054 3459 3055 3460 for ($i = 2; $i < $np * 2; $i = $i + 2) { 3056 3461 3057 $this->objects[$this->currentContents]['c'].= sprintf('%.3 f', $p[$i]) .' '.sprintf('%.3f', $p[$i+1]) .' l ';3462 $this->objects[$this->currentContents]['c'].= sprintf('%.3F', $p[$i]) .' '.sprintf('%.3F', $p[$i+1]) .' l '; 3058 3463 } 3059 3464 … … 3074 3479 function filledRectangle($x1, $y1, $width, $height) { 3075 3480 3076 $this->objects[$this->currentContents]['c'].= "\n".sprintf('%.3 f', $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'; 3077 3482 } 3078 3483 … … 3084 3489 function rectangle($x1, $y1, $width, $height) { 3085 3490 3086 $this->objects[$this->currentContents]['c'].= "\n".sprintf('%.3 f', $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'; 3087 3492 } 3088 3493 … … 3145 3550 3146 3551 // if there is a line style set, then put this in too 3147 if ( strlen($this->currentLineStyle)) {3552 if (mb_strlen($this->currentLineStyle)) { 3148 3553 3149 3554 $this->objects[$this->currentContents]['c'].= "\n".$this->currentLineStyle; … … 3193 3598 header("Content-type: application/pdf"); 3194 3599 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)); 3196 3602 $fileName = (isset($options['Content-Disposition']) ? $options['Content-Disposition'] : 'file.pdf'); 3197 3603 … … 3207 3613 3208 3614 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)); 3211 3617 } 3212 3618 … … 3223 3629 3224 3630 if (!$this->numFonts) { 3225 3226 3631 $this->selectFont('./fonts/Helvetica'); 3227 3632 } 3228 3633 3229 3634 // for the current font, and the given size, what is the height of the font in user units 3230 3635 $h = $this->fonts[$this->currentFont]['FontBBox'][3]-$this->fonts[$this->currentFont]['FontBBox'][1]; 3231 3636 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 3232 3651 return $size*$h/1000; 3233 3652 } … … 3235 3654 3236 3655 /** 3237 * return the font de cender, this will normally return a negative number3656 * return the font descender, this will normally return a negative number 3238 3657 * if you add this number to the baseline, you get the level of the bottom of the font 3239 3658 * it is in the pdf user units 3240 3659 */ 3241 function getFontDe cender($size) {3660 function getFontDescender($size) { 3242 3661 3243 3662 // note that this will most likely return a negative value 3244 3663 if (!$this->numFonts) { 3245 3246 3664 $this->selectFont('./fonts/Helvetica'); 3247 3665 } 3248 3666 3249 $h = $this->fonts[$this->currentFont]['FontBBox'][1]; 3667 //$h = $this->fonts[$this->currentFont]['FontBBox'][1]; 3668 $h = $this->fonts[$this->currentFont]['Descender']; 3250 3669 3251 3670 return $size*$h/1000; … … 3259 3678 * @access private 3260 3679 */ 3261 function filterText($text) { 3262 3263 $search = array("\\", "(", ")", "<", ">", "'", """, "&"); 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')); 3270 3695 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; 3271 3805 } 3272 3806 … … 3340 3874 $j++; 3341 3875 3342 if ( strlen($text) <= $j) {3876 if (mb_strlen($text) <= $j) { 3343 3877 3344 3878 return $directive; … … 3355 3889 if ($text[$j] == '>') { 3356 3890 3357 $p = strrpos($this->currentTextState, $text[$j-1]);3891 $p = mb_strrpos($this->currentTextState, $text[$j-1]); 3358 3892 3359 3893 if ($p !== false) { 3360 3894 3361 3895 // 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); 3363 3897 } 3364 3898 … … 3373 3907 $j++; 3374 3908 3375 $k = strpos($text, '>', $j);3909 $k = mb_strpos($text, '>', $j); 3376 3910 3377 3911 if ($k !== false && $text[$j] == ':') { … … 3383 3917 3384 3918 // 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, ':'); 3388 3922 3389 3923 if ($b1 !== false) { 3390 3924 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); 3394 3928 } else { 3395 3929 … … 3399 3933 } 3400 3934 3401 if (!isset($func) || ! strlen(trim($func))) {3935 if (!isset($func) || !mb_strlen(trim($func))) { 3402 3936 3403 3937 $directive = 0; … … 3409 3943 // need to assess the text position, calculate the text width to this point 3410 3944 // 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)); 3412 3946 3413 3947 $info = array('x' => $tmp[0], 'y' => $tmp[1], 'angle' => $angle, 'status' => 'end', 'p' => $parm, 'nCallback' => $this->nCallback); … … 3478 4012 $j++; 3479 4013 3480 $k = strpos($text, '>', $j);4014 $k = mb_strpos($text, '>', $j); 3481 4015 3482 4016 if ($k !== false && $text[$j] == ':') { … … 3489 4023 // split the remainder on colons to get the function name and the paramater 3490 4024 // $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, ':'); 3494 4028 3495 4029 if ($b1 !== false) { 3496 4030 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); 3500 4034 } else { 3501 4035 … … 3505 4039 } 3506 4040 3507 if (!isset($func) || ! strlen(trim($func))) {4041 if (!isset($func) || !mb_strlen(trim($func))) { 3508 4042 3509 4043 $directive = 0; … … 3515 4049 // need to assess the text position, calculate the text width to this point 3516 4050 // can use getTextWidth to find the text width I think 3517 // also add the text height and de cender3518 $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), 'de cender' => $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)); 3521 4055 3522 4056 $x = $tmp[0]; … … 3569 4103 */ 3570 4104 function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0) { 3571 3572 4105 if (!$this->numFonts) { 3573 4106 $this->selectFont('./fonts/Helvetica'); … … 3588 4121 'nCallback' => $this->callback[$i]['nCallback'], 3589 4122 'height' => $this->callback[$i]['height'], 3590 'de cender' => $this->callback[$i]['decender']);4123 'descender' => $this->callback[$i]['descender']); 3591 4124 3592 4125 $func = $this->callback[$i]['f']; … … 3598 4131 if ($angle == 0) { 3599 4132 3600 $this->objects[$this->currentContents]['c'].= "\n".'BT '.sprintf('%.3 f', $x) .' '.sprintf('%.3f', $y) .' Td';4133 $this->objects[$this->currentContents]['c'].= "\n".'BT '.sprintf('%.3F', $x) .' '.sprintf('%.3F', $y) .' Td'; 3601 4134 3602 4135 } else { … … 3606 4139 $tmp = "\n".'BT '; 3607 4140 3608 $tmp.= sprintf('%.3 f', cos($a)) .' '.sprintf('%.3f', (-1.0*sin($a))) .' '.sprintf('%.3f', sin($a)) .' '.sprintf('%.3f', cos($a)) .' ';3609 3610 $tmp.= sprintf('%.3 f', $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'; 3611 4144 3612 4145 $this->objects[$this->currentContents]['c'].= $tmp; … … 3617 4150 $this->wordSpaceAdjust = $wordSpaceAdjust; 3618 4151 3619 $this->objects[$this->currentContents]['c'].= ' '.sprintf('%.3 f', $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); 3623 4156 3624 4157 $start = 0; … … 3631 4164 // then we should write what we need to 3632 4165 if ($i>$start){ 3633 $part = substr($text,$start,$i-$start);3634 $this->objects[$this->currentContents]['c'] .= ' /F'.$this->currentFontNum.' '.sprintf('%.1 f',$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'; 3636 4169 } 3637 4170 if ($f){ … … 3647 4180 // restart the text object 3648 4181 if ($angle == 0){ 3649 $this->objects[$this->currentContents]['c'] .= "\n".'BT '.sprintf('%.3 f',$xp).' '.sprintf('%.3f',$yp).' Td';4182 $this->objects[$this->currentContents]['c'] .= "\n".'BT '.sprintf('%.3F',$xp).' '.sprintf('%.3F',$yp).' Td'; 3650 4183 } else { 3651 4184 $a = deg2rad((float)$angle); 3652 4185 $tmp = "\n".'BT '; 3653 $tmp .= sprintf('%.3 f',cos($a)).' '.sprintf('%.3f',(-1.0*sin($a))).' '.sprintf('%.3f',sin($a)).' '.sprintf('%.3f',cos($a)).' ';3654 $tmp .= sprintf('%.3 f',$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'; 3655 4188 $this->objects[$this->currentContents]['c'] .= $tmp; 3656 4189 } 3657 4190 if ($wordSpaceAdjust != 0 || $wordSpaceAdjust != $this->wordSpaceAdjust){ 3658 4191 $this->wordSpaceAdjust = $wordSpaceAdjust; 3659 $this->objects[$this->currentContents]['c'] .= ' '.sprintf('%.3 f',$wordSpaceAdjust).' Tw';4192 $this->objects[$this->currentContents]['c'] .= ' '.sprintf('%.3F',$wordSpaceAdjust).' Tw'; 3660 4193 } 3661 4194 } … … 3669 4202 if ($start < $len) { 3670 4203 3671 $part = substr($text, $start);3672 3673 $this->objects[$this->currentContents]['c'].= ' /F'.$this->currentFontNum.' '.sprintf('%.1 f', $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'; 3676 4209 } 3677 4210 … … 3687 4220 $tmp = $this->PRVTgetTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text); 3688 4221 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'], 'de cender' => $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']); 3690 4223 3691 4224 $func = $this->callback[$i]['f']; … … 3710 4243 3711 4244 if (!$this->numFonts) { 3712 3713 4245 $this->selectFont('./fonts/Helvetica'); 3714 4246 } … … 3717 4249 // converts a number or a float to a string so it can get the width 3718 4250 $text = "$text"; 3719 3720 4251 3721 4252 // hmm, this is where it all starts to get tricky - use the font information to … … 3723 4254 $w = 0; 3724 4255 3725 $len = strlen($text);3726 3727 4256 $cf = $this->currentFont; 3728 4257 3729 4258 $space_scale = 1000 / $size; 3730 4259 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 3744 4267 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'])) { 3754 4292 $w+= $this->fonts[$cf]['C'][$char]['WX']; 3755 3756 3757 if ( $char == 32)// Space4293 } 4294 // add additional padding for space 4295 if ( $char == 32) { // Space 3758 4296 $w+= $spacing * $space_scale; 3759 4297 } 3760 4298 } 4299 } 3761 4300 3762 4301 … … 3826 4365 */ 3827 4366 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 } 3828 4371 3829 4372 // this will display the text, and if it goes beyond the width $width, will backtrack to the … … 3853 4396 $breakWidth = 0; 3854 4397 3855 $len = strlen($text);4398 $len = mb_strlen($text); 3856 4399 3857 4400 $cf = $this->currentFont; … … 3902 4445 if ($text[$break] == ' ') { 3903 4446 3904 $tmp = substr($text, 0, $break);4447 $tmp = mb_substr($text, 0, $break); 3905 4448 } else { 3906 4449 3907 $tmp = substr($text, 0, $break+1);4450 $tmp = mb_substr($text, 0, $break+1); 3908 4451 } 3909 4452 … … 3923 4466 } 3924 4467 3925 return substr($text, $break+1);4468 return mb_substr($text, $break+1); 3926 4469 } else { 3927 4470 3928 4471 // just split before the current character 3929 $tmp = substr($text, 0, $i);4472 $tmp = mb_substr($text, 0, $i); 3930 4473 3931 4474 $adjust = 0; … … 3952 4495 } 3953 4496 3954 return substr($text, $i);4497 return mb_substr($text, $i); 3955 4498 } 3956 4499 } … … 4231 4774 */ 4232 4775 function serializeObject($id) { 4233 4234 4776 if ( array_key_exists($id, $this->objects)) 4235 4777 return var_export($this->objects[$id], true); … … 4320 4862 4321 4863 /** 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 /** 4322 4914 * add a PNG image into the document, from a file 4323 4915 * this should work with remote files 4324 4916 */ 4325 4917 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.']'; 4328 4977 $error = 0; 4329 4978 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 4351 4979 if (!$error) { 4352 4980 4353 4981 $header = chr(137) .chr(80) .chr(78) .chr(71) .chr(13) .chr(10) .chr(26) .chr(10); 4354 4982 4355 if ( substr($data, 0, 8) != $header) {4983 if (mb_substr($data, 0, 8, '8bit') != $header) { 4356 4984 4357 4985 $error = 1; 4986 4987 //debugpng 4988 if (DEBUGPNG) print '[addPngFromFile this file does not have a valid header '.$file.']'; 4358 4989 4359 4990 $errormsg = 'this file does not have a valid header'; … … 4367 4998 $p = 8; 4368 4999 4369 $len = strlen($data);5000 $len = mb_strlen($data, '8bit'); 4370 5001 4371 5002 // cycle through the file, identifying chunks … … 4382 5013 $chunkLen = $this->PRVT_getBytes($data, $p, 4); 4383 5014 4384 $chunkType = substr($data, $p+4, 4);5015 $chunkType = mb_substr($data, $p+4, 4, '8bit'); 4385 5016 4386 5017 // echo $chunkType.' - '.$chunkLen.'<br>'; … … 4412 5043 $error = 1; 4413 5044 5045 //debugpng 5046 if (DEBUGPNG) print '[addPngFromFile unsupported compression method '.$file.']'; 5047 4414 5048 $errormsg = 'unsupported compression method'; 4415 5049 } … … 4419 5053 $error = 1; 4420 5054 5055 //debugpng 5056 if (DEBUGPNG) print '[addPngFromFile unsupported filter method '.$file.']'; 5057 4421 5058 $errormsg = 'unsupported filter method'; 4422 5059 } … … 4426 5063 case 'PLTE': 4427 5064 4428 $pdata.= substr($data, $p+8, $chunkLen);5065 $pdata.= mb_substr($data, $p+8, $chunkLen, '8bit'); 4429 5066 4430 5067 break; … … 4432 5069 case 'IDAT': 4433 5070 4434 $idata.= substr($data, $p+8, $chunkLen);5071 $idata.= mb_substr($data, $p+8, $chunkLen, '8bit'); 4435 5072 4436 5073 break; … … 4453 5090 $transparency['type'] = 'indexed'; 4454 5091 4455 $numPalette = strlen($pdata) /3;5092 $numPalette = mb_strlen($pdata, '8bit') /3; 4456 5093 4457 5094 $trans = 0; … … 4495 5132 //unsupported transparency type 4496 5133 5134 //debugpng 5135 if (DEBUGPNG) print '[addPngFromFile unsupported transparency type '.$file.']'; 4497 5136 } 4498 5137 … … 4514 5153 $error = 1; 4515 5154 5155 //debugpng 5156 if (DEBUGPNG) print '[addPngFromFile information header is missing '.$file.']'; 5157 4516 5158 $errormsg = 'information header is missing'; 4517 5159 } … … 4521 5163 $error = 1; 4522 5164 5165 //debugpng 5166 if (DEBUGPNG) print '[addPngFromFile no support for interlaced images in pdf '.$file.']'; 5167 4523 5168 $errormsg = 'There appears to be no support for interlaced images in pdf.'; 4524 5169 } … … 4530 5175 $error = 1; 4531 5176 5177 //debugpng 5178 if (DEBUGPNG) print '[addPngFromFile bit depth of 8 or less is supported '.$file.']'; 5179 4532 5180 $errormsg = 'only bit depth of 8 or less is supported'; 4533 5181 } … … 4539 5187 4540 5188 $error = 1; 5189 5190 //debugpng 5191 if (DEBUGPNG) print '[addPngFromFile alpha channel not supported: '.$info['colorType'].' '.$file.']'; 4541 5192 4542 5193 $errormsg = 'transparancey alpha channel not supported, transparency only supported for palette images.'; … … 4579 5230 } 4580 5231 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) { 4582 5261 4583 5262 $w = $h/$info['height']*$info['width']; 4584 5263 } 4585 5264 4586 if ($h == 0) {5265 if ($h <= 0) { 4587 5266 4588 5267 $h = $w*$info['height']/$info['width']; 4589 5268 } 4590 5269 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 4612 5270 $this->objects[$this->currentContents]['c'].= "\nq"; 4613 5271 4614 $this->objects[$this->currentContents]['c'].= "\n".sprintf('%.3 f', $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"; 4615 5273 4616 5274 $this->objects[$this->currentContents]['c'].= "\n/".$label.' Do'; … … 4633 5291 } 4634 5292 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 } 4651 5324 4652 5325 if ($w <= 0 && $h <= 0) { … … 4665 5338 } 4666 5339 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); 4683 5341 } 4684 5342 … … 4691 5349 function addImage(&$img, $x, $y, $w = 0, $h = 0, $quality = 75) { 4692 5350 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 4693 5361 // add a new image into the current location, as an external object 4694 5362 // add the image at $x,$y, and with width and height as defined by $w & $h … … 4727 5395 } 4728 5396 4729 4730 5397 // 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]); 4766 5412 } 4767 5413 … … 4772 5418 * @access private 4773 5419 */ 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 } 4787 5448 4788 5449 4789 5450 $this->objects[$this->currentContents]['c'].= "\nq"; 4790 5451 4791 $this->objects[$this->currentContents]['c'].= "\n".sprintf('%.3 f', $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"; 4792 5453 4793 5454 $this->objects[$this->currentContents]['c'].= "\n/".$label.' Do'; … … 4877 5538 // the user is trying to set a font family 4878 5539 // note that this can also be used to set the base ones to something else 4879 if ( strlen($family)) {5540 if (mb_strlen($family)) { 4880 5541 4881 5542 $this->fontFamilies[$family] = $options;
Note: See TracChangeset
for help on using the changeset viewer.