#***************************************************************************** # 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('Error: 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 'Warning: character '.$map[$i].' is missing
'; $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('Error: 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 'Warning: 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=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('Error: 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('Error: unrecognized font file extension: '.$ext); end else { if ($type!='TrueType' and $type!='Type1') die('Error: 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('Error: font file not found: '.$fontfile); if ($type=='TrueType') CheckTTF($fontfile); $f=fopen($fontfile,'rb'); if (!$f) die('Error: 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('Error: 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('Error: 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.')
'; else { $s<<'$file=\''.basename($fontfile)."';\n"; echo 'Notice: font file could not be compressed (zlib extension not available)
'; 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'.')
'; }