makefont.php

Go to the documentation of this file.
00001 <?php
00002 /*******************************************************************************
00003 * Utility to generate font definition files                                    *
00004 *                                                                              *
00005 * Version: 1.14                                                                *
00006 * Date:    2008-08-03                                                          *
00007 * Author:  Olivier PLATHEY                                                     *
00008 *******************************************************************************/
00009 
00010 function ReadMap($enc)
00011 {
00012     //Read a map file
00013     $file=dirname(__FILE__).'/'.strtolower($enc).'.map';
00014     $a=file($file);
00015     if(empty($a))
00016         die('<b>Error:</b> encoding not found: '.$enc);
00017     $cc2gn=array();
00018     foreach($a as $l)
00019     {
00020         if($l[0]=='!')
00021         {
00022             $e=preg_split('/[ \\t]+/',rtrim($l));
00023             $cc=hexdec(substr($e[0],1));
00024             $gn=$e[2];
00025             $cc2gn[$cc]=$gn;
00026         }
00027     }
00028     for($i=0;$i<=255;$i++)
00029     {
00030         if(!isset($cc2gn[$i]))
00031             $cc2gn[$i]='.notdef';
00032     }
00033     return $cc2gn;
00034 }
00035 
00036 function ReadAFM($file, &$map)
00037 {
00038     //Read a font metric file
00039     $a=file($file);
00040     if(empty($a))
00041         die('File not found');
00042     $widths=array();
00043     $fm=array();
00044     $fix=array('Edot'=>'Edotaccent','edot'=>'edotaccent','Idot'=>'Idotaccent','Zdot'=>'Zdotaccent','zdot'=>'zdotaccent',
00045         'Odblacute'=>'Ohungarumlaut','odblacute'=>'ohungarumlaut','Udblacute'=>'Uhungarumlaut','udblacute'=>'uhungarumlaut',
00046         'Gcedilla'=>'Gcommaaccent','gcedilla'=>'gcommaaccent','Kcedilla'=>'Kcommaaccent','kcedilla'=>'kcommaaccent',
00047         'Lcedilla'=>'Lcommaaccent','lcedilla'=>'lcommaaccent','Ncedilla'=>'Ncommaaccent','ncedilla'=>'ncommaaccent',
00048         'Rcedilla'=>'Rcommaaccent','rcedilla'=>'rcommaaccent','Scedilla'=>'Scommaaccent','scedilla'=>'scommaaccent',
00049         'Tcedilla'=>'Tcommaaccent','tcedilla'=>'tcommaaccent','Dslash'=>'Dcroat','dslash'=>'dcroat','Dmacron'=>'Dcroat','dmacron'=>'dcroat',
00050         'combininggraveaccent'=>'gravecomb','combininghookabove'=>'hookabovecomb','combiningtildeaccent'=>'tildecomb',
00051         'combiningacuteaccent'=>'acutecomb','combiningdotbelow'=>'dotbelowcomb','dongsign'=>'dong');
00052     foreach($a as $l)
00053     {
00054         $e=explode(' ',rtrim($l));
00055         if(count($e)<2)
00056             continue;
00057         $code=$e[0];
00058         $param=$e[1];
00059         if($code=='C')
00060         {
00061             //Character metrics
00062             $cc=(int)$e[1];
00063             $w=$e[4];
00064             $gn=$e[7];
00065             if(substr($gn,-4)=='20AC')
00066                 $gn='Euro';
00067             if(isset($fix[$gn]))
00068             {
00069                 //Fix incorrect glyph name
00070                 foreach($map as $c=>$n)
00071                 {
00072                     if($n==$fix[$gn])
00073                         $map[$c]=$gn;
00074                 }
00075             }
00076             if(empty($map))
00077             {
00078                 //Symbolic font: use built-in encoding
00079                 $widths[$cc]=$w;
00080             }
00081             else
00082             {
00083                 $widths[$gn]=$w;
00084                 if($gn=='X')
00085                     $fm['CapXHeight']=$e[13];
00086             }
00087             if($gn=='.notdef')
00088                 $fm['MissingWidth']=$w;
00089         }
00090         elseif($code=='FontName')
00091             $fm['FontName']=$param;
00092         elseif($code=='Weight')
00093             $fm['Weight']=$param;
00094         elseif($code=='ItalicAngle')
00095             $fm['ItalicAngle']=(double)$param;
00096         elseif($code=='Ascender')
00097             $fm['Ascender']=(int)$param;
00098         elseif($code=='Descender')
00099             $fm['Descender']=(int)$param;
00100         elseif($code=='UnderlineThickness')
00101             $fm['UnderlineThickness']=(int)$param;
00102         elseif($code=='UnderlinePosition')
00103             $fm['UnderlinePosition']=(int)$param;
00104         elseif($code=='IsFixedPitch')
00105             $fm['IsFixedPitch']=($param=='true');
00106         elseif($code=='FontBBox')
00107             $fm['FontBBox']=array($e[1],$e[2],$e[3],$e[4]);
00108         elseif($code=='CapHeight')
00109             $fm['CapHeight']=(int)$param;
00110         elseif($code=='StdVW')
00111             $fm['StdVW']=(int)$param;
00112     }
00113     if(!isset($fm['FontName']))
00114         die('FontName not found');
00115     if(!empty($map))
00116     {
00117         if(!isset($widths['.notdef']))
00118             $widths['.notdef']=600;
00119         if(!isset($widths['Delta']) && isset($widths['increment']))
00120             $widths['Delta']=$widths['increment'];
00121         //Order widths according to map
00122         for($i=0;$i<=255;$i++)
00123         {
00124             if(!isset($widths[$map[$i]]))
00125             {
00126                 echo '<b>Warning:</b> character '.$map[$i].' is missing<br>';
00127                 $widths[$i]=$widths['.notdef'];
00128             }
00129             else
00130                 $widths[$i]=$widths[$map[$i]];
00131         }
00132     }
00133     $fm['Widths']=$widths;
00134     return $fm;
00135 }
00136 
00137 function MakeFontDescriptor($fm, $symbolic)
00138 {
00139     //Ascent
00140     $asc=(isset($fm['Ascender']) ? $fm['Ascender'] : 1000);
00141     $fd="array('Ascent'=>".$asc;
00142     //Descent
00143     $desc=(isset($fm['Descender']) ? $fm['Descender'] : -200);
00144     $fd.=",'Descent'=>".$desc;
00145     //CapHeight
00146     if(isset($fm['CapHeight']))
00147         $ch=$fm['CapHeight'];
00148     elseif(isset($fm['CapXHeight']))
00149         $ch=$fm['CapXHeight'];
00150     else
00151         $ch=$asc;
00152     $fd.=",'CapHeight'=>".$ch;
00153     //Flags
00154     $flags=0;
00155     if(isset($fm['IsFixedPitch']) && $fm['IsFixedPitch'])
00156         $flags+=1<<0;
00157     if($symbolic)
00158         $flags+=1<<2;
00159     if(!$symbolic)
00160         $flags+=1<<5;
00161     if(isset($fm['ItalicAngle']) && $fm['ItalicAngle']!=0)
00162         $flags+=1<<6;
00163     $fd.=",'Flags'=>".$flags;
00164     //FontBBox
00165     if(isset($fm['FontBBox']))
00166         $fbb=$fm['FontBBox'];
00167     else
00168         $fbb=array(0,$desc-100,1000,$asc+100);
00169     $fd.=",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'";
00170     //ItalicAngle
00171     $ia=(isset($fm['ItalicAngle']) ? $fm['ItalicAngle'] : 0);
00172     $fd.=",'ItalicAngle'=>".$ia;
00173     //StemV
00174     if(isset($fm['StdVW']))
00175         $stemv=$fm['StdVW'];
00176     elseif(isset($fm['Weight']) && preg_match('/bold|black/i',$fm['Weight']))
00177         $stemv=120;
00178     else
00179         $stemv=70;
00180     $fd.=",'StemV'=>".$stemv;
00181     //MissingWidth
00182     if(isset($fm['MissingWidth']))
00183         $fd.=",'MissingWidth'=>".$fm['MissingWidth'];
00184     $fd.=')';
00185     return $fd;
00186 }
00187 
00188 function MakeWidthArray($fm)
00189 {
00190     //Make character width array
00191     $s="array(\n\t";
00192     $cw=$fm['Widths'];
00193     for($i=0;$i<=255;$i++)
00194     {
00195         if(chr($i)=="'")
00196             $s.="'\\''";
00197         elseif(chr($i)=="\\")
00198             $s.="'\\\\'";
00199         elseif($i>=32 && $i<=126)
00200             $s.="'".chr($i)."'";
00201         else
00202             $s.="chr($i)";
00203         $s.='=>'.$fm['Widths'][$i];
00204         if($i<255)
00205             $s.=',';
00206         if(($i+1)%22==0)
00207             $s.="\n\t";
00208     }
00209     $s.=')';
00210     return $s;
00211 }
00212 
00213 function MakeFontEncoding($map)
00214 {
00215     //Build differences from reference encoding
00216     $ref=ReadMap('cp1252');
00217     $s='';
00218     $last=0;
00219     for($i=32;$i<=255;$i++)
00220     {
00221         if($map[$i]!=$ref[$i])
00222         {
00223             if($i!=$last+1)
00224                 $s.=$i.' ';
00225             $last=$i;
00226             $s.='/'.$map[$i].' ';
00227         }
00228     }
00229     return rtrim($s);
00230 }
00231 
00232 function SaveToFile($file, $s, $mode)
00233 {
00234     $f=fopen($file,'w'.$mode);
00235     if(!$f)
00236         die('Can\'t write to file '.$file);
00237     fwrite($f,$s,strlen($s));
00238     fclose($f);
00239 }
00240 
00241 function ReadShort($f)
00242 {
00243     $a=unpack('n1n',fread($f,2));
00244     return $a['n'];
00245 }
00246 
00247 function ReadLong($f)
00248 {
00249     $a=unpack('N1N',fread($f,4));
00250     return $a['N'];
00251 }
00252 
00253 function CheckTTF($file)
00254 {
00255     //Check if font license allows embedding
00256     $f=fopen($file,'rb');
00257     if(!$f)
00258         die('<b>Error:</b> Can\'t open '.$file);
00259     //Extract number of tables
00260     fseek($f,4,SEEK_CUR);
00261     $nb=ReadShort($f);
00262     fseek($f,6,SEEK_CUR);
00263     //Seek OS/2 table
00264     $found=false;
00265     for($i=0;$i<$nb;$i++)
00266     {
00267         if(fread($f,4)=='OS/2')
00268         {
00269             $found=true;
00270             break;
00271         }
00272         fseek($f,12,SEEK_CUR);
00273     }
00274     if(!$found)
00275     {
00276         fclose($f);
00277         return;
00278     }
00279     fseek($f,4,SEEK_CUR);
00280     $offset=ReadLong($f);
00281     fseek($f,$offset,SEEK_SET);
00282     //Extract fsType flags
00283     fseek($f,8,SEEK_CUR);
00284     $fsType=ReadShort($f);
00285     $rl=($fsType & 0x02)!=0;
00286     $pp=($fsType & 0x04)!=0;
00287     $e=($fsType & 0x08)!=0;
00288     fclose($f);
00289     if($rl && !$pp && !$e)
00290         echo '<b>Warning:</b> font license does not allow embedding';
00291 }
00292 
00293 /*******************************************************************************
00294 * fontfile: path to TTF file (or empty string if not to be embedded)           *
00295 * afmfile:  path to AFM file                                                   *
00296 * enc:      font encoding (or empty string for symbolic fonts)                 *
00297 * patch:    optional patch for encoding                                        *
00298 * type:     font type if fontfile is empty                                     *
00299 *******************************************************************************/
00300 function MakeFont($fontfile, $afmfile, $enc='cp1252', $patch=array(), $type='TrueType')
00301 {
00302     //Generate a font definition file
00303     if(get_magic_quotes_runtime())
00304         @set_magic_quotes_runtime(0);
00305     ini_set('auto_detect_line_endings','1');
00306     if($enc)
00307     {
00308         $map=ReadMap($enc);
00309         foreach($patch as $cc=>$gn)
00310             $map[$cc]=$gn;
00311     }
00312     else
00313         $map=array();
00314     if(!file_exists($afmfile))
00315         die('<b>Error:</b> AFM file not found: '.$afmfile);
00316     $fm=ReadAFM($afmfile,$map);
00317     if($enc)
00318         $diff=MakeFontEncoding($map);
00319     else
00320         $diff='';
00321     $fd=MakeFontDescriptor($fm,empty($map));
00322     //Find font type
00323     if($fontfile)
00324     {
00325         $ext=strtolower(substr($fontfile,-3));
00326         if($ext=='ttf')
00327             $type='TrueType';
00328         elseif($ext=='pfb')
00329             $type='Type1';
00330         else
00331             die('<b>Error:</b> unrecognized font file extension: '.$ext);
00332     }
00333     else
00334     {
00335         if($type!='TrueType' && $type!='Type1')
00336             die('<b>Error:</b> incorrect font type: '.$type);
00337     }
00338     //Start generation
00339     $s='<?php'."\n";
00340     $s.='$type=\''.$type."';\n";
00341     $s.='$name=\''.$fm['FontName']."';\n";
00342     $s.='$desc='.$fd.";\n";
00343     if(!isset($fm['UnderlinePosition']))
00344         $fm['UnderlinePosition']=-100;
00345     if(!isset($fm['UnderlineThickness']))
00346         $fm['UnderlineThickness']=50;
00347     $s.='$up='.$fm['UnderlinePosition'].";\n";
00348     $s.='$ut='.$fm['UnderlineThickness'].";\n";
00349     $w=MakeWidthArray($fm);
00350     $s.='$cw='.$w.";\n";
00351     $s.='$enc=\''.$enc."';\n";
00352     $s.='$diff=\''.$diff."';\n";
00353     $basename=substr(basename($afmfile),0,-4);
00354     if($fontfile)
00355     {
00356         //Embedded font
00357         if(!file_exists($fontfile))
00358             die('<b>Error:</b> font file not found: '.$fontfile);
00359         if($type=='TrueType')
00360             CheckTTF($fontfile);
00361         $f=fopen($fontfile,'rb');
00362         if(!$f)
00363             die('<b>Error:</b> Can\'t open '.$fontfile);
00364         $file=fread($f,filesize($fontfile));
00365         fclose($f);
00366         if($type=='Type1')
00367         {
00368             //Find first two sections and discard third one
00369             $header=(ord($file[0])==128);
00370             if($header)
00371             {
00372                 //Strip first binary header
00373                 $file=substr($file,6);
00374             }
00375             $pos=strpos($file,'eexec');
00376             if(!$pos)
00377                 die('<b>Error:</b> font file does not seem to be valid Type1');
00378             $size1=$pos+6;
00379             if($header && ord($file[$size1])==128)
00380             {
00381                 //Strip second binary header
00382                 $file=substr($file,0,$size1).substr($file,$size1+6);
00383             }
00384             $pos=strpos($file,'00000000');
00385             if(!$pos)
00386                 die('<b>Error:</b> font file does not seem to be valid Type1');
00387             $size2=$pos-$size1;
00388             $file=substr($file,0,$size1+$size2);
00389         }
00390         if(function_exists('gzcompress'))
00391         {
00392             $cmp=$basename.'.z';
00393             SaveToFile($cmp,gzcompress($file),'b');
00394             $s.='$file=\''.$cmp."';\n";
00395             echo 'Font file compressed ('.$cmp.')<br>';
00396         }
00397         else
00398         {
00399             $s.='$file=\''.basename($fontfile)."';\n";
00400             echo '<b>Notice:</b> font file could not be compressed (zlib extension not available)<br>';
00401         }
00402         if($type=='Type1')
00403         {
00404             $s.='$size1='.$size1.";\n";
00405             $s.='$size2='.$size2.";\n";
00406         }
00407         else
00408             $s.='$originalsize='.filesize($fontfile).";\n";
00409     }
00410     else
00411     {
00412         //Not embedded font
00413         $s.='$file='."'';\n";
00414     }
00415     $s.="?>\n";
00416     SaveToFile($basename.'.php',$s,'t');
00417     echo 'Font definition file generated ('.$basename.'.php'.')<br>';
00418 }
00419 ?>
 All Data Structures Files Functions Variables Enumerations