COSC4606-Assignment-02

Database front end that allows for CRUD operations and user management
git clone git://mattcarlson.org/repos/COSC4606-Assignment-02.git
Log | Files | Refs | README

makefont.php (10873B)


      1 <?php
      2 /*******************************************************************************
      3 * Utility to generate font definition files                                    *
      4 *                                                                              *
      5 * Version: 1.31                                                                *
      6 * Date:    2019-12-07                                                          *
      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, $subset, $map)
     63 {
     64 	// Return information from a TrueType font
     65 	try
     66 	{
     67 		$ttf = new TTFParser($file);
     68 		$ttf->Parse();
     69 	}
     70 	catch(Exception $e)
     71 	{
     72 		Error($e->getMessage());
     73 	}
     74 	if($embed)
     75 	{
     76 		if(!$ttf->embeddable)
     77 			Error('Font license does not allow embedding');
     78 		if($subset)
     79 		{
     80 			$chars = array();
     81 			foreach($map as $v)
     82 			{
     83 				if($v['name']!='.notdef')
     84 					$chars[] = $v['uv'];
     85 			}
     86 			$ttf->Subset($chars);
     87 			$info['Data'] = $ttf->Build();
     88 		}
     89 		else
     90 			$info['Data'] = file_get_contents($file);
     91 		$info['OriginalSize'] = strlen($info['Data']);
     92 	}
     93 	$k = 1000/$ttf->unitsPerEm;
     94 	$info['FontName'] = $ttf->postScriptName;
     95 	$info['Bold'] = $ttf->bold;
     96 	$info['ItalicAngle'] = $ttf->italicAngle;
     97 	$info['IsFixedPitch'] = $ttf->isFixedPitch;
     98 	$info['Ascender'] = round($k*$ttf->typoAscender);
     99 	$info['Descender'] = round($k*$ttf->typoDescender);
    100 	$info['UnderlineThickness'] = round($k*$ttf->underlineThickness);
    101 	$info['UnderlinePosition'] = round($k*$ttf->underlinePosition);
    102 	$info['FontBBox'] = array(round($k*$ttf->xMin), round($k*$ttf->yMin), round($k*$ttf->xMax), round($k*$ttf->yMax));
    103 	$info['CapHeight'] = round($k*$ttf->capHeight);
    104 	$info['MissingWidth'] = round($k*$ttf->glyphs[0]['w']);
    105 	$widths = array_fill(0, 256, $info['MissingWidth']);
    106 	foreach($map as $c=>$v)
    107 	{
    108 		if($v['name']!='.notdef')
    109 		{
    110 			if(isset($ttf->chars[$v['uv']]))
    111 			{
    112 				$id = $ttf->chars[$v['uv']];
    113 				$w = $ttf->glyphs[$id]['w'];
    114 				$widths[$c] = round($k*$w);
    115 			}
    116 			else
    117 				Warning('Character '.$v['name'].' is missing');
    118 		}
    119 	}
    120 	$info['Widths'] = $widths;
    121 	return $info;
    122 }
    123 
    124 function GetInfoFromType1($file, $embed, $map)
    125 {
    126 	// Return information from a Type1 font
    127 	if($embed)
    128 	{
    129 		$f = fopen($file, 'rb');
    130 		if(!$f)
    131 			Error('Can\'t open font file');
    132 		// Read first segment
    133 		$a = unpack('Cmarker/Ctype/Vsize', fread($f,6));
    134 		if($a['marker']!=128)
    135 			Error('Font file is not a valid binary Type1');
    136 		$size1 = $a['size'];
    137 		$data = fread($f, $size1);
    138 		// Read second segment
    139 		$a = unpack('Cmarker/Ctype/Vsize', fread($f,6));
    140 		if($a['marker']!=128)
    141 			Error('Font file is not a valid binary Type1');
    142 		$size2 = $a['size'];
    143 		$data .= fread($f, $size2);
    144 		fclose($f);
    145 		$info['Data'] = $data;
    146 		$info['Size1'] = $size1;
    147 		$info['Size2'] = $size2;
    148 	}
    149 
    150 	$afm = substr($file, 0, -3).'afm';
    151 	if(!file_exists($afm))
    152 		Error('AFM font file not found: '.$afm);
    153 	$a = file($afm);
    154 	if(empty($a))
    155 		Error('AFM file empty or not readable');
    156 	foreach($a as $line)
    157 	{
    158 		$e = explode(' ', rtrim($line));
    159 		if(count($e)<2)
    160 			continue;
    161 		$entry = $e[0];
    162 		if($entry=='C')
    163 		{
    164 			$w = $e[4];
    165 			$name = $e[7];
    166 			$cw[$name] = $w;
    167 		}
    168 		elseif($entry=='FontName')
    169 			$info['FontName'] = $e[1];
    170 		elseif($entry=='Weight')
    171 			$info['Weight'] = $e[1];
    172 		elseif($entry=='ItalicAngle')
    173 			$info['ItalicAngle'] = (int)$e[1];
    174 		elseif($entry=='Ascender')
    175 			$info['Ascender'] = (int)$e[1];
    176 		elseif($entry=='Descender')
    177 			$info['Descender'] = (int)$e[1];
    178 		elseif($entry=='UnderlineThickness')
    179 			$info['UnderlineThickness'] = (int)$e[1];
    180 		elseif($entry=='UnderlinePosition')
    181 			$info['UnderlinePosition'] = (int)$e[1];
    182 		elseif($entry=='IsFixedPitch')
    183 			$info['IsFixedPitch'] = ($e[1]=='true');
    184 		elseif($entry=='FontBBox')
    185 			$info['FontBBox'] = array((int)$e[1], (int)$e[2], (int)$e[3], (int)$e[4]);
    186 		elseif($entry=='CapHeight')
    187 			$info['CapHeight'] = (int)$e[1];
    188 		elseif($entry=='StdVW')
    189 			$info['StdVW'] = (int)$e[1];
    190 	}
    191 
    192 	if(!isset($info['FontName']))
    193 		Error('FontName missing in AFM file');
    194 	if(!isset($info['Ascender']))
    195 		$info['Ascender'] = $info['FontBBox'][3];
    196 	if(!isset($info['Descender']))
    197 		$info['Descender'] = $info['FontBBox'][1];
    198 	$info['Bold'] = isset($info['Weight']) && preg_match('/bold|black/i', $info['Weight']);
    199 	if(isset($cw['.notdef']))
    200 		$info['MissingWidth'] = $cw['.notdef'];
    201 	else
    202 		$info['MissingWidth'] = 0;
    203 	$widths = array_fill(0, 256, $info['MissingWidth']);
    204 	foreach($map as $c=>$v)
    205 	{
    206 		if($v['name']!='.notdef')
    207 		{
    208 			if(isset($cw[$v['name']]))
    209 				$widths[$c] = $cw[$v['name']];
    210 			else
    211 				Warning('Character '.$v['name'].' is missing');
    212 		}
    213 	}
    214 	$info['Widths'] = $widths;
    215 	return $info;
    216 }
    217 
    218 function MakeFontDescriptor($info)
    219 {
    220 	// Ascent
    221 	$fd = "array('Ascent'=>".$info['Ascender'];
    222 	// Descent
    223 	$fd .= ",'Descent'=>".$info['Descender'];
    224 	// CapHeight
    225 	if(!empty($info['CapHeight']))
    226 		$fd .= ",'CapHeight'=>".$info['CapHeight'];
    227 	else
    228 		$fd .= ",'CapHeight'=>".$info['Ascender'];
    229 	// Flags
    230 	$flags = 0;
    231 	if($info['IsFixedPitch'])
    232 		$flags += 1<<0;
    233 	$flags += 1<<5;
    234 	if($info['ItalicAngle']!=0)
    235 		$flags += 1<<6;
    236 	$fd .= ",'Flags'=>".$flags;
    237 	// FontBBox
    238 	$fbb = $info['FontBBox'];
    239 	$fd .= ",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'";
    240 	// ItalicAngle
    241 	$fd .= ",'ItalicAngle'=>".$info['ItalicAngle'];
    242 	// StemV
    243 	if(isset($info['StdVW']))
    244 		$stemv = $info['StdVW'];
    245 	elseif($info['Bold'])
    246 		$stemv = 120;
    247 	else
    248 		$stemv = 70;
    249 	$fd .= ",'StemV'=>".$stemv;
    250 	// MissingWidth
    251 	$fd .= ",'MissingWidth'=>".$info['MissingWidth'].')';
    252 	return $fd;
    253 }
    254 
    255 function MakeWidthArray($widths)
    256 {
    257 	$s = "array(\n\t";
    258 	for($c=0;$c<=255;$c++)
    259 	{
    260 		if(chr($c)=="'")
    261 			$s .= "'\\''";
    262 		elseif(chr($c)=="\\")
    263 			$s .= "'\\\\'";
    264 		elseif($c>=32 && $c<=126)
    265 			$s .= "'".chr($c)."'";
    266 		else
    267 			$s .= "chr($c)";
    268 		$s .= '=>'.$widths[$c];
    269 		if($c<255)
    270 			$s .= ',';
    271 		if(($c+1)%22==0)
    272 			$s .= "\n\t";
    273 	}
    274 	$s .= ')';
    275 	return $s;
    276 }
    277 
    278 function MakeFontEncoding($map)
    279 {
    280 	// Build differences from reference encoding
    281 	$ref = LoadMap('cp1252');
    282 	$s = '';
    283 	$last = 0;
    284 	for($c=32;$c<=255;$c++)
    285 	{
    286 		if($map[$c]['name']!=$ref[$c]['name'])
    287 		{
    288 			if($c!=$last+1)
    289 				$s .= $c.' ';
    290 			$last = $c;
    291 			$s .= '/'.$map[$c]['name'].' ';
    292 		}
    293 	}
    294 	return rtrim($s);
    295 }
    296 
    297 function MakeUnicodeArray($map)
    298 {
    299 	// Build mapping to Unicode values
    300 	$ranges = array();
    301 	foreach($map as $c=>$v)
    302 	{
    303 		$uv = $v['uv'];
    304 		if($uv!=-1)
    305 		{
    306 			if(isset($range))
    307 			{
    308 				if($c==$range[1]+1 && $uv==$range[3]+1)
    309 				{
    310 					$range[1]++;
    311 					$range[3]++;
    312 				}
    313 				else
    314 				{
    315 					$ranges[] = $range;
    316 					$range = array($c, $c, $uv, $uv);
    317 				}
    318 			}
    319 			else
    320 				$range = array($c, $c, $uv, $uv);
    321 		}
    322 	}
    323 	$ranges[] = $range;
    324 
    325 	foreach($ranges as $range)
    326 	{
    327 		if(isset($s))
    328 			$s .= ',';
    329 		else
    330 			$s = 'array(';
    331 		$s .= $range[0].'=>';
    332 		$nb = $range[1]-$range[0]+1;
    333 		if($nb>1)
    334 			$s .= 'array('.$range[2].','.$nb.')';
    335 		else
    336 			$s .= $range[2];
    337 	}
    338 	$s .= ')';
    339 	return $s;
    340 }
    341 
    342 function SaveToFile($file, $s, $mode)
    343 {
    344 	$f = fopen($file, 'w'.$mode);
    345 	if(!$f)
    346 		Error('Can\'t write to file '.$file);
    347 	fwrite($f, $s);
    348 	fclose($f);
    349 }
    350 
    351 function MakeDefinitionFile($file, $type, $enc, $embed, $subset, $map, $info)
    352 {
    353 	$s = "<?php\n";
    354 	$s .= '$type = \''.$type."';\n";
    355 	$s .= '$name = \''.$info['FontName']."';\n";
    356 	$s .= '$desc = '.MakeFontDescriptor($info).";\n";
    357 	$s .= '$up = '.$info['UnderlinePosition'].";\n";
    358 	$s .= '$ut = '.$info['UnderlineThickness'].";\n";
    359 	$s .= '$cw = '.MakeWidthArray($info['Widths']).";\n";
    360 	$s .= '$enc = \''.$enc."';\n";
    361 	$diff = MakeFontEncoding($map);
    362 	if($diff)
    363 		$s .= '$diff = \''.$diff."';\n";
    364 	$s .= '$uv = '.MakeUnicodeArray($map).";\n";
    365 	if($embed)
    366 	{
    367 		$s .= '$file = \''.$info['File']."';\n";
    368 		if($type=='Type1')
    369 		{
    370 			$s .= '$size1 = '.$info['Size1'].";\n";
    371 			$s .= '$size2 = '.$info['Size2'].";\n";
    372 		}
    373 		else
    374 		{
    375 			$s .= '$originalsize = '.$info['OriginalSize'].";\n";
    376 			if($subset)
    377 				$s .= "\$subsetted = true;\n";
    378 		}
    379 	}
    380 	$s .= "?>\n";
    381 	SaveToFile($file, $s, 't');
    382 }
    383 
    384 function MakeFont($fontfile, $enc='cp1252', $embed=true, $subset=true)
    385 {
    386 	// Generate a font definition file
    387 	if(!file_exists($fontfile))
    388 		Error('Font file not found: '.$fontfile);
    389 	$ext = strtolower(substr($fontfile,-3));
    390 	if($ext=='ttf' || $ext=='otf')
    391 		$type = 'TrueType';
    392 	elseif($ext=='pfb')
    393 		$type = 'Type1';
    394 	else
    395 		Error('Unrecognized font file extension: '.$ext);
    396 
    397 	$map = LoadMap($enc);
    398 
    399 	if($type=='TrueType')
    400 		$info = GetInfoFromTrueType($fontfile, $embed, $subset, $map);
    401 	else
    402 		$info = GetInfoFromType1($fontfile, $embed, $map);
    403 
    404 	$basename = substr(basename($fontfile), 0, -4);
    405 	if($embed)
    406 	{
    407 		if(function_exists('gzcompress'))
    408 		{
    409 			$file = $basename.'.z';
    410 			SaveToFile($file, gzcompress($info['Data']), 'b');
    411 			$info['File'] = $file;
    412 			Message('Font file compressed: '.$file);
    413 		}
    414 		else
    415 		{
    416 			$info['File'] = basename($fontfile);
    417 			$subset = false;
    418 			Notice('Font file could not be compressed (zlib extension not available)');
    419 		}
    420 	}
    421 
    422 	MakeDefinitionFile($basename.'.php', $type, $enc, $embed, $subset, $map, $info);
    423 	Message('Font definition file generated: '.$basename.'.php');
    424 }
    425 
    426 if(PHP_SAPI=='cli')
    427 {
    428 	// Command-line interface
    429 	ini_set('log_errors', '0');
    430 	if($argc==1)
    431 		die("Usage: php makefont.php fontfile [encoding] [embed] [subset]\n");
    432 	$fontfile = $argv[1];
    433 	if($argc>=3)
    434 		$enc = $argv[2];
    435 	else
    436 		$enc = 'cp1252';
    437 	if($argc>=4)
    438 		$embed = ($argv[3]=='true' || $argv[3]=='1');
    439 	else
    440 		$embed = true;
    441 	if($argc>=5)
    442 		$subset = ($argv[4]=='true' || $argv[4]=='1');
    443 	else
    444 		$subset = true;
    445 	MakeFont($fontfile, $enc, $embed, $subset);
    446 }
    447 ?>