<?php
 
/*******************************************************************************
 
* Utility to generate font definition files                                    *
 
*                                                                              *
 
* Version: 1.2                                                                 *
 
* Date:    2011-06-18                                                          *
 
* Author:  Olivier PLATHEY                                                     *
 
*******************************************************************************/
 
 
require('ttfparser.php');
 
 
function Message($txt, $severity='')
 
{
 
    if(PHP_SAPI=='cli')
 
    {
 
        if($severity)
 
            echo "$severity: ";
 
        echo "$txt\n";
 
    }
 
    else
 
    {
 
        if($severity)
 
            echo "<b>$severity</b>: ";
 
        echo "$txt<br>";
 
    }
 
}
 
 
function Notice($txt)
 
{
 
    Message($txt, 'Notice');
 
}
 
 
function Warning($txt)
 
{
 
    Message($txt, 'Warning');
 
}
 
 
function Error($txt)
 
{
 
    Message($txt, 'Error');
 
    exit;
 
}
 
 
function LoadMap($enc)
 
{
 
    $file = dirname(__FILE__).'/'.strtolower($enc).'.map';
 
    $a = file($file);
 
    if(empty($a))
 
        Error('Encoding not found: '.$enc);
 
    $map = array_fill(0, 256, array('uv'=>-1, 'name'=>'.notdef'));
 
    foreach($a as $line)
 
    {
 
        $e = explode(' ', rtrim($line));
 
        $c = hexdec(substr($e[0],1));
 
        $uv = hexdec(substr($e[1],2));
 
        $name = $e[2];
 
        $map[$c] = array('uv'=>$uv, 'name'=>$name);
 
    }
 
    return $map;
 
}
 
 
function GetInfoFromTrueType($file, $embed, $map)
 
{
 
    // Return informations from a TrueType font
 
    $ttf = new TTFParser();
 
    $ttf->Parse($file);
 
    if($embed)
 
    {
 
        if(!$ttf->Embeddable)
 
            Error('Font license does not allow embedding');
 
        $info['Data'] = file_get_contents($file);
 
        $info['OriginalSize'] = filesize($file);
 
    }
 
    $k = 1000/$ttf->unitsPerEm;
 
    $info['FontName'] = $ttf->postScriptName;
 
    $info['Bold'] = $ttf->Bold;
 
    $info['ItalicAngle'] = $ttf->italicAngle;
 
    $info['IsFixedPitch'] = $ttf->isFixedPitch;
 
    $info['Ascender'] = round($k*$ttf->typoAscender);
 
    $info['Descender'] = round($k*$ttf->typoDescender);
 
    $info['UnderlineThickness'] = round($k*$ttf->underlineThickness);
 
    $info['UnderlinePosition'] = round($k*$ttf->underlinePosition);
 
    $info['FontBBox'] = array(round($k*$ttf->xMin), round($k*$ttf->yMin), round($k*$ttf->xMax), round($k*$ttf->yMax));
 
    $info['CapHeight'] = round($k*$ttf->capHeight);
 
    $info['MissingWidth'] = round($k*$ttf->widths[0]);
 
    $widths = array_fill(0, 256, $info['MissingWidth']);
 
    for($c=0;$c<=255;$c++)
 
    {
 
        if($map[$c]['name']!='.notdef')
 
        {
 
            $uv = $map[$c]['uv'];
 
            if(isset($ttf->chars[$uv]))
 
            {
 
                $w = $ttf->widths[$ttf->chars[$uv]];
 
                $widths[$c] = round($k*$w);
 
            }
 
            else
 
                Warning('Character '.$map[$c]['name'].' is missing');
 
        }
 
    }
 
    $info['Widths'] = $widths;
 
    return $info;
 
}
 
 
function GetInfoFromType1($file, $embed, $map)
 
{
 
    // Return informations from a Type1 font
 
    if($embed)
 
    {
 
        $f = fopen($file, 'rb');
 
        if(!$f)
 
            Error('Can\'t open font file');
 
        // Read first segment
 
        $a = unpack('Cmarker/Ctype/Vsize', fread($f,6));
 
        if($a['marker']!=128)
 
            Error('Font file is not a valid binary Type1');
 
        $size1 = $a['size'];
 
        $data = fread($f, $size1);
 
        // Read second segment
 
        $a = unpack('Cmarker/Ctype/Vsize', fread($f,6));
 
        if($a['marker']!=128)
 
            Error('Font file is not a valid binary Type1');
 
        $size2 = $a['size'];
 
        $data .= fread($f, $size2);
 
        fclose($f);
 
        $info['Data'] = $data;
 
        $info['Size1'] = $size1;
 
        $info['Size2'] = $size2;
 
    }
 
 
    $afm = substr($file, 0, -3).'afm';
 
    if(!file_exists($afm))
 
        Error('AFM font file not found: '.$afm);
 
    $a = file($afm);
 
    if(empty($a))
 
        Error('AFM file empty or not readable');
 
    foreach($a as $line)
 
    {
 
        $e = explode(' ', rtrim($line));
 
        if(count($e)<2)
 
            continue;
 
        $entry = $e[0];
 
        if($entry=='C')
 
        {
 
            $w = $e[4];
 
            $name = $e[7];
 
            $cw[$name] = $w;
 
        }
 
        elseif($entry=='FontName')
 
            $info['FontName'] = $e[1];
 
        elseif($entry=='Weight')
 
            $info['Weight'] = $e[1];
 
        elseif($entry=='ItalicAngle')
 
            $info['ItalicAngle'] = (int)$e[1];
 
        elseif($entry=='Ascender')
 
            $info['Ascender'] = (int)$e[1];
 
        elseif($entry=='Descender')
 
            $info['Descender'] = (int)$e[1];
 
        elseif($entry=='UnderlineThickness')
 
            $info['UnderlineThickness'] = (int)$e[1];
 
        elseif($entry=='UnderlinePosition')
 
            $info['UnderlinePosition'] = (int)$e[1];
 
        elseif($entry=='IsFixedPitch')
 
            $info['IsFixedPitch'] = ($e[1]=='true');
 
        elseif($entry=='FontBBox')
 
            $info['FontBBox'] = array((int)$e[1], (int)$e[2], (int)$e[3], (int)$e[4]);
 
        elseif($entry=='CapHeight')
 
            $info['CapHeight'] = (int)$e[1];
 
        elseif($entry=='StdVW')
 
            $info['StdVW'] = (int)$e[1];
 
    }
 
 
    if(!isset($info['FontName']))
 
        Error('FontName missing in AFM file');
 
    $info['Bold'] = isset($info['Weight']) && preg_match('/bold|black/i', $info['Weight']);
 
    if(isset($cw['.notdef']))
 
        $info['MissingWidth'] = $cw['.notdef'];
 
    else
 
        $info['MissingWidth'] = 0;
 
    $widths = array_fill(0, 256, $info['MissingWidth']);
 
    for($c=0;$c<=255;$c++)
 
    {
 
        $name = $map[$c]['name'];
 
        if($name!='.notdef')
 
        {
 
            if(isset($cw[$name]))
 
                $widths[$c] = $cw[$name];
 
            else
 
                Warning('Character '.$name.' is missing');
 
        }
 
    }
 
    $info['Widths'] = $widths;
 
    return $info;
 
}
 
 
function MakeFontDescriptor($info)
 
{
 
    // Ascent
 
    $fd = "array('Ascent'=>".$info['Ascender'];
 
    // Descent
 
    $fd .= ",'Descent'=>".$info['Descender'];
 
    // CapHeight
 
    if(!empty($info['CapHeight']))
 
        $fd .= ",'CapHeight'=>".$info['CapHeight'];
 
    else
 
        $fd .= ",'CapHeight'=>".$info['Ascender'];
 
    // Flags
 
    $flags = 0;
 
    if($info['IsFixedPitch'])
 
        $flags += 1<<0;
 
    $flags += 1<<5;
 
    if($info['ItalicAngle']!=0)
 
        $flags += 1<<6;
 
    $fd .= ",'Flags'=>".$flags;
 
    // FontBBox
 
    $fbb = $info['FontBBox'];
 
    $fd .= ",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'";
 
    // ItalicAngle
 
    $fd .= ",'ItalicAngle'=>".$info['ItalicAngle'];
 
    // StemV
 
    if(isset($info['StdVW']))
 
        $stemv = $info['StdVW'];
 
    elseif($info['Bold'])
 
        $stemv = 120;
 
    else
 
        $stemv = 70;
 
    $fd .= ",'StemV'=>".$stemv;
 
    // MissingWidth
 
    $fd .= ",'MissingWidth'=>".$info['MissingWidth'].')';
 
    return $fd;
 
}
 
 
function MakeWidthArray($widths)
 
{
 
    $s = "array(\n\t";
 
    for($c=0;$c<=255;$c++)
 
    {
 
        if(chr($c)=="'")
 
            $s .= "'\\''";
 
        elseif(chr($c)=="\\")
 
            $s .= "'\\\\'";
 
        elseif($c>=32 && $c<=126)
 
            $s .= "'".chr($c)."'";
 
        else
 
            $s .= "chr($c)";
 
        $s .= '=>'.$widths[$c];
 
        if($c<255)
 
            $s .= ',';
 
        if(($c+1)%22==0)
 
            $s .= "\n\t";
 
    }
 
    $s .= ')';
 
    return $s;
 
}
 
 
function MakeFontEncoding($map)
 
{
 
    // Build differences from reference encoding
 
    $ref = LoadMap('cp1252');
 
    $s = '';
 
    $last = 0;
 
    for($c=32;$c<=255;$c++)
 
    {
 
        if($map[$c]['name']!=$ref[$c]['name'])
 
        {
 
            if($c!=$last+1)
 
                $s .= $c.' ';
 
            $last = $c;
 
            $s .= '/'.$map[$c]['name'].' ';
 
        }
 
    }
 
    return rtrim($s);
 
}
 
 
function SaveToFile($file, $s, $mode)
 
{
 
    $f = fopen($file, 'w'.$mode);
 
    if(!$f)
 
        Error('Can\'t write to file '.$file);
 
    fwrite($f, $s, strlen($s));
 
    fclose($f);
 
}
 
 
function MakeDefinitionFile($file, $type, $enc, $embed, $map, $info)
 
{
 
    $s = "<?php\n";
 
    $s .= '$type = \''.$type."';\n";
 
    $s .= '$name = \''.$info['FontName']."';\n";
 
    $s .= '$desc = '.MakeFontDescriptor($info).";\n";
 
    $s .= '$up = '.$info['UnderlinePosition'].";\n";
 
    $s .= '$ut = '.$info['UnderlineThickness'].";\n";
 
    $s .= '$cw = '.MakeWidthArray($info['Widths']).";\n";
 
    $s .= '$enc = \''.$enc."';\n";
 
    $diff = MakeFontEncoding($map);
 
    if($diff)
 
        $s .= '$diff = \''.$diff."';\n";
 
    if($embed)
 
    {
 
        $s .= '$file = \''.$info['File']."';\n";
 
        if($type=='Type1')
 
        {
 
            $s .= '$size1 = '.$info['Size1'].";\n";
 
            $s .= '$size2 = '.$info['Size2'].";\n";
 
        }
 
        else
 
            $s .= '$originalsize = '.$info['OriginalSize'].";\n";
 
    }
 
    $s .= "?>\n";
 
    SaveToFile($file, $s, 't');
 
}
 
 
function MakeFont($fontfile, $enc='cp1252', $embed=true)
 
{
 
    // Generate a font definition file
 
    if(get_magic_quotes_runtime())
 
        @set_magic_quotes_runtime(0);
 
    ini_set('auto_detect_line_endings', '1');
 
 
    if(!file_exists($fontfile))
 
        Error('Font file not found: '.$fontfile);
 
    $ext = strtolower(substr($fontfile,-3));
 
    if($ext=='ttf' || $ext=='otf')
 
        $type = 'TrueType';
 
    elseif($ext=='pfb')
 
        $type = 'Type1';
 
    else
 
        Error('Unrecognized font file extension: '.$ext);
 
 
    $map = LoadMap($enc);
 
 
    if($type=='TrueType')
 
        $info = GetInfoFromTrueType($fontfile, $embed, $map);
 
    else
 
        $info = GetInfoFromType1($fontfile, $embed, $map);
 
 
    $basename = substr(basename($fontfile), 0, -4);
 
    if($embed)
 
    {
 
        if(function_exists('gzcompress'))
 
        {
 
            $file = $basename.'.z';
 
            SaveToFile($file, gzcompress($info['Data']), 'b');
 
            $info['File'] = $file;
 
            Message('Font file compressed: '.$file);
 
        }
 
        else
 
        {
 
            $info['File'] = basename($fontfile);
 
            Notice('Font file could not be compressed (zlib extension not available)');
 
        }
 
    }
 
 
    MakeDefinitionFile($basename.'.php', $type, $enc, $embed, $map, $info);
 
    Message('Font definition file generated: '.$basename.'.php');
 
}
 
 
if(PHP_SAPI=='cli')
 
{
 
    // Command-line interface
 
    if($argc==1)
 
        die("Usage: php makefont.php fontfile [enc] [embed]\n");
 
    $fontfile = $argv[1];
 
    if($argc>=3)
 
        $enc = $argv[2];
 
    else
 
        $enc = 'cp1252';
 
    if($argc>=4)
 
        $embed = ($argv[3]=='true' || $argv[3]=='1');
 
    else
 
        $embed = true;
 
    MakeFont($fontfile, $enc, $embed);
 
}
 
?>
 
 
 |