Subversion Repositories ALCASAR

Rev

Blame | Last modification | View Log

<?php
/*******************************************************************************
* Utilitaire de génération de fichier de définition de police                  *
*                                                                              *
* Version : 1.14                                                               *
* Date :    03/08/2008                                                         *
* Auteur :  Olivier PLATHEY                                                    *
*******************************************************************************/

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']) && 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']) && $fm['IsFixedPitch'])
                $flags+=1<<0;
        if($symbolic)
                $flags+=1<<2;
        if(!$symbolic)
                $flags+=1<<5;
        if(isset($fm['ItalicAngle']) && $fm['ItalicAngle']!=0)
                $flags+=1<<6;
        $fd.=",'Flags'=>".$flags;
        //FontBBox
        if(isset($fm['FontBBox']))
                $fbb=$fm['FontBBox'];
        else
                $fbb=array(0,$desc-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']) && preg_match('/bold|black/i',$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 && $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)
{
        $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 && !$pp && !$e)
                echo '<b>Warning:</b> 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=array(), $type='TrueType')
{
        //Generate a font definition file
        if(get_magic_quotes_runtime())
                @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;
        }
        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' && $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 && 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,'t');
        echo 'Font definition file generated ('.$basename.'.php'.')<br>';
}
?>