<?php

/*******************************************************************************

* Utility to generate font definition files                                    *

* Version: 1.13                                                                *

* Date:    2004-12-31                                                          *

*******************************************************************************/



function ReadMap($enc)

{

	//Read a map file

	$file=dirname(__FILE__).'/'.strtolower($enc).'.map';

	$a=file($file);

	if(empty($a))

		die('<B>Error:</B> encoding not found: '.$enc);

	$cc2gn=array();

	foreach($a as $l)

	{

		if($l{0}=='!')

		{

			$e=preg_split('/[ \\t]+/',rtrim($l));

			$cc=hexdec(substr($e[0],1));

			$gn=$e[2];

			$cc2gn[$cc]=$gn;

		}

	}

	for($i=0;$i<=255;$i++)

	{

		if(!isset($cc2gn[$i]))

			$cc2gn[$i]='.notdef';

	}

	return $cc2gn;

}



function ReadAFM($file,&$map)

{

	//Read a font metric file

	$a=file($file);

	if(empty($a))

		die('File not found');

	$widths=array();

	$fm=array();

	$fix=array('Edot'=>'Edotaccent','edot'=>'edotaccent','Idot'=>'Idotaccent','Zdot'=>'Zdotaccent','zdot'=>'zdotaccent',

		'Odblacute'=>'Ohungarumlaut','odblacute'=>'ohungarumlaut','Udblacute'=>'Uhungarumlaut','udblacute'=>'uhungarumlaut',

		'Gcedilla'=>'Gcommaaccent','gcedilla'=>'gcommaaccent','Kcedilla'=>'Kcommaaccent','kcedilla'=>'kcommaaccent',

		'Lcedilla'=>'Lcommaaccent','lcedilla'=>'lcommaaccent','Ncedilla'=>'Ncommaaccent','ncedilla'=>'ncommaaccent',

		'Rcedilla'=>'Rcommaaccent','rcedilla'=>'rcommaaccent','Scedilla'=>'Scommaaccent','scedilla'=>'scommaaccent',

		'Tcedilla'=>'Tcommaaccent','tcedilla'=>'tcommaaccent','Dslash'=>'Dcroat','dslash'=>'dcroat','Dmacron'=>'Dcroat','dmacron'=>'dcroat',

		'combininggraveaccent'=>'gravecomb','combininghookabove'=>'hookabovecomb','combiningtildeaccent'=>'tildecomb',

		'combiningacuteaccent'=>'acutecomb','combiningdotbelow'=>'dotbelowcomb','dongsign'=>'dong');

	foreach($a as $l)

	{

		$e=explode(' ',rtrim($l));

		if(count($e)<2)

			continue;

		$code=$e[0];

		$param=$e[1];

		if($code=='C')

		{

			//Character metrics

			$cc=(int)$e[1];

			$w=$e[4];

			$gn=$e[7];

			if(substr($gn,-4)=='20AC')

				$gn='Euro';

			if(isset($fix[$gn]))

			{

				//Fix incorrect glyph name

				foreach($map as $c=>$n)

				{

					if($n==$fix[$gn])

						$map[$c]=$gn;

				}

			}

			if(empty($map))

			{

				//Symbolic font: use built-in encoding

				$widths[$cc]=$w;

			}

			else

			{

				$widths[$gn]=$w;

				if($gn=='X')

					$fm['CapXHeight']=$e[13];

			}

			if($gn=='.notdef')

				$fm['MissingWidth']=$w;

		}

		elseif($code=='FontName')

			$fm['FontName']=$param;

		elseif($code=='Weight')

			$fm['Weight']=$param;

		elseif($code=='ItalicAngle')

			$fm['ItalicAngle']=(double)$param;

		elseif($code=='Ascender')

			$fm['Ascender']=(int)$param;

		elseif($code=='Descender')

			$fm['Descender']=(int)$param;

		elseif($code=='UnderlineThickness')

			$fm['UnderlineThickness']=(int)$param;

		elseif($code=='UnderlinePosition')

			$fm['UnderlinePosition']=(int)$param;

		elseif($code=='IsFixedPitch')

			$fm['IsFixedPitch']=($param=='true');

		elseif($code=='FontBBox')

			$fm['FontBBox']=array($e[1],$e[2],$e[3],$e[4]);

		elseif($code=='CapHeight')

			$fm['CapHeight']=(int)$param;

		elseif($code=='StdVW')

			$fm['StdVW']=(int)$param;

	}

	if(!isset($fm['FontName']))

		die('FontName not found');

	if(!empty($map))

	{

		if(!isset($widths['.notdef']))

			$widths['.notdef']=600;

		if(!isset($widths['Delta']) and isset($widths['increment']))

			$widths['Delta']=$widths['increment'];

		//Order widths according to map

		for($i=0;$i<=255;$i++)

		{

			if(!isset($widths[$map[$i]]))

			{

				echo '<B>Warning:</B> character '.$map[$i].' is missing<BR>';

				$widths[$i]=$widths['.notdef'];

			}

			else

				$widths[$i]=$widths[$map[$i]];

		}

	}

	$fm['Widths']=$widths;

	return $fm;

}



function MakeFontDescriptor($fm,$symbolic)

{

	//Ascent

	$asc=(isset($fm['Ascender']) ? $fm['Ascender'] : 1000);

	$fd="array('Ascent'=>".$asc;

	//Descent

	$desc=(isset($fm['Descender']) ? $fm['Descender'] : -200);

	$fd.=",'Descent'=>".$desc;

	//CapHeight

	if(isset($fm['CapHeight']))

		$ch=$fm['CapHeight'];

	elseif(isset($fm['CapXHeight']))

		$ch=$fm['CapXHeight'];

	else

		$ch=$asc;

	$fd.=",'CapHeight'=>".$ch;

	//Flags

	$flags=0;

	if(isset($fm['IsFixedPitch']) and $fm['IsFixedPitch'])

		$flags+=1<<0;

	if($symbolic)

		$flags+=1<<2;

	if(!$symbolic)

		$flags+=1<<5;

	if(isset($fm['ItalicAngle']) and $fm['ItalicAngle']!=0)

		$flags+=1<<6;

	$fd.=",'Flags'=>".$flags;

	//FontBBox

	if(isset($fm['FontBBox']))

		$fbb=$fm['FontBBox'];

	else

		$fbb=array(0,$des-100,1000,$asc+100);

	$fd.=",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'";

	//ItalicAngle

	$ia=(isset($fm['ItalicAngle']) ? $fm['ItalicAngle'] : 0);

	$fd.=",'ItalicAngle'=>".$ia;

	//StemV

	if(isset($fm['StdVW']))

		$stemv=$fm['StdVW'];

	elseif(isset($fm['Weight']) and eregi('(bold|black)',$fm['Weight']))

		$stemv=120;

	else

		$stemv=70;

	$fd.=",'StemV'=>".$stemv;

	//MissingWidth

	if(isset($fm['MissingWidth']))

		$fd.=",'MissingWidth'=>".$fm['MissingWidth'];

	$fd.=')';

	return $fd;

}



function MakeWidthArray($fm)

{

	//Make character width array

	$s="array(\n\t";

	$cw=$fm['Widths'];

	for($i=0;$i<=255;$i++)

	{

		if(chr($i)=="'")

			$s.="'\\''";

		elseif(chr($i)=="\\")

			$s.="'\\\\'";

		elseif($i>=32 and $i<=126)

			$s.="'".chr($i)."'";

		else

			$s.="chr($i)";

		$s.='=>'.$fm['Widths'][$i];

		if($i<255)

			$s.=',';

		if(($i+1)%22==0)

			$s.="\n\t";

	}

	$s.=')';

	return $s;

}



function MakeFontEncoding($map)

{

	//Build differences from reference encoding

	$ref=ReadMap('cp1252');

	$s='';

	$last=0;

	for($i=32;$i<=255;$i++)

	{

		if($map[$i]!=$ref[$i])

		{

			if($i!=$last+1)

				$s.=$i.' ';

			$last=$i;

			$s.='/'.$map[$i].' ';

		}

	}

	return rtrim($s);

}



function SaveToFile($file,$s,$mode='t')

{

	$f=fopen($file,'w'.$mode);

	if(!$f)

		die('Can\'t write to file '.$file);

	fwrite($f,$s,strlen($s));

	fclose($f);

}



function ReadShort($f)

{

	$a=unpack('n1n',fread($f,2));

	return $a['n'];

}



function ReadLong($f)

{

	$a=unpack('N1N',fread($f,4));

	return $a['N'];

}



function CheckTTF($file)

{

	//Check if font license allows embedding

	$f=fopen($file,'rb');

	if(!$f)

		die('<B>Error:</B> Can\'t open '.$file);

	//Extract number of tables

	fseek($f,4,SEEK_CUR);

	$nb=ReadShort($f);

	fseek($f,6,SEEK_CUR);

	//Seek OS/2 table

	$found=false;

	for($i=0;$i<$nb;$i++)

	{

		if(fread($f,4)=='OS/2')

		{

			$found=true;

			break;

		}

		fseek($f,12,SEEK_CUR);

	}

	if(!$found)

	{

		fclose($f);

		return;

	}

	fseek($f,4,SEEK_CUR);

	$offset=ReadLong($f);

	fseek($f,$offset,SEEK_SET);

	//Extract fsType flags

	fseek($f,8,SEEK_CUR);

	$fsType=ReadShort($f);

	$rl=($fsType & 0x02)!=0;

	$pp=($fsType & 0x04)!=0;

	$e=($fsType & 0x08)!=0;

	fclose($f);

	if($rl and !$pp and !$e)

		echo '<B>Warning:</B> font license does not allow embedding';

}



/*******************************************************************************

* $fontfile : chemin du fichier TTF (ou chaîne vide si pas d'incorporation)    *

* $afmfile :  chemin du fichier AFM                                            *

* $enc :      encodage (ou chaîne vide si la police est symbolique)            *

* $patch :    patch optionnel pour l'encodage                                  *

* $type :     type de la police si $fontfile est vide                          *

*******************************************************************************/

function MakeFont($fontfile,$afmfile,$enc='cp1252',$patch=array(),$type='TrueType')

{

	//Generate a font definition file

	set_magic_quotes_runtime(0);

	ini_set('auto_detect_line_endings','1');

	if($enc)

	{

		$map=ReadMap($enc);

		foreach($patch as $cc=>$gn)

			$map[$cc]=$gn;

	}

	else

		$map=array();

	if(!file_exists($afmfile))

		die('<B>Error:</B> AFM file not found: '.$afmfile);

	$fm=ReadAFM($afmfile,$map);

	if($enc)

		$diff=MakeFontEncoding($map);

	else

		$diff='';

	$fd=MakeFontDescriptor($fm,empty($map));

	//Find font type

	if($fontfile)

	{

		$ext=strtolower(substr($fontfile,-3));

		if($ext=='ttf')

			$type='TrueType';

		elseif($ext=='pfb')

			$type='Type1';

		else

			die('<B>Error:</B> unrecognized font file extension: '.$ext);

	}

	else

	{

		if($type!='TrueType' and $type!='Type1')

			die('<B>Error:</B> incorrect font type: '.$type);

	}

	//Start generation

	$s='<?php'."\n";

	$s.='$type=\''.$type."';\n";

	$s.='$name=\''.$fm['FontName']."';\n";

	$s.='$desc='.$fd.";\n";

	if(!isset($fm['UnderlinePosition']))

		$fm['UnderlinePosition']=-100;

	if(!isset($fm['UnderlineThickness']))

		$fm['UnderlineThickness']=50;

	$s.='$up='.$fm['UnderlinePosition'].";\n";

	$s.='$ut='.$fm['UnderlineThickness'].";\n";

	$w=MakeWidthArray($fm);

	$s.='$cw='.$w.";\n";

	$s.='$enc=\''.$enc."';\n";

	$s.='$diff=\''.$diff."';\n";

	$basename=substr(basename($afmfile),0,-4);

	if($fontfile)

	{

		//Embedded font

		if(!file_exists($fontfile))

			die('<B>Error:</B> font file not found: '.$fontfile);

		if($type=='TrueType')

			CheckTTF($fontfile);

		$f=fopen($fontfile,'rb');

		if(!$f)

			die('<B>Error:</B> Can\'t open '.$fontfile);

		$file=fread($f,filesize($fontfile));

		fclose($f);

		if($type=='Type1')

		{

			//Find first two sections and discard third one

			$header=(ord($file{0})==128);

			if($header)

			{

				//Strip first binary header

				$file=substr($file,6);

			}

			$pos=strpos($file,'eexec');

			if(!$pos)

				die('<B>Error:</B> font file does not seem to be valid Type1');

			$size1=$pos+6;

			if($header and ord($file{$size1})==128)

			{

				//Strip second binary header

				$file=substr($file,0,$size1).substr($file,$size1+6);

			}

			$pos=strpos($file,'00000000');

			if(!$pos)

				die('<B>Error:</B> font file does not seem to be valid Type1');

			$size2=$pos-$size1;

			$file=substr($file,0,$size1+$size2);

		}

		if(function_exists('gzcompress'))

		{

			$cmp=$basename.'.z';

			SaveToFile($cmp,gzcompress($file),'b');

			$s.='$file=\''.$cmp."';\n";

			echo 'Font file compressed ('.$cmp.')<BR>';

		}

		else

		{

			$s.='$file=\''.basename($fontfile)."';\n";

			echo '<B>Notice:</B> font file could not be compressed (zlib extension not available)<BR>';

		}

		if($type=='Type1')

		{

			$s.='$size1='.$size1.";\n";

			$s.='$size2='.$size2.";\n";

		}

		else

			$s.='$originalsize='.filesize($fontfile).";\n";

	}

	else

	{

		//Not embedded font

		$s.='$file='."'';\n";

	}

	$s.="?>\n";

	SaveToFile($basename.'.php',$s);

	echo 'Font definition file generated ('.$basename.'.php'.')<BR>';

}

?>