$severity: "; echo "$txt
"; } } 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 = "\n"; SaveToFile($file, $s, 't'); } function MakeFont($fontfile, $enc='cp1252', $embed=true, $subset=true) { // Generate a font definition file 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); } ?>