Chris@909:
Chris@909: #*****************************************************************************
Chris@909: # Utility to generate font definition files #
Chris@909: # Version: 1.13 #
Chris@909: # Date: 2004-12-31 #
Chris@909: ******************************************************************************#
Chris@909:
Chris@909: function ReadMap($enc)
Chris@909: {
Chris@909: #Read a map file
Chris@909: $file=dirname(__FILE__) + '/' + $enc.downcase + '.map';
Chris@909: $a=file($file);
Chris@909: if ($a))
Chris@909: die('Error: encoding not found: '.$enc);
Chris@909: $cc2gn = []
Chris@909: foreach($a as $l)
Chris@909: {
Chris@909: if ($l{0}=='!')
Chris@909: {
Chris@909: $e=rtrim($l).scan('/[ \\t]+/');
Chris@909: $cc=hexdec(substr($e[0],1));
Chris@909: $gn=$e[2];
Chris@909: $cc2gn[$cc]=$gn;
Chris@909: end
Chris@909: end
Chris@909: for($i=0;$i<=255;$i++)
Chris@909: {
Chris@909: if (!$cc2gn[$i].nil?)
Chris@909: $cc2gn[$i]='.notdef';
Chris@909: end
Chris@909: return $cc2gn;
Chris@909: }
Chris@909:
Chris@909: function ReadAFM($file,&$map)
Chris@909: {
Chris@909: #Read a font metric file
Chris@909: $a=file($file);
Chris@909: if ($a.empty?)
Chris@909: die('File not found');
Chris@909: $widths = []
Chris@909: $fm = []
Chris@909: $fix=Hash.new('Edot'=>'Edotaccent','edot'=>'edotaccent','Idot'=>'Idotaccent','Zdot'=>'Zdotaccent','zdot'=>'zdotaccent',
Chris@909: 'Odblacute'=>'Ohungarumlaut','odblacute'=>'ohungarumlaut','Udblacute'=>'Uhungarumlaut','udblacute'=>'uhungarumlaut',
Chris@909: 'Gcedilla'=>'Gcommaaccent','gcedilla'=>'gcommaaccent','Kcedilla'=>'Kcommaaccent','kcedilla'=>'kcommaaccent',
Chris@909: 'Lcedilla'=>'Lcommaaccent','lcedilla'=>'lcommaaccent','Ncedilla'=>'Ncommaaccent','ncedilla'=>'ncommaaccent',
Chris@909: 'Rcedilla'=>'Rcommaaccent','rcedilla'=>'rcommaaccent','Scedilla'=>'Scommaaccent','scedilla'=>'scommaaccent',
Chris@909: 'Tcedilla'=>'Tcommaaccent','tcedilla'=>'tcommaaccent','Dslash'=>'Dcroat','dslash'=>'dcroat','Dmacron'=>'Dcroat','dmacron'=>'dcroat',
Chris@909: 'combininggraveaccent'=>'gravecomb','combininghookabove'=>'hookabovecomb','combiningtildeaccent'=>'tildecomb',
Chris@909: 'combiningacuteaccent'=>'acutecomb','combiningdotbelow'=>'dotbelowcomb','dongsign'=>'dong');
Chris@909: foreach($a as $l)
Chris@909: {
Chris@909: $e=explode(' ',rtrim($l));
Chris@909: if ($e.length<2)
Chris@909: continue;
Chris@909: $code=$e[0];
Chris@909: $param=$e[1];
Chris@909: if ($code=='C')
Chris@909: {
Chris@909: #Character metrics
Chris@909: $cc=(int)$e[1];
Chris@909: $w=$e[4];
Chris@909: $gn=$e[7];
Chris@909: if (substr($gn,-4)=='20AC')
Chris@909: $gn='Euro';
Chris@909: if ($fix[$gn].nil?)
Chris@909: {
Chris@909: #Fix incorrect glyph name
Chris@909: foreach($map as $c=>$n)
Chris@909: {
Chris@909: if ($n==$fix[$gn])
Chris@909: $map[$c]=$gn;
Chris@909: end
Chris@909: end
Chris@909: if ($map.empty?)
Chris@909: {
Chris@909: #Symbolic font: use built-in encoding
Chris@909: $widths[$cc]=$w;
Chris@909: else
Chris@909: {
Chris@909: $widths[$gn]=$w;
Chris@909: if ($gn=='X')
Chris@909: $fm['CapXHeight']=$e[13];
Chris@909: end
Chris@909: if ($gn=='.notdef')
Chris@909: $fm['MissingWidth']=$w;
Chris@909: elsif ($code=='FontName')
Chris@909: $fm['FontName']=$param;
Chris@909: elsif ($code=='Weight')
Chris@909: $fm['Weight']=$param;
Chris@909: elsif ($code=='ItalicAngle')
Chris@909: $fm['ItalicAngle']=(double)$param;
Chris@909: elsif ($code=='Ascender')
Chris@909: $fm['Ascender']=(int)$param;
Chris@909: elsif ($code=='Descender')
Chris@909: $fm['Descender']=(int)$param;
Chris@909: elsif ($code=='UnderlineThickness')
Chris@909: $fm['UnderlineThickness']=(int)$param;
Chris@909: elsif ($code=='UnderlinePosition')
Chris@909: $fm['UnderlinePosition']=(int)$param;
Chris@909: elsif ($code=='IsFixedPitch')
Chris@909: $fm['IsFixedPitch']=($param=='true');
Chris@909: elsif ($code=='FontBBox')
Chris@909: $fm['FontBBox']=Hash.new($e[1],$e[2],$e[3],$e[4]);
Chris@909: elsif ($code=='CapHeight')
Chris@909: $fm['CapHeight']=(int)$param;
Chris@909: elsif ($code=='StdVW')
Chris@909: $fm['StdVW']=(int)$param;
Chris@909: end
Chris@909: if (!$fm['FontName'].nil?)
Chris@909: die('FontName not found');
Chris@909: if (!$map.empty?)
Chris@909: {
Chris@909: if (!$widths['.notdef'].nil?)
Chris@909: $widths['.notdef']=600;
Chris@909: if (!$widths['Delta'].nil? and $widths['increment'].nil?)
Chris@909: $widths['Delta']=$widths['increment'];
Chris@909: #Order widths according to map
Chris@909: for($i=0;$i<=255;$i++)
Chris@909: {
Chris@909: if (!$widths[$map[$i]].nil?)
Chris@909: {
Chris@909: echo 'Warning: character '.$map[$i].' is missing
';
Chris@909: $widths[$i]=$widths['.notdef'];
Chris@909: else
Chris@909: $widths[$i]=$widths[$map[$i]];
Chris@909: end
Chris@909: end
Chris@909: $fm['Widths']=$widths;
Chris@909: return $fm;
Chris@909: }
Chris@909:
Chris@909: function MakeFontDescriptor($fm,$symbolic)
Chris@909: {
Chris@909: #Ascent
Chris@909: $asc=($fm['Ascender'].nil? ? $fm['Ascender'] : 1000);
Chris@909: $fd="Hash.new('Ascent'=>".$asc;
Chris@909: #Descent
Chris@909: $desc=($fm['Descender'].nil? ? $fm['Descender'] : -200);
Chris@909: $fd<<",'Descent'=>".$desc;
Chris@909: #CapHeight
Chris@909: if ($fm['CapHeight'].nil?)
Chris@909: $ch=$fm['CapHeight'];
Chris@909: elsif ($fm['CapXHeight'].nil?)
Chris@909: $ch=$fm['CapXHeight'];
Chris@909: else
Chris@909: $ch=$asc;
Chris@909: $fd<<",'CapHeight'=>".$ch;
Chris@909: #Flags
Chris@909: $flags=0;
Chris@909: if ($fm['IsFixedPitch'].nil? and $fm['IsFixedPitch'])
Chris@909: $flags+=1<<0;
Chris@909: if ($symbolic)
Chris@909: $flags+=1<<2;
Chris@909: if (!$symbolic)
Chris@909: $flags+=1<<5;
Chris@909: if ($fm['ItalicAngle'].nil? and $fm['ItalicAngle']!=0)
Chris@909: $flags+=1<<6;
Chris@909: $fd<<",'Flags'=>".$flags;
Chris@909: #FontBBox
Chris@909: if ($fm['FontBBox'].nil?)
Chris@909: $fbb=$fm['FontBBox'];
Chris@909: else
Chris@909: $fbb=Hash.new(0,$des-100,1000,$asc+100);
Chris@909: $fd<<",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'";
Chris@909: #ItalicAngle
Chris@909: $ia=($fm['ItalicAngle'].nil? ? $fm['ItalicAngle'] : 0);
Chris@909: $fd<<",'ItalicAngle'=>".$ia;
Chris@909: #StemV
Chris@909: if ($fm['StdVW'].nil?)
Chris@909: $stemv=$fm['StdVW'];
Chris@909: elsif ($fm['Weight'].nil? and eregi('(bold|black)',$fm['Weight']))
Chris@909: $stemv=120;
Chris@909: else
Chris@909: $stemv=70;
Chris@909: $fd<<",'StemV'=>".$stemv;
Chris@909: #MissingWidth
Chris@909: if ($fm['MissingWidth'].nil?)
Chris@909: $fd<<",'MissingWidth'=>".$fm['MissingWidth'];
Chris@909: $fd<<')';
Chris@909: return $fd;
Chris@909: }
Chris@909:
Chris@909: function MakeWidthArray($fm)
Chris@909: {
Chris@909: #Make character width array
Chris@909: $s="Hash.new(\n\t";
Chris@909: $cw=$fm['Widths'];
Chris@909: for($i=0;$i<=255;$i++)
Chris@909: {
Chris@909: if ($i.chr=="'")
Chris@909: $s<<"'\\''";
Chris@909: elsif ($i.chr=="\\")
Chris@909: $s<<"'\\\\'";
Chris@909: elsif ($i>=32 and $i<=126)
Chris@909: $s<<"'".$i.chr."'";
Chris@909: else
Chris@909: $s<<"$i.chr";
Chris@909: $s<<'=>'.$fm['Widths'][$i];
Chris@909: if ($i<255)
Chris@909: $s<<',';
Chris@909: if (($i+1)%22==0)
Chris@909: $s<<"\n\t";
Chris@909: end
Chris@909: $s<<')';
Chris@909: return $s;
Chris@909: }
Chris@909:
Chris@909: function MakeFontEncoding($map)
Chris@909: {
Chris@909: #Build differences from reference encoding
Chris@909: $ref=ReadMap('cp1252');
Chris@909: $s='';
Chris@909: $last=0;
Chris@909: for($i=32;$i<=255;$i++)
Chris@909: {
Chris@909: if ($map[$i]!=$ref[$i])
Chris@909: {
Chris@909: if ($i!=$last+1)
Chris@909: $s<<$i.' ';
Chris@909: $last=$i;
Chris@909: $s<<'/'.$map[$i].' ';
Chris@909: end
Chris@909: end
Chris@909: return rtrim($s);
Chris@909: }
Chris@909:
Chris@909: function SaveToFile($file,$s,$mode='t')
Chris@909: {
Chris@909: $f=fopen($file,'w'.$mode);
Chris@909: if (!$f)
Chris@909: die('Can\'t write to file '.$file);
Chris@909: fwrite($f,$s,$s.length);
Chris@909: fclose($f);
Chris@909: }
Chris@909:
Chris@909: function ReadShort($f)
Chris@909: {
Chris@909: $a=unpack('n1n',fread($f,2));
Chris@909: return $a['n'];
Chris@909: }
Chris@909:
Chris@909: function ReadLong($f)
Chris@909: {
Chris@909: $a=unpack('N1N',fread($f,4));
Chris@909: return $a['N'];
Chris@909: }
Chris@909:
Chris@909: function CheckTTF($file)
Chris@909: {
Chris@909: #Check if font license allows embedding
Chris@909: $f=fopen($file,'rb');
Chris@909: if (!$f)
Chris@909: die('Error: Can\'t open '.$file);
Chris@909: #Extract number of tables
Chris@909: fseek($f,4,SEEK_CUR);
Chris@909: $nb=ReadShort($f);
Chris@909: fseek($f,6,SEEK_CUR);
Chris@909: #Seek OS/2 table
Chris@909: $found=false;
Chris@909: for($i=0;$i<$nb;$i++)
Chris@909: {
Chris@909: if (fread($f,4)=='OS/2')
Chris@909: {
Chris@909: $found=true;
Chris@909: break;
Chris@909: end
Chris@909: fseek($f,12,SEEK_CUR);
Chris@909: end
Chris@909: if (!$found)
Chris@909: {
Chris@909: fclose($f);
Chris@909: return;
Chris@909: end
Chris@909: fseek($f,4,SEEK_CUR);
Chris@909: $offset=ReadLong($f);
Chris@909: fseek($f,$offset,SEEK_SET);
Chris@909: #Extract fsType flags
Chris@909: fseek($f,8,SEEK_CUR);
Chris@909: $fsType=ReadShort($f);
Chris@909: $rl=($fsType & 0x02)!=0;
Chris@909: $pp=($fsType & 0x04)!=0;
Chris@909: $e=($fsType & 0x08)!=0;
Chris@909: fclose($f);
Chris@909: if ($rl and !$pp and !$e)
Chris@909: echo 'Warning: font license does not allow embedding';
Chris@909: }
Chris@909:
Chris@909: #*****************************************************************************
Chris@909: # $fontfile : chemin du fichier TTF (ou chaîne vide si pas d'incorporation) #
Chris@909: # $afmfile : chemin du fichier AFM #
Chris@909: # $enc : encodage (ou chaîne vide si la police est symbolique) #
Chris@909: # $patch : patch optionnel pour l'encodage #
Chris@909: # $type : type de la police si $fontfile est vide #
Chris@909: ******************************************************************************#
Chris@909: function MakeFont($fontfile,$afmfile,$enc='cp1252',$patch=Hash.new(),$type='TrueType')
Chris@909: {
Chris@909: #Generate a font definition file
Chris@909: set_magic_quotes_runtime(0);
Chris@909: ini_set('auto_detect_line_endings','1');
Chris@909: if ($enc)
Chris@909: {
Chris@909: $map=ReadMap($enc);
Chris@909: foreach($patch as $cc=>$gn)
Chris@909: $map[$cc]=$gn;
Chris@909: end
Chris@909: else
Chris@909: $map = []
Chris@909: if (!file_exists($afmfile))
Chris@909: die('Error: AFM file not found: '.$afmfile);
Chris@909: $fm=ReadAFM($afmfile,$map);
Chris@909: if ($enc)
Chris@909: $diff=MakeFontEncoding($map);
Chris@909: else
Chris@909: $diff='';
Chris@909: $fd=MakeFontDescriptor($fm,$map.empty?);
Chris@909: #Find font type
Chris@909: if ($fontfile)
Chris@909: {
Chris@909: $ext=strtolower(substr($fontfile,-3));
Chris@909: if ($ext=='ttf')
Chris@909: $type='TrueType';
Chris@909: elsif ($ext=='pfb')
Chris@909: $type='Type1';
Chris@909: else
Chris@909: die('Error: unrecognized font file extension: '.$ext);
Chris@909: end
Chris@909: else
Chris@909: {
Chris@909: if ($type!='TrueType' and $type!='Type1')
Chris@909: die('Error: incorrect font type: '.$type);
Chris@909: end
Chris@909: #Start generation
Chris@909: $s=''."\n";
Chris@909: $s<<'$type=\''.$type."';\n";
Chris@909: $s<<'$name=\''.$fm['FontName']."';\n";
Chris@909: $s<<'$desc='.$fd.";\n";
Chris@909: if (!$fm['UnderlinePosition'].nil?)
Chris@909: $fm['UnderlinePosition']=-100;
Chris@909: if (!$fm['UnderlineThickness'].nil?)
Chris@909: $fm['UnderlineThickness']=50;
Chris@909: $s<<'$up='.$fm['UnderlinePosition'].";\n";
Chris@909: $s<<'$ut='.$fm['UnderlineThickness'].";\n";
Chris@909: $w=MakeWidthArray($fm);
Chris@909: $s<<'$cw='.$w.";\n";
Chris@909: $s<<'$enc=\''.$enc."';\n";
Chris@909: $s<<'$diff=\''.$diff."';\n";
Chris@909: $basename=substr(basename($afmfile),0,-4);
Chris@909: if ($fontfile)
Chris@909: {
Chris@909: #Embedded font
Chris@909: if (!file_exists($fontfile))
Chris@909: die('Error: font file not found: '.$fontfile);
Chris@909: if ($type=='TrueType')
Chris@909: CheckTTF($fontfile);
Chris@909: $f=fopen($fontfile,'rb');
Chris@909: if (!$f)
Chris@909: die('Error: Can\'t open '.$fontfile);
Chris@909: $file=fread($f,filesize($fontfile));
Chris@909: fclose($f);
Chris@909: if ($type=='Type1')
Chris@909: {
Chris@909: #Find first two sections and discard third one
Chris@909: $header=($file[0][0]==128);
Chris@909: if ($header)
Chris@909: {
Chris@909: #Strip first binary header
Chris@909: $file=substr($file,6);
Chris@909: end
Chris@909: $pos=$file.include?('eexec');
Chris@909: if (!$pos)
Chris@909: die('Error: font file does not seem to be valid Type1');
Chris@909: $size1=$pos+6;
Chris@909: if ($header and ?($file{$size1})==128)
Chris@909: {
Chris@909: #Strip second binary header
Chris@909: $file=substr($file,0,$size1).substr($file,$size1+6);
Chris@909: end
Chris@909: $pos=$file.include?('00000000');
Chris@909: if (!$pos)
Chris@909: die('Error: font file does not seem to be valid Type1');
Chris@909: $size2=$pos-$size1;
Chris@909: $file=substr($file,0,$size1+$size2);
Chris@909: end
Chris@909: if (respond_to('gzcompress'))
Chris@909: {
Chris@909: $cmp=$basename.'.z';
Chris@909: SaveToFile($cmp,gzcompress($file),'b');
Chris@909: $s<<'$file=\''.$cmp."';\n";
Chris@909: echo 'Font file compressed ('.$cmp.')
';
Chris@909: else
Chris@909: {
Chris@909: $s<<'$file=\''.basename($fontfile)."';\n";
Chris@909: echo 'Notice: font file could not be compressed (zlib extension not available)
';
Chris@909: end
Chris@909: if ($type=='Type1')
Chris@909: {
Chris@909: $s<<'$size1='.$size1.";\n";
Chris@909: $s<<'$size2='.$size2.";\n";
Chris@909: else
Chris@909: $s<<'$originalsize='.filesize($fontfile).";\n";
Chris@909: end
Chris@909: else
Chris@909: {
Chris@909: #Not embedded font
Chris@909: $s<<'$file='."'';\n";
Chris@909: end
Chris@909: $s<<"\n";
Chris@909: SaveToFile($basename.'.rb',$s);
Chris@909: echo 'Font definition file generated ('.$basename.'.rb'.')
';
Chris@909: }
Chris@909: