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