File Source for makefont.php

com-tecnick-tcpdf

# Source for file makefont.php

Documentation is available at makefont.php

1. <?php
2. //============================================================+
3. // File name : makefont.php
4. // Begin : 2004-12-31
5. // Last Update : 2010-08-08
6. // Version : 1.2.006
7. // License : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
8. // ----------------------------------------------------------------------------
9. // Copyright (C) 2008-2010 Nicola Asuni - Tecnick.com S.r.l.
10. //
11. // This file is part of TCPDF software library.
12. //
13. // TCPDF is free software: you can redistribute it and/or modify it
14. // under the terms of the GNU Lesser General Public License as
15. // published by the Free Software Foundation, either version 3 of the
16. // License, or (at your option) any later version.
17. //
18. // TCPDF is distributed in the hope that it will be useful, but
19. // WITHOUT ANY WARRANTY; without even the implied warranty of
20. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
21. // See the GNU Lesser General Public License for more details.
22. //
23. // You should have received a copy of the GNU Lesser General Public License
24. // along with TCPDF. If not, see <http://www.gnu.org/licenses/>.
25. //
26. // See LICENSE.TXT file for more information.
27. // ----------------------------------------------------------------------------
28. //
29. // Description : Utility to generate font definition files fot TCPDF
30. //
31. // Authors: Nicola Asuni, Olivier Plathey, Steven Wittens
32. //
33. // (c) Copyright:
34. // Nicola Asuni
35. // Tecnick.com S.r.l.
36. // Via della Pace, 11
37. // 09044 Quartucciu (CA)
38. // ITALY
39. // www.tecnick.com
40. // info@tecnick.com
41. //============================================================+
43. /**
44. * Utility to generate font definition files fot TCPDF.
45. * @author Nicola Asuni, Olivier Plathey, Steven Wittens
46. * @copyright 2004-2008 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
47. * @package com.tecnick.tcpdf
48. * @link http://www.tcpdf.org
49. * @license http://www.gnu.org/copyleft/lesser.html LGPL
50. */
52. /**
53. *
54. * @param string $fontfile path to font file (TTF, OTF or PFB).
55. * @param string $fmfile font metrics file (UFM or AFM).
56. * @param boolean $embedded Set to false to not embed the font, true otherwise (default).
57. * @param string $enc Name of the encoding table to use. Omit this parameter for TrueType Unicode, OpenType Unicode and symbolic fonts like Symbol or ZapfDingBats.
58. * @param array $patch Optional modification of the encoding
59. */
60. function MakeFont($fontfile, $fmfile, $embedded=true, $enc='cp1252', $patch=array()) {
61. //Generate a font definition file
62. set_magic_quotes_runtime(0);
63. ini_set('auto_detect_line_endings', '1');
64. if (! file_exists($fontfile)) {
65. die('Error: file not found: '.$fontfile);
66. }
67. if (! file_exists($fmfile)) {
68. die('Error: file not found: '.$fmfile);
69. }
70. $cidtogidmap = '';
71. $map = array();
72. $diff = '';
73. $dw = 0; // default width
74. $ffext = strtolower( substr($fontfile, -3));
75. $fmext = strtolower( substr($fmfile, -3));
76. if ($fmext == 'afm') {
77. if (($ffext == 'ttf') OR ($ffext == 'otf')) {
78. $type = 'TrueType';
79. } elseif ($ffext == 'pfb') {
80. $type = 'Type1';
81. } else {
82. die('Error: unrecognized font file extension: '.$ffext);
83. }
84. if ($enc) {
85. $map = ReadMap($enc);
86. foreach ($patch as $cc => $gn) {
87. $map[$cc] = $gn;
88. }
89. }
90. $fm = ReadAFM($fmfile, $map);
91. if (isset($widths['.notdef'])) {
92. $dw = $widths['.notdef'];
93. }
94. if ($enc) {
95. $diff = MakeFontEncoding($map);
96. }
97. $fd = MakeFontDescriptor($fm, empty($map));
98. } elseif ($fmext == 'ufm') {
99. $enc = '';
100. if (($ffext == 'ttf') OR ($ffext == 'otf')) {
101. $type = 'TrueTypeUnicode';
102. } else {
103. die('Error: not a TrueType font: '.$ffext);
104. }
105. $fm = ReadUFM($fmfile, $cidtogidmap);
106. $dw = $fm['MissingWidth'];
107. $fd = MakeFontDescriptor($fm, false);
108. }
109. //Start generation
110. $s = '<?php'."\n";
111. $s .= '$type=\''.$type."';\n";
112. $s .= '$name=\''.$fm['FontName']."';\n";
113. $s .= '$desc='.$fd.";\n";
114. if (!isset($fm['UnderlinePosition'])) {
115. $fm['UnderlinePosition'] = -100;
116. }
117. if (!isset($fm['UnderlineThickness'])) {
118. $fm['UnderlineThickness'] = 50;
119. }
120. $s .= '$up='.$fm['UnderlinePosition'].";\n";
121. $s .= '$ut='.$fm['UnderlineThickness'].";\n";
122. if ($dw <= 0) {
123. if (isset($fm['Widths'][32]) AND ($fm['Widths'][32] > 0)) {
124. // assign default space width
125. $dw = $fm['Widths'][32];
126. } else {
127. $dw = 600;
128. }
129. }
130. $s .= '$dw='.$dw.";\n";
131. $w = MakeWidthArray($fm);
132. $s .= '$cw='.$w.";\n";
133. $s .= '$enc=\''.$enc."';\n";
134. $s .= '$diff=\''.$diff."';\n";
135. $basename = substr( basename($fmfile), 0, -4);
136. if ($embedded) {
137. //Embedded font
138. if (($type == 'TrueType') OR ($type == 'TrueTypeUnicode')) {
139. CheckTTF($fontfile);
140. }
141. $f = fopen($fontfile,'rb');
142. if (!$f) {
143. die('Error: Unable to open '.$fontfile);
144. }
145. $file = fread($f, filesize($fontfile));
146. fclose($f);
147. if ($type == 'Type1') {
148. //Find first two sections and discard third one
149. $header = ( ord($file{0}) == 128);
150. if ($header) {
151. //Strip first binary header
152. $file = substr($file, 6);
153. }
154. $pos = strpos($file, 'eexec');
155. if (!$pos) {
156. die('Error: font file does not seem to be valid Type1');
157. }
158. $size1 = $pos + 6;
159. if ($header AND ( ord($file{$size1}) == 128)) {
160. //Strip second binary header
161. $file = substr($file, 0, $size1).substr($file, $size1+6);
162. }
163. $pos = strpos($file, '00000000');
164. if (!$pos) {
165. die('Error: font file does not seem to be valid Type1');
166. }
167. $size2 = $pos - $size1;
168. $file = substr($file, 0, ($size1 + $size2));
169. }
170. $basename = strtolower($basename);
171. if ( function_exists('gzcompress')) {
172. $cmp = $basename.'.z';
173. SaveToFile($cmp, gzcompress($file, 9), 'b');
174. $s .= '$file=\''.$cmp."';\n";
175. print "Font file compressed (".$cmp.")\n";
176. if (! empty($cidtogidmap)) {
177. $cmp = $basename.'.ctg.z';
178. SaveToFile($cmp, gzcompress($cidtogidmap, 9), 'b');
179. print "CIDToGIDMap created and compressed (".$cmp.")\n";
180. $s .= '$ctg=\''.$cmp."';\n";
181. }
182. } else {
183. $s .= '$file=\''.basename($fontfile)."';\n";
184. print "Notice: font file could not be compressed (zlib extension not available)\n";
185. if (! empty($cidtogidmap)) {
186. $cmp = $basename.'.ctg';
187. $f = fopen($cmp, 'wb');
188. fwrite($f, $cidtogidmap);
189. fclose($f);
190. print "CIDToGIDMap created (".$cmp.")\n";
191. $s .= '$ctg=\''.$cmp."';\n";
192. }
193. }
194. if($type == 'Type1') {
195. $s .= '$size1='.$size1.";\n";
196. $s .= '$size2='.$size2.";\n";
197. } else {
198. $s.='$originalsize='.filesize($fontfile).";\n";
199. }
200. } else {
201. //Not embedded font
202. $s .= '$file='."'';\n";
203. }
204. $s .= '// --- EOF ---';
205. SaveToFile($basename.'.php',$s);
206. print "Font definition file generated (".$basename.".php)\n";
207. }
209. /**
210. * Read the specified encoding map.
211. * @param string $enc map name (see /enc/ folder for valid names).
212. */
213. function ReadMap($enc) {
214. //Read a map file
215. $file = dirname(__FILE__).'/enc/'.strtolower($enc).'.map';
216. $a = file($file);
217. if (empty($a)) {
218. die('Error: encoding not found: '.$enc);
219. }
220. $cc2gn = array();
221. foreach ($a as $l) {
222. if ($l{0} == '!') {
223. $e = preg_split('/[ \\t]+/', rtrim($l));
224. $cc = hexdec( substr($e[0],1));
225. $gn = $e[2];
226. $cc2gn[$cc] = $gn;
227. }
228. }
229. for($i = 0; $i <= 255; $i++) {
230. if(!isset($cc2gn[$i])) {
231. $cc2gn[$i] = '.notdef';
232. }
233. }
234. return $cc2gn;
235. }
237. /**
238. * Read UFM file
239. */
240. function ReadUFM($file, &$cidtogidmap) {
241. //Prepare empty CIDToGIDMap
242. $cidtogidmap = str_pad('', (256 * 256 * 2), "\x00");
243. //Read a font metric file
244. $a = file($file);
245. if (empty($a)) {
246. die('File not found');
247. }
248. $widths = array();
249. $fm = array();
250. foreach($a as $l) {
251. $e = explode(' ', chop($l));
252. if( count($e) < 2) {
253. continue;
254. }
255. $code = $e[0];
256. $param = $e[1];
257. if($code == 'U') {
258. // U 827 ; WX 0 ; N squaresubnosp ; G 675 ;
259. //Character metrics
260. $cc = (int)$e[1];
261. if ($cc != -1) {
262. $gn = $e[7];
263. $w = $e[4];
264. $glyph = $e[10];
265. $widths[$cc] = $w;
266. if($cc == ord('X')) {
267. $fm['CapXHeight'] = $e[13];
268. }
269. // Set GID
270. if (($cc >= 0) AND ($cc < 0xFFFF) AND $glyph) {
271. $cidtogidmap{($cc * 2)} = chr($glyph >> 8);
272. $cidtogidmap{(($cc * 2) + 1)} = chr($glyph & 0xFF);
273. }
274. }
275. if((isset($gn) AND ($gn == '.notdef')) AND (!isset($fm['MissingWidth']))) {
276. $fm['MissingWidth'] = $w;
277. }
278. } elseif($code == 'FontName') {
279. $fm['FontName'] = $param;
280. } elseif($code == 'Weight') {
281. $fm['Weight'] = $param;
282. } elseif($code == 'ItalicAngle') {
283. $fm['ItalicAngle'] = (double)$param;
284. } elseif($code == 'Ascender') {
285. $fm['Ascender'] = (int)$param;
286. } elseif($code == 'Descender') {
287. $fm['Descender'] = (int)$param;
288. } elseif($code == 'UnderlineThickness') {
289. $fm['UnderlineThickness'] = (int)$param;
290. } elseif($code == 'UnderlinePosition') {
291. $fm['UnderlinePosition'] = (int)$param;
292. } elseif($code == 'IsFixedPitch') {
293. $fm['IsFixedPitch'] = ($param == 'true');
294. } elseif($code == 'FontBBox') {
295. $fm['FontBBox'] = array($e[1], $e[2], $e[3], $e[4]);
296. } elseif($code == 'CapHeight') {
297. $fm['CapHeight'] = (int)$param;
298. } elseif($code == 'StdVW') {
299. $fm['StdVW'] = (int)$param;
300. }
301. }
302. if(!isset($fm['MissingWidth'])) {
303. $fm['MissingWidth'] = 600;
304. }
305. if(!isset($fm['FontName'])) {
306. die('FontName not found');
307. }
308. $fm['Widths'] = $widths;
309. return $fm;
310. }
312. /**
313. * Read AFM file
314. */
315. function ReadAFM($file,&$map) {
316. //Read a font metric file
317. $a = file($file);
318. if(empty($a)) {
319. die('File not found');
320. }
321. $widths = array();
322. $fm = array();
323. $fix = array(
324. 'Edot'=>'Edotaccent',
325. 'edot'=>'edotaccent',
326. 'Idot'=>'Idotaccent',
327. 'Zdot'=>'Zdotaccent',
328. 'zdot'=>'zdotaccent',
329. 'Odblacute' => 'Ohungarumlaut',
330. 'odblacute' => 'ohungarumlaut',
331. 'Udblacute'=>'Uhungarumlaut',
332. 'udblacute'=>'uhungarumlaut',
333. 'Gcedilla'=>'Gcommaaccent'
334. ,'gcedilla'=>'gcommaaccent',
335. 'Kcedilla'=>'Kcommaaccent',
336. 'kcedilla'=>'kcommaaccent',
337. 'Lcedilla'=>'Lcommaaccent',
338. 'lcedilla'=>'lcommaaccent',
339. 'Ncedilla'=>'Ncommaaccent',
340. 'ncedilla'=>'ncommaaccent',
341. 'Rcedilla'=>'Rcommaaccent',
342. 'rcedilla'=>'rcommaaccent',
343. 'Scedilla'=>'Scommaaccent',
344. 'scedilla'=>'scommaaccent',
345. 'Tcedilla'=>'Tcommaaccent',
346. 'tcedilla'=>'tcommaaccent',
347. 'Dslash'=>'Dcroat',
348. 'dslash'=>'dcroat',
349. 'Dmacron'=>'Dcroat',
350. 'dmacron'=>'dcroat',
351. 'combininggraveaccent'=>'gravecomb',
352. 'combininghookabove'=>'hookabovecomb',
353. 'combiningtildeaccent'=>'tildecomb',
354. 'combiningacuteaccent'=>'acutecomb',
355. 'combiningdotbelow'=>'dotbelowcomb',
356. 'dongsign'=>'dong'
357. );
358. foreach($a as $l) {
359. $e = explode(' ', rtrim($l));
360. if ( count($e) < 2) {
361. continue;
362. }
363. $code = $e[0];
364. $param = $e[1];
365. if ($code == 'C') {
366. //Character metrics
367. $cc = (int)$e[1];
368. $w = $e[4];
369. $gn = $e[7];
370. if ( substr($gn, -4) == '20AC') {
371. $gn = 'Euro';
372. }
373. if (isset($fix[$gn])) {
374. //Fix incorrect glyph name
375. foreach ($map as $c => $n) {
376. if ($n == $fix[$gn]) {
377. $map[$c] = $gn;
378. }
379. }
380. }
381. if (empty($map)) {
382. //Symbolic font: use built-in encoding
383. $widths[$cc] = $w;
384. } else {
385. $widths[$gn] = $w;
386. if($gn == 'X') {
387. $fm['CapXHeight'] = $e[13];
388. }
389. }
390. if($gn == '.notdef') {
391. $fm['MissingWidth'] = $w;
392. }
393. } elseif($code == 'FontName') {
394. $fm['FontName'] = $param;
395. } elseif($code == 'Weight') {
396. $fm['Weight'] = $param;
397. } elseif($code == 'ItalicAngle') {
398. $fm['ItalicAngle'] = (double)$param;
399. } elseif($code == 'Ascender') {
400. $fm['Ascender'] = (int)$param;
401. } elseif($code == 'Descender') {
402. $fm['Descender'] = (int)$param;
403. } elseif($code == 'UnderlineThickness') {
404. $fm['UnderlineThickness'] = (int)$param;
405. } elseif($code == 'UnderlinePosition') {
406. $fm['UnderlinePosition'] = (int)$param;
407. } elseif($code == 'IsFixedPitch') {
408. $fm['IsFixedPitch'] = ($param == 'true');
409. } elseif($code == 'FontBBox') {
410. $fm['FontBBox'] = array($e[1], $e[2], $e[3], $e[4]);
411. } elseif($code == 'CapHeight') {
412. $fm['CapHeight'] = (int)$param;
413. } elseif($code == 'StdVW') {
414. $fm['StdVW'] = (int)$param;
415. }
416. }
417. if (!isset($fm['FontName'])) {
418. die('FontName not found');
419. }
420. if (! empty($map)) {
421. if (!isset($widths['.notdef'])) {
422. $widths['.notdef'] = 600;
423. }
424. if (!isset($widths['Delta']) AND isset($widths['increment'])) {
425. $widths['Delta'] = $widths['increment'];
426. }
427. //Order widths according to map
428. for ($i = 0; $i <= 255; $i++) {
429. if (!isset($widths[$map[$i]])) {
430. print "Warning: character ".$map[$i]." is missing\n";
431. $widths[$i] = $widths['.notdef'];
432. } else {
433. $widths[$i] = $widths[$map[$i]];
434. }
435. }
436. }
437. $fm['Widths'] = $widths;
438. return $fm;
439. }
441. function MakeFontDescriptor($fm, $symbolic=false) {
442. //Ascent
443. $asc = (isset($fm['Ascender']) ? $fm['Ascender'] : 1000);
444. $fd = "array('Ascent'=>".$asc;
445. //Descent
446. $desc = (isset($fm['Descender']) ? $fm['Descender'] : -200);
447. $fd .= ",'Descent'=>".$desc;
448. //CapHeight
449. if (isset($fm['CapHeight'])) {
450. $ch = $fm['CapHeight'];
451. } elseif (isset($fm['CapXHeight'])) {
452. $ch = $fm['CapXHeight'];
453. } else {
454. $ch = $asc;
455. }
456. $fd .= ",'CapHeight'=>".$ch;
457. //Flags
458. $flags = 0;
459. if (isset($fm['IsFixedPitch']) AND $fm['IsFixedPitch']) {
460. $flags += 1<<0;
461. }
462. if ($symbolic) {
463. $flags += 1<<2;
464. } else {
465. $flags += 1<<5;
466. }
467. if (isset($fm['ItalicAngle']) AND ($fm['ItalicAngle'] != 0)) {
468. $flags += 1<<6;
469. }
470. $fd .= ",'Flags'=>".$flags;
471. //FontBBox
472. if (isset($fm['FontBBox'])) {
473. $fbb = $fm['FontBBox'];
474. } else {
475. $fbb = array(0, ($desc - 100), 1000, ($asc + 100));
476. }
477. $fd .= ",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'";
478. //ItalicAngle
479. $ia = (isset($fm['ItalicAngle']) ? $fm['ItalicAngle'] : 0);
480. $fd .= ",'ItalicAngle'=>".$ia;
481. //StemV
482. if (isset($fm['StdVW'])) {
483. $stemv = $fm['StdVW'];
484. } elseif (isset($fm['Weight']) AND preg_match('/(bold|black)/i', $fm['Weight'])) {
485. $stemv = 120;
486. } else {
487. $stemv = 70;
488. }
489. $fd .= ",'StemV'=>".$stemv;
490. //MissingWidth
491. if(isset($fm['MissingWidth'])) {
492. $fd .= ",'MissingWidth'=>".$fm['MissingWidth'];
493. }
494. $fd .= ')';
495. return $fd;
496. }
498. function MakeWidthArray($fm) {
499. //Make character width array
500. $s = 'array(';
501. $cw = $fm['Widths'];
502. $els = array();
503. $c = 0;
504. foreach ($cw as $i => $w) {
505. if ( is_numeric($i)) {
506. $els[] = (((($c++)%10) == 0) ? "\n" : '').$i.'=>'.$w;
507. }
508. }
509. $s .= implode(',', $els);
510. $s .= ')';
511. return $s;
512. }
514. function MakeFontEncoding($map) {
515. //Build differences from reference encoding
516. $ref = ReadMap('cp1252');
517. $s = '';
518. $last = 0;
519. for ($i = 32; $i <= 255; $i++) {
520. if ($map[$i] != $ref[$i]) {
521. if ($i != $last+1) {
522. $s .= $i.' ';
523. }
524. $last = $i;
525. $s .= '/'.$map[$i].' ';
526. }
527. }
528. return rtrim($s);
529. }
531. function SaveToFile($file, $s, $mode='t') {
532. $f = fopen($file, 'w'.$mode);
533. if(!$f) {
534. die('Can\'t write to file '.$file);
535. }
536. fwrite($f, $s, strlen($s));
537. fclose($f);
538. }
540. function ReadShort($f) {
541. $a = unpack('n1n', fread($f, 2));
542. return $a['n'];
543. }
545. function ReadLong($f) {
546. $a = unpack('N1N', fread($f, 4));
547. return $a['N'];
548. }
550. function CheckTTF($file) {
551. //Check if font license allows embedding
552. $f = fopen($file, 'rb');
553. if (!$f) {
554. die('Error: unable to open '.$file);
555. }
556. //Extract number of tables
557. fseek($f, 4, SEEK_CUR);
558. $nb = ReadShort($f);
559. fseek($f, 6, SEEK_CUR);
560. //Seek OS/2 table
561. $found = false;
562. for ($i = 0; $i < $nb; $i++) {
563. if ( fread($f, 4) == 'OS/2') {
564. $found = true;
565. break;
566. }
567. fseek($f, 12, SEEK_CUR);
568. }
569. if (!$found) {
570. fclose($f);
571. return;
572. }
573. fseek($f, 4, SEEK_CUR);
574. $offset = ReadLong($f);
575. fseek($f, $offset, SEEK_SET);
576. //Extract fsType flags
577. fseek($f, 8, SEEK_CUR);
578. $fsType = ReadShort($f);
579. $rl = ($fsType & 0x02) != 0;
580. $pp = ($fsType & 0x04) != 0;
581. $e = ($fsType & 0x08) != 0;
582. fclose($f);
583. if($rl AND (!$pp) AND (!$e)) {
584. print "Warning: font license does not allow embedding\n";
585. }
586. }
588. $arg = $GLOBALS['argv'];
589. if ( count($arg) >= 3) {
590. ob_start();
591. array_shift($arg);
592. if ( sizeof($arg) == 3) {
593. $arg[3] = $arg[2];
594. $arg[2] = true;
595. } else {
596. if (!isset($arg[2])) {
597. $arg[2] = true;
598. }
599. if (!isset($arg[3])) {
600. $arg[3] = 'cp1252';
601. }
602. }
603. if (!isset($arg[4])) {
604. $arg[4] = array();
605. }
606. MakeFont($arg[0], $arg[1], $arg[2], $arg[3], $arg[4]);
607. $t = ob_get_clean();
608. print preg_replace('!<BR( /)?>!i', "\n", $t);
609. } else {
610. print "Usage: makefont.php <ttf/otf/pfb file> <afm/ufm file> <encoding> <patch>\n";
611. }
613. //============================================================+
614. // END OF FILE
615. //============================================================+

Documentation generated on Sun, 27 Feb 2011 16:32:36 -0800 by phpDocumentor 1.4.3