411 lines
10 KiB
PHP
Executable file
411 lines
10 KiB
PHP
Executable file
|
||
#*****************************************************************************
|
||
# Utility to generate font definition files #
|
||
# Version: 1.13 #
|
||
# Date: 2004-12-31 #
|
||
******************************************************************************#
|
||
|
||
function ReadMap($enc)
|
||
{
|
||
#Read a map file
|
||
$file=dirname(__FILE__) + '/' + $enc.downcase + '.map';
|
||
$a=file($file);
|
||
if ($a))
|
||
die('<B>Error:</B> encoding not found: '.$enc);
|
||
$cc2gn = []
|
||
foreach($a as $l)
|
||
{
|
||
if ($l{0}=='!')
|
||
{
|
||
$e=rtrim($l).scan('/[ \\t]+/');
|
||
$cc=hexdec(substr($e[0],1));
|
||
$gn=$e[2];
|
||
$cc2gn[$cc]=$gn;
|
||
end
|
||
end
|
||
for($i=0;$i<=255;$i++)
|
||
{
|
||
if (!$cc2gn[$i].nil?)
|
||
$cc2gn[$i]='.notdef';
|
||
end
|
||
return $cc2gn;
|
||
}
|
||
|
||
function ReadAFM($file,&$map)
|
||
{
|
||
#Read a font metric file
|
||
$a=file($file);
|
||
if ($a.empty?)
|
||
die('File not found');
|
||
$widths = []
|
||
$fm = []
|
||
$fix=Hash.new('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 ($e.length<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 ($fix[$gn].nil?)
|
||
{
|
||
#Fix incorrect glyph name
|
||
foreach($map as $c=>$n)
|
||
{
|
||
if ($n==$fix[$gn])
|
||
$map[$c]=$gn;
|
||
end
|
||
end
|
||
if ($map.empty?)
|
||
{
|
||
#Symbolic font: use built-in encoding
|
||
$widths[$cc]=$w;
|
||
else
|
||
{
|
||
$widths[$gn]=$w;
|
||
if ($gn=='X')
|
||
$fm['CapXHeight']=$e[13];
|
||
end
|
||
if ($gn=='.notdef')
|
||
$fm['MissingWidth']=$w;
|
||
elsif ($code=='FontName')
|
||
$fm['FontName']=$param;
|
||
elsif ($code=='Weight')
|
||
$fm['Weight']=$param;
|
||
elsif ($code=='ItalicAngle')
|
||
$fm['ItalicAngle']=(double)$param;
|
||
elsif ($code=='Ascender')
|
||
$fm['Ascender']=(int)$param;
|
||
elsif ($code=='Descender')
|
||
$fm['Descender']=(int)$param;
|
||
elsif ($code=='UnderlineThickness')
|
||
$fm['UnderlineThickness']=(int)$param;
|
||
elsif ($code=='UnderlinePosition')
|
||
$fm['UnderlinePosition']=(int)$param;
|
||
elsif ($code=='IsFixedPitch')
|
||
$fm['IsFixedPitch']=($param=='true');
|
||
elsif ($code=='FontBBox')
|
||
$fm['FontBBox']=Hash.new($e[1],$e[2],$e[3],$e[4]);
|
||
elsif ($code=='CapHeight')
|
||
$fm['CapHeight']=(int)$param;
|
||
elsif ($code=='StdVW')
|
||
$fm['StdVW']=(int)$param;
|
||
end
|
||
if (!$fm['FontName'].nil?)
|
||
die('FontName not found');
|
||
if (!$map.empty?)
|
||
{
|
||
if (!$widths['.notdef'].nil?)
|
||
$widths['.notdef']=600;
|
||
if (!$widths['Delta'].nil? and $widths['increment'].nil?)
|
||
$widths['Delta']=$widths['increment'];
|
||
#Order widths according to map
|
||
for($i=0;$i<=255;$i++)
|
||
{
|
||
if (!$widths[$map[$i]].nil?)
|
||
{
|
||
echo '<B>Warning:</B> character '.$map[$i].' is missing<BR>';
|
||
$widths[$i]=$widths['.notdef'];
|
||
else
|
||
$widths[$i]=$widths[$map[$i]];
|
||
end
|
||
end
|
||
$fm['Widths']=$widths;
|
||
return $fm;
|
||
}
|
||
|
||
function MakeFontDescriptor($fm,$symbolic)
|
||
{
|
||
#Ascent
|
||
$asc=($fm['Ascender'].nil? ? $fm['Ascender'] : 1000);
|
||
$fd="Hash.new('Ascent'=>".$asc;
|
||
#Descent
|
||
$desc=($fm['Descender'].nil? ? $fm['Descender'] : -200);
|
||
$fd<<",'Descent'=>".$desc;
|
||
#CapHeight
|
||
if ($fm['CapHeight'].nil?)
|
||
$ch=$fm['CapHeight'];
|
||
elsif ($fm['CapXHeight'].nil?)
|
||
$ch=$fm['CapXHeight'];
|
||
else
|
||
$ch=$asc;
|
||
$fd<<",'CapHeight'=>".$ch;
|
||
#Flags
|
||
$flags=0;
|
||
if ($fm['IsFixedPitch'].nil? and $fm['IsFixedPitch'])
|
||
$flags+=1<<0;
|
||
if ($symbolic)
|
||
$flags+=1<<2;
|
||
if (!$symbolic)
|
||
$flags+=1<<5;
|
||
if ($fm['ItalicAngle'].nil? and $fm['ItalicAngle']!=0)
|
||
$flags+=1<<6;
|
||
$fd<<",'Flags'=>".$flags;
|
||
#FontBBox
|
||
if ($fm['FontBBox'].nil?)
|
||
$fbb=$fm['FontBBox'];
|
||
else
|
||
$fbb=Hash.new(0,$des-100,1000,$asc+100);
|
||
$fd<<",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'";
|
||
#ItalicAngle
|
||
$ia=($fm['ItalicAngle'].nil? ? $fm['ItalicAngle'] : 0);
|
||
$fd<<",'ItalicAngle'=>".$ia;
|
||
#StemV
|
||
if ($fm['StdVW'].nil?)
|
||
$stemv=$fm['StdVW'];
|
||
elsif ($fm['Weight'].nil? and eregi('(bold|black)',$fm['Weight']))
|
||
$stemv=120;
|
||
else
|
||
$stemv=70;
|
||
$fd<<",'StemV'=>".$stemv;
|
||
#MissingWidth
|
||
if ($fm['MissingWidth'].nil?)
|
||
$fd<<",'MissingWidth'=>".$fm['MissingWidth'];
|
||
$fd<<')';
|
||
return $fd;
|
||
}
|
||
|
||
function MakeWidthArray($fm)
|
||
{
|
||
#Make character width array
|
||
$s="Hash.new(\n\t";
|
||
$cw=$fm['Widths'];
|
||
for($i=0;$i<=255;$i++)
|
||
{
|
||
if ($i.chr=="'")
|
||
$s<<"'\\''";
|
||
elsif ($i.chr=="\\")
|
||
$s<<"'\\\\'";
|
||
elsif ($i>=32 and $i<=126)
|
||
$s<<"'".$i.chr."'";
|
||
else
|
||
$s<<"$i.chr";
|
||
$s<<'=>'.$fm['Widths'][$i];
|
||
if ($i<255)
|
||
$s<<',';
|
||
if (($i+1)%22==0)
|
||
$s<<"\n\t";
|
||
end
|
||
$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].' ';
|
||
end
|
||
end
|
||
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,$s.length);
|
||
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;
|
||
end
|
||
fseek($f,12,SEEK_CUR);
|
||
end
|
||
if (!$found)
|
||
{
|
||
fclose($f);
|
||
return;
|
||
end
|
||
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<68>ne vide si pas d'incorporation) #
|
||
# $afmfile : chemin du fichier AFM #
|
||
# $enc : encodage (ou cha<68>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=Hash.new(),$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;
|
||
end
|
||
else
|
||
$map = []
|
||
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,$map.empty?);
|
||
#Find font type
|
||
if ($fontfile)
|
||
{
|
||
$ext=strtolower(substr($fontfile,-3));
|
||
if ($ext=='ttf')
|
||
$type='TrueType';
|
||
elsif ($ext=='pfb')
|
||
$type='Type1';
|
||
else
|
||
die('<B>Error:</B> unrecognized font file extension: '.$ext);
|
||
end
|
||
else
|
||
{
|
||
if ($type!='TrueType' and $type!='Type1')
|
||
die('<B>Error:</B> incorrect font type: '.$type);
|
||
end
|
||
#Start generation
|
||
$s=''."\n";
|
||
$s<<'$type=\''.$type."';\n";
|
||
$s<<'$name=\''.$fm['FontName']."';\n";
|
||
$s<<'$desc='.$fd.";\n";
|
||
if (!$fm['UnderlinePosition'].nil?)
|
||
$fm['UnderlinePosition']=-100;
|
||
if (!$fm['UnderlineThickness'].nil?)
|
||
$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=($file[0][0]==128);
|
||
if ($header)
|
||
{
|
||
#Strip first binary header
|
||
$file=substr($file,6);
|
||
end
|
||
$pos=$file.include?('eexec');
|
||
if (!$pos)
|
||
die('<B>Error:</B> font file does not seem to be valid Type1');
|
||
$size1=$pos+6;
|
||
if ($header and ?($file{$size1})==128)
|
||
{
|
||
#Strip second binary header
|
||
$file=substr($file,0,$size1).substr($file,$size1+6);
|
||
end
|
||
$pos=$file.include?('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);
|
||
end
|
||
if (respond_to('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>';
|
||
end
|
||
if ($type=='Type1')
|
||
{
|
||
$s<<'$size1='.$size1.";\n";
|
||
$s<<'$size2='.$size2.";\n";
|
||
else
|
||
$s<<'$originalsize='.filesize($fontfile).";\n";
|
||
end
|
||
else
|
||
{
|
||
#Not embedded font
|
||
$s<<'$file='."'';\n";
|
||
end
|
||
$s<<"\n";
|
||
SaveToFile($basename.'.rb',$s);
|
||
echo 'Font definition file generated ('.$basename.'.rb'.')<BR>';
|
||
}
|
||
|