makefont.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. <?php
  2. /*******************************************************************************
  3. * Utility to generate font definition files *
  4. * *
  5. * Version: 1.2 *
  6. * Date: 2011-06-18 *
  7. * Author: Olivier PLATHEY *
  8. *******************************************************************************/
  9. require('ttfparser.php');
  10. function Message($txt, $severity='')
  11. {
  12. if(PHP_SAPI=='cli')
  13. {
  14. if($severity)
  15. echo "$severity: ";
  16. echo "$txt\n";
  17. }
  18. else
  19. {
  20. if($severity)
  21. echo "<b>$severity</b>: ";
  22. echo "$txt<br>";
  23. }
  24. }
  25. function Notice($txt)
  26. {
  27. Message($txt, 'Notice');
  28. }
  29. function Warning($txt)
  30. {
  31. Message($txt, 'Warning');
  32. }
  33. function Error($txt)
  34. {
  35. Message($txt, 'Error');
  36. exit;
  37. }
  38. function LoadMap($enc)
  39. {
  40. $file = dirname(__FILE__).'/'.strtolower($enc).'.map';
  41. $a = file($file);
  42. if(empty($a))
  43. Error('Encoding not found: '.$enc);
  44. $map = array_fill(0, 256, array('uv'=>-1, 'name'=>'.notdef'));
  45. foreach($a as $line)
  46. {
  47. $e = explode(' ', rtrim($line));
  48. $c = hexdec(substr($e[0],1));
  49. $uv = hexdec(substr($e[1],2));
  50. $name = $e[2];
  51. $map[$c] = array('uv'=>$uv, 'name'=>$name);
  52. }
  53. return $map;
  54. }
  55. function GetInfoFromTrueType($file, $embed, $map)
  56. {
  57. // Return informations from a TrueType font
  58. $ttf = new TTFParser();
  59. $ttf->Parse($file);
  60. if($embed)
  61. {
  62. if(!$ttf->Embeddable)
  63. Error('Font license does not allow embedding');
  64. $info['Data'] = file_get_contents($file);
  65. $info['OriginalSize'] = filesize($file);
  66. }
  67. $k = 1000/$ttf->unitsPerEm;
  68. $info['FontName'] = $ttf->postScriptName;
  69. $info['Bold'] = $ttf->Bold;
  70. $info['ItalicAngle'] = $ttf->italicAngle;
  71. $info['IsFixedPitch'] = $ttf->isFixedPitch;
  72. $info['Ascender'] = round($k*$ttf->typoAscender);
  73. $info['Descender'] = round($k*$ttf->typoDescender);
  74. $info['UnderlineThickness'] = round($k*$ttf->underlineThickness);
  75. $info['UnderlinePosition'] = round($k*$ttf->underlinePosition);
  76. $info['FontBBox'] = array(round($k*$ttf->xMin), round($k*$ttf->yMin), round($k*$ttf->xMax), round($k*$ttf->yMax));
  77. $info['CapHeight'] = round($k*$ttf->capHeight);
  78. $info['MissingWidth'] = round($k*$ttf->widths[0]);
  79. $widths = array_fill(0, 256, $info['MissingWidth']);
  80. for($c=0;$c<=255;$c++)
  81. {
  82. if($map[$c]['name']!='.notdef')
  83. {
  84. $uv = $map[$c]['uv'];
  85. if(isset($ttf->chars[$uv]))
  86. {
  87. $w = $ttf->widths[$ttf->chars[$uv]];
  88. $widths[$c] = round($k*$w);
  89. }
  90. else
  91. Warning('Character '.$map[$c]['name'].' is missing');
  92. }
  93. }
  94. $info['Widths'] = $widths;
  95. return $info;
  96. }
  97. function GetInfoFromType1($file, $embed, $map)
  98. {
  99. // Return informations from a Type1 font
  100. if($embed)
  101. {
  102. $f = fopen($file, 'rb');
  103. if(!$f)
  104. Error('Can\'t open font file');
  105. // Read first segment
  106. $a = unpack('Cmarker/Ctype/Vsize', fread($f,6));
  107. if($a['marker']!=128)
  108. Error('Font file is not a valid binary Type1');
  109. $size1 = $a['size'];
  110. $data = fread($f, $size1);
  111. // Read second segment
  112. $a = unpack('Cmarker/Ctype/Vsize', fread($f,6));
  113. if($a['marker']!=128)
  114. Error('Font file is not a valid binary Type1');
  115. $size2 = $a['size'];
  116. $data .= fread($f, $size2);
  117. fclose($f);
  118. $info['Data'] = $data;
  119. $info['Size1'] = $size1;
  120. $info['Size2'] = $size2;
  121. }
  122. $afm = substr($file, 0, -3).'afm';
  123. if(!file_exists($afm))
  124. Error('AFM font file not found: '.$afm);
  125. $a = file($afm);
  126. if(empty($a))
  127. Error('AFM file empty or not readable');
  128. foreach($a as $line)
  129. {
  130. $e = explode(' ', rtrim($line));
  131. if(count($e)<2)
  132. continue;
  133. $entry = $e[0];
  134. if($entry=='C')
  135. {
  136. $w = $e[4];
  137. $name = $e[7];
  138. $cw[$name] = $w;
  139. }
  140. elseif($entry=='FontName')
  141. $info['FontName'] = $e[1];
  142. elseif($entry=='Weight')
  143. $info['Weight'] = $e[1];
  144. elseif($entry=='ItalicAngle')
  145. $info['ItalicAngle'] = (int)$e[1];
  146. elseif($entry=='Ascender')
  147. $info['Ascender'] = (int)$e[1];
  148. elseif($entry=='Descender')
  149. $info['Descender'] = (int)$e[1];
  150. elseif($entry=='UnderlineThickness')
  151. $info['UnderlineThickness'] = (int)$e[1];
  152. elseif($entry=='UnderlinePosition')
  153. $info['UnderlinePosition'] = (int)$e[1];
  154. elseif($entry=='IsFixedPitch')
  155. $info['IsFixedPitch'] = ($e[1]=='true');
  156. elseif($entry=='FontBBox')
  157. $info['FontBBox'] = array((int)$e[1], (int)$e[2], (int)$e[3], (int)$e[4]);
  158. elseif($entry=='CapHeight')
  159. $info['CapHeight'] = (int)$e[1];
  160. elseif($entry=='StdVW')
  161. $info['StdVW'] = (int)$e[1];
  162. }
  163. if(!isset($info['FontName']))
  164. Error('FontName missing in AFM file');
  165. $info['Bold'] = isset($info['Weight']) && preg_match('/bold|black/i', $info['Weight']);
  166. if(isset($cw['.notdef']))
  167. $info['MissingWidth'] = $cw['.notdef'];
  168. else
  169. $info['MissingWidth'] = 0;
  170. $widths = array_fill(0, 256, $info['MissingWidth']);
  171. for($c=0;$c<=255;$c++)
  172. {
  173. $name = $map[$c]['name'];
  174. if($name!='.notdef')
  175. {
  176. if(isset($cw[$name]))
  177. $widths[$c] = $cw[$name];
  178. else
  179. Warning('Character '.$name.' is missing');
  180. }
  181. }
  182. $info['Widths'] = $widths;
  183. return $info;
  184. }
  185. function MakeFontDescriptor($info)
  186. {
  187. // Ascent
  188. $fd = "array('Ascent'=>".$info['Ascender'];
  189. // Descent
  190. $fd .= ",'Descent'=>".$info['Descender'];
  191. // CapHeight
  192. if(!empty($info['CapHeight']))
  193. $fd .= ",'CapHeight'=>".$info['CapHeight'];
  194. else
  195. $fd .= ",'CapHeight'=>".$info['Ascender'];
  196. // Flags
  197. $flags = 0;
  198. if($info['IsFixedPitch'])
  199. $flags += 1<<0;
  200. $flags += 1<<5;
  201. if($info['ItalicAngle']!=0)
  202. $flags += 1<<6;
  203. $fd .= ",'Flags'=>".$flags;
  204. // FontBBox
  205. $fbb = $info['FontBBox'];
  206. $fd .= ",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'";
  207. // ItalicAngle
  208. $fd .= ",'ItalicAngle'=>".$info['ItalicAngle'];
  209. // StemV
  210. if(isset($info['StdVW']))
  211. $stemv = $info['StdVW'];
  212. elseif($info['Bold'])
  213. $stemv = 120;
  214. else
  215. $stemv = 70;
  216. $fd .= ",'StemV'=>".$stemv;
  217. // MissingWidth
  218. $fd .= ",'MissingWidth'=>".$info['MissingWidth'].')';
  219. return $fd;
  220. }
  221. function MakeWidthArray($widths)
  222. {
  223. $s = "array(\n\t";
  224. for($c=0;$c<=255;$c++)
  225. {
  226. if(chr($c)=="'")
  227. $s .= "'\\''";
  228. elseif(chr($c)=="\\")
  229. $s .= "'\\\\'";
  230. elseif($c>=32 && $c<=126)
  231. $s .= "'".chr($c)."'";
  232. else
  233. $s .= "chr($c)";
  234. $s .= '=>'.$widths[$c];
  235. if($c<255)
  236. $s .= ',';
  237. if(($c+1)%22==0)
  238. $s .= "\n\t";
  239. }
  240. $s .= ')';
  241. return $s;
  242. }
  243. function MakeFontEncoding($map)
  244. {
  245. // Build differences from reference encoding
  246. $ref = LoadMap('cp1252');
  247. $s = '';
  248. $last = 0;
  249. for($c=32;$c<=255;$c++)
  250. {
  251. if($map[$c]['name']!=$ref[$c]['name'])
  252. {
  253. if($c!=$last+1)
  254. $s .= $c.' ';
  255. $last = $c;
  256. $s .= '/'.$map[$c]['name'].' ';
  257. }
  258. }
  259. return rtrim($s);
  260. }
  261. function SaveToFile($file, $s, $mode)
  262. {
  263. $f = fopen($file, 'w'.$mode);
  264. if(!$f)
  265. Error('Can\'t write to file '.$file);
  266. fwrite($f, $s, strlen($s));
  267. fclose($f);
  268. }
  269. function MakeDefinitionFile($file, $type, $enc, $embed, $map, $info)
  270. {
  271. $s = "<?php\n";
  272. $s .= '$type = \''.$type."';\n";
  273. $s .= '$name = \''.$info['FontName']."';\n";
  274. $s .= '$desc = '.MakeFontDescriptor($info).";\n";
  275. $s .= '$up = '.$info['UnderlinePosition'].";\n";
  276. $s .= '$ut = '.$info['UnderlineThickness'].";\n";
  277. $s .= '$cw = '.MakeWidthArray($info['Widths']).";\n";
  278. $s .= '$enc = \''.$enc."';\n";
  279. $diff = MakeFontEncoding($map);
  280. if($diff)
  281. $s .= '$diff = \''.$diff."';\n";
  282. if($embed)
  283. {
  284. $s .= '$file = \''.$info['File']."';\n";
  285. if($type=='Type1')
  286. {
  287. $s .= '$size1 = '.$info['Size1'].";\n";
  288. $s .= '$size2 = '.$info['Size2'].";\n";
  289. }
  290. else
  291. $s .= '$originalsize = '.$info['OriginalSize'].";\n";
  292. }
  293. $s .= "?>\n";
  294. SaveToFile($file, $s, 't');
  295. }
  296. function MakeFont($fontfile, $enc='cp1252', $embed=true)
  297. {
  298. // Generate a font definition file
  299. if(get_magic_quotes_runtime())
  300. @set_magic_quotes_runtime(0);
  301. ini_set('auto_detect_line_endings', '1');
  302. if(!file_exists($fontfile))
  303. Error('Font file not found: '.$fontfile);
  304. $ext = strtolower(substr($fontfile,-3));
  305. if($ext=='ttf' || $ext=='otf')
  306. $type = 'TrueType';
  307. elseif($ext=='pfb')
  308. $type = 'Type1';
  309. else
  310. Error('Unrecognized font file extension: '.$ext);
  311. $map = LoadMap($enc);
  312. if($type=='TrueType')
  313. $info = GetInfoFromTrueType($fontfile, $embed, $map);
  314. else
  315. $info = GetInfoFromType1($fontfile, $embed, $map);
  316. $basename = substr(basename($fontfile), 0, -4);
  317. if($embed)
  318. {
  319. if(function_exists('gzcompress'))
  320. {
  321. $file = $basename.'.z';
  322. SaveToFile($file, gzcompress($info['Data']), 'b');
  323. $info['File'] = $file;
  324. Message('Font file compressed: '.$file);
  325. }
  326. else
  327. {
  328. $info['File'] = basename($fontfile);
  329. Notice('Font file could not be compressed (zlib extension not available)');
  330. }
  331. }
  332. MakeDefinitionFile($basename.'.php', $type, $enc, $embed, $map, $info);
  333. Message('Font definition file generated: '.$basename.'.php');
  334. }
  335. if(PHP_SAPI=='cli')
  336. {
  337. // Command-line interface
  338. if($argc==1)
  339. die("Usage: php makefont.php fontfile [enc] [embed]\n");
  340. $fontfile = $argv[1];
  341. if($argc>=3)
  342. $enc = $argv[2];
  343. else
  344. $enc = 'cp1252';
  345. if($argc>=4)
  346. $embed = ($argv[3]=='true' || $argv[3]=='1');
  347. else
  348. $embed = true;
  349. MakeFont($fontfile, $enc, $embed);
  350. }
  351. ?>