123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- <?php
- /*******************************************************************************
- * Utility to generate font definition files *
- * *
- * Version: 1.3 *
- * Date: 2015-11-29 *
- * Author: Olivier PLATHEY *
- *******************************************************************************/
- require('ttfparser.php');
- function Message($txt, $severity='')
- {
- if(PHP_SAPI=='cli')
- {
- if($severity)
- echo "$severity: ";
- echo "$txt\n";
- }
- else
- {
- if($severity)
- echo "<b>$severity</b>: ";
- echo "$txt<br>";
- }
- }
- function Notice($txt)
- {
- Message($txt, 'Notice');
- }
- function Warning($txt)
- {
- Message($txt, 'Warning');
- }
- function Error($txt)
- {
- Message($txt, 'Error');
- exit;
- }
- function LoadMap($enc)
- {
- $file = dirname(__FILE__).'/'.strtolower($enc).'.map';
- $a = file($file);
- if(empty($a))
- Error('Encoding not found: '.$enc);
- $map = array_fill(0, 256, array('uv'=>-1, 'name'=>'.notdef'));
- foreach($a as $line)
- {
- $e = explode(' ', rtrim($line));
- $c = hexdec(substr($e[0],1));
- $uv = hexdec(substr($e[1],2));
- $name = $e[2];
- $map[$c] = array('uv'=>$uv, 'name'=>$name);
- }
- return $map;
- }
- function GetInfoFromTrueType($file, $embed, $subset, $map)
- {
- // Return information from a TrueType font
- try
- {
- $ttf = new TTFParser($file);
- $ttf->Parse();
- }
- catch(Exception $e)
- {
- Error($e->getMessage());
- }
- if($embed)
- {
- if(!$ttf->embeddable)
- Error('Font license does not allow embedding');
- if($subset)
- {
- $chars = array();
- foreach($map as $v)
- {
- if($v['name']!='.notdef')
- $chars[] = $v['uv'];
- }
- $ttf->Subset($chars);
- $info['Data'] = $ttf->Build();
- }
- else
- $info['Data'] = file_get_contents($file);
- $info['OriginalSize'] = strlen($info['Data']);
- }
- $k = 1000/$ttf->unitsPerEm;
- $info['FontName'] = $ttf->postScriptName;
- $info['Bold'] = $ttf->bold;
- $info['ItalicAngle'] = $ttf->italicAngle;
- $info['IsFixedPitch'] = $ttf->isFixedPitch;
- $info['Ascender'] = round($k*$ttf->typoAscender);
- $info['Descender'] = round($k*$ttf->typoDescender);
- $info['UnderlineThickness'] = round($k*$ttf->underlineThickness);
- $info['UnderlinePosition'] = round($k*$ttf->underlinePosition);
- $info['FontBBox'] = array(round($k*$ttf->xMin), round($k*$ttf->yMin), round($k*$ttf->xMax), round($k*$ttf->yMax));
- $info['CapHeight'] = round($k*$ttf->capHeight);
- $info['MissingWidth'] = round($k*$ttf->glyphs[0]['w']);
- $widths = array_fill(0, 256, $info['MissingWidth']);
- foreach($map as $c=>$v)
- {
- if($v['name']!='.notdef')
- {
- if(isset($ttf->chars[$v['uv']]))
- {
- $id = $ttf->chars[$v['uv']];
- $w = $ttf->glyphs[$id]['w'];
- $widths[$c] = round($k*$w);
- }
- else
- Warning('Character '.$v['name'].' is missing');
- }
- }
- $info['Widths'] = $widths;
- return $info;
- }
- function GetInfoFromType1($file, $embed, $map)
- {
- // Return information from a Type1 font
- if($embed)
- {
- $f = fopen($file, 'rb');
- if(!$f)
- Error('Can\'t open font file');
- // Read first segment
- $a = unpack('Cmarker/Ctype/Vsize', fread($f,6));
- if($a['marker']!=128)
- Error('Font file is not a valid binary Type1');
- $size1 = $a['size'];
- $data = fread($f, $size1);
- // Read second segment
- $a = unpack('Cmarker/Ctype/Vsize', fread($f,6));
- if($a['marker']!=128)
- Error('Font file is not a valid binary Type1');
- $size2 = $a['size'];
- $data .= fread($f, $size2);
- fclose($f);
- $info['Data'] = $data;
- $info['Size1'] = $size1;
- $info['Size2'] = $size2;
- }
- $afm = substr($file, 0, -3).'afm';
- if(!file_exists($afm))
- Error('AFM font file not found: '.$afm);
- $a = file($afm);
- if(empty($a))
- Error('AFM file empty or not readable');
- foreach($a as $line)
- {
- $e = explode(' ', rtrim($line));
- if(count($e)<2)
- continue;
- $entry = $e[0];
- if($entry=='C')
- {
- $w = $e[4];
- $name = $e[7];
- $cw[$name] = $w;
- }
- elseif($entry=='FontName')
- $info['FontName'] = $e[1];
- elseif($entry=='Weight')
- $info['Weight'] = $e[1];
- elseif($entry=='ItalicAngle')
- $info['ItalicAngle'] = (int)$e[1];
- elseif($entry=='Ascender')
- $info['Ascender'] = (int)$e[1];
- elseif($entry=='Descender')
- $info['Descender'] = (int)$e[1];
- elseif($entry=='UnderlineThickness')
- $info['UnderlineThickness'] = (int)$e[1];
- elseif($entry=='UnderlinePosition')
- $info['UnderlinePosition'] = (int)$e[1];
- elseif($entry=='IsFixedPitch')
- $info['IsFixedPitch'] = ($e[1]=='true');
- elseif($entry=='FontBBox')
- $info['FontBBox'] = array((int)$e[1], (int)$e[2], (int)$e[3], (int)$e[4]);
- elseif($entry=='CapHeight')
- $info['CapHeight'] = (int)$e[1];
- elseif($entry=='StdVW')
- $info['StdVW'] = (int)$e[1];
- }
- if(!isset($info['FontName']))
- Error('FontName missing in AFM file');
- if(!isset($info['Ascender']))
- $info['Ascender'] = $info['FontBBox'][3];
- if(!isset($info['Descender']))
- $info['Descender'] = $info['FontBBox'][1];
- $info['Bold'] = isset($info['Weight']) && preg_match('/bold|black/i', $info['Weight']);
- if(isset($cw['.notdef']))
- $info['MissingWidth'] = $cw['.notdef'];
- else
- $info['MissingWidth'] = 0;
- $widths = array_fill(0, 256, $info['MissingWidth']);
- foreach($map as $c=>$v)
- {
- if($v['name']!='.notdef')
- {
- if(isset($cw[$v['name']]))
- $widths[$c] = $cw[$v['name']];
- else
- Warning('Character '.$v['name'].' is missing');
- }
- }
- $info['Widths'] = $widths;
- return $info;
- }
- function MakeFontDescriptor($info)
- {
- // Ascent
- $fd = "array('Ascent'=>".$info['Ascender'];
- // Descent
- $fd .= ",'Descent'=>".$info['Descender'];
- // CapHeight
- if(!empty($info['CapHeight']))
- $fd .= ",'CapHeight'=>".$info['CapHeight'];
- else
- $fd .= ",'CapHeight'=>".$info['Ascender'];
- // Flags
- $flags = 0;
- if($info['IsFixedPitch'])
- $flags += 1<<0;
- $flags += 1<<5;
- if($info['ItalicAngle']!=0)
- $flags += 1<<6;
- $fd .= ",'Flags'=>".$flags;
- // FontBBox
- $fbb = $info['FontBBox'];
- $fd .= ",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'";
- // ItalicAngle
- $fd .= ",'ItalicAngle'=>".$info['ItalicAngle'];
- // StemV
- if(isset($info['StdVW']))
- $stemv = $info['StdVW'];
- elseif($info['Bold'])
- $stemv = 120;
- else
- $stemv = 70;
- $fd .= ",'StemV'=>".$stemv;
- // MissingWidth
- $fd .= ",'MissingWidth'=>".$info['MissingWidth'].')';
- return $fd;
- }
- function MakeWidthArray($widths)
- {
- $s = "array(\n\t";
- for($c=0;$c<=255;$c++)
- {
- if(chr($c)=="'")
- $s .= "'\\''";
- elseif(chr($c)=="\\")
- $s .= "'\\\\'";
- elseif($c>=32 && $c<=126)
- $s .= "'".chr($c)."'";
- else
- $s .= "chr($c)";
- $s .= '=>'.$widths[$c];
- if($c<255)
- $s .= ',';
- if(($c+1)%22==0)
- $s .= "\n\t";
- }
- $s .= ')';
- return $s;
- }
- function MakeFontEncoding($map)
- {
- // Build differences from reference encoding
- $ref = LoadMap('cp1252');
- $s = '';
- $last = 0;
- for($c=32;$c<=255;$c++)
- {
- if($map[$c]['name']!=$ref[$c]['name'])
- {
- if($c!=$last+1)
- $s .= $c.' ';
- $last = $c;
- $s .= '/'.$map[$c]['name'].' ';
- }
- }
- return rtrim($s);
- }
- function MakeUnicodeArray($map)
- {
- // Build mapping to Unicode values
- $ranges = array();
- foreach($map as $c=>$v)
- {
- $uv = $v['uv'];
- if($uv!=-1)
- {
- if(isset($range))
- {
- if($c==$range[1]+1 && $uv==$range[3]+1)
- {
- $range[1]++;
- $range[3]++;
- }
- else
- {
- $ranges[] = $range;
- $range = array($c, $c, $uv, $uv);
- }
- }
- else
- $range = array($c, $c, $uv, $uv);
- }
- }
- $ranges[] = $range;
- foreach($ranges as $range)
- {
- if(isset($s))
- $s .= ',';
- else
- $s = 'array(';
- $s .= $range[0].'=>';
- $nb = $range[1]-$range[0]+1;
- if($nb>1)
- $s .= 'array('.$range[2].','.$nb.')';
- else
- $s .= $range[2];
- }
- $s .= ')';
- return $s;
- }
- function SaveToFile($file, $s, $mode)
- {
- $f = fopen($file, 'w'.$mode);
- if(!$f)
- Error('Can\'t write to file '.$file);
- fwrite($f, $s);
- fclose($f);
- }
- function MakeDefinitionFile($file, $type, $enc, $embed, $subset, $map, $info)
- {
- $s = "<?php\n";
- $s .= '$type = \''.$type."';\n";
- $s .= '$name = \''.$info['FontName']."';\n";
- $s .= '$desc = '.MakeFontDescriptor($info).";\n";
- $s .= '$up = '.$info['UnderlinePosition'].";\n";
- $s .= '$ut = '.$info['UnderlineThickness'].";\n";
- $s .= '$cw = '.MakeWidthArray($info['Widths']).";\n";
- $s .= '$enc = \''.$enc."';\n";
- $diff = MakeFontEncoding($map);
- if($diff)
- $s .= '$diff = \''.$diff."';\n";
- $s .= '$uv = '.MakeUnicodeArray($map).";\n";
- if($embed)
- {
- $s .= '$file = \''.$info['File']."';\n";
- if($type=='Type1')
- {
- $s .= '$size1 = '.$info['Size1'].";\n";
- $s .= '$size2 = '.$info['Size2'].";\n";
- }
- else
- {
- $s .= '$originalsize = '.$info['OriginalSize'].";\n";
- if($subset)
- $s .= "\$subsetted = true;\n";
- }
- }
- $s .= "?>\n";
- SaveToFile($file, $s, 't');
- }
- function MakeFont($fontfile, $enc='cp1252', $embed=true, $subset=true)
- {
- // Generate a font definition file
- if(get_magic_quotes_runtime())
- @set_magic_quotes_runtime(false);
- ini_set('auto_detect_line_endings', '1');
- if(!file_exists($fontfile))
- Error('Font file not found: '.$fontfile);
- $ext = strtolower(substr($fontfile,-3));
- if($ext=='ttf' || $ext=='otf')
- $type = 'TrueType';
- elseif($ext=='pfb')
- $type = 'Type1';
- else
- Error('Unrecognized font file extension: '.$ext);
- $map = LoadMap($enc);
- if($type=='TrueType')
- $info = GetInfoFromTrueType($fontfile, $embed, $subset, $map);
- else
- $info = GetInfoFromType1($fontfile, $embed, $map);
- $basename = substr(basename($fontfile), 0, -4);
- if($embed)
- {
- if(function_exists('gzcompress'))
- {
- $file = $basename.'.z';
- SaveToFile($file, gzcompress($info['Data']), 'b');
- $info['File'] = $file;
- Message('Font file compressed: '.$file);
- }
- else
- {
- $info['File'] = basename($fontfile);
- $subset = false;
- Notice('Font file could not be compressed (zlib extension not available)');
- }
- }
- MakeDefinitionFile($basename.'.php', $type, $enc, $embed, $subset, $map, $info);
- Message('Font definition file generated: '.$basename.'.php');
- }
- if(PHP_SAPI=='cli')
- {
- // Command-line interface
- ini_set('log_errors', '0');
- if($argc==1)
- die("Usage: php makefont.php fontfile [encoding] [embed] [subset]\n");
- $fontfile = $argv[1];
- if($argc>=3)
- $enc = $argv[2];
- else
- $enc = 'cp1252';
- if($argc>=4)
- $embed = ($argv[3]=='true' || $argv[3]=='1');
- else
- $embed = true;
- if($argc>=5)
- $subset = ($argv[4]=='true' || $argv[4]=='1');
- else
- $subset = true;
- MakeFont($fontfile, $enc, $embed, $subset);
- }
- ?>
|