ILIAS  eassessment Revision 61809
 All Data Structures Namespaces Files Functions Variables Groups Pages
makefont.php
Go to the documentation of this file.
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 //============================================================+
42 
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 }
208 
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 }
236 
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 }
311 
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 }
440 
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 }
497 
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 }
513 
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 }
530 
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 }
539 
540 function ReadShort($f) {
541  $a = unpack('n1n', fread($f, 2));
542  return $a['n'];
543 }
544 
545 function ReadLong($f) {
546  $a = unpack('N1N', fread($f, 4));
547  return $a['N'];
548 }
549 
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 }
587 
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 }
612 
613 //============================================================+
614 // END OF FILE
615 //============================================================+