<?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 preg_match('/(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 chane vide si pas d'incorporation)    *
* $afmfile :  chemin du fichier AFM                                            *
* $enc :      encodage (ou chane 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
    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>';
}

