Login   Register  
PHP Classes
elePHPant
Icontem

File: extSrc/makefont/ttfparser.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Vincenzo Di Biaggio  >  PHP Digital Download Script  >  extSrc/makefont/ttfparser.php  >  Download  
File: extSrc/makefont/ttfparser.php
Role: Auxiliary script
Content type: text/plain
Description: Auxiliary script
Class: PHP Digital Download Script
Serve files for download with codes given to users
Author: By
Last change:
Date: 1 year ago
Size: 7,288 bytes
 

Contents

Class file image Download
<?php
/*******************************************************************************
* Utility to parse TTF font files                                              *
*                                                                              *
* Version: 1.0                                                                 *
* Date:    2011-06-18                                                          *
* Author:  Olivier PLATHEY                                                     *
*******************************************************************************/

class TTFParser
{
    var 
$f;
    var 
$tables;
    var 
$unitsPerEm;
    var 
$xMin$yMin$xMax$yMax;
    var 
$numberOfHMetrics;
    var 
$numGlyphs;
    var 
$widths;
    var 
$chars;
    var 
$postScriptName;
    var 
$Embeddable;
    var 
$Bold;
    var 
$typoAscender;
    var 
$typoDescender;
    var 
$capHeight;
    var 
$italicAngle;
    var 
$underlinePosition;
    var 
$underlineThickness;
    var 
$isFixedPitch;

    function 
Parse($file)
    {
        
$this->fopen($file'rb');
        if(!
$this->f)
            
$this->Error('Can\'t open file: '.$file);

        
$version $this->Read(4);
        if(
$version=='OTTO')
            
$this->Error('OpenType fonts based on PostScript outlines are not supported');
        if(
$version!="\x00\x01\x00\x00")
            
$this->Error('Unrecognized file format');
        
$numTables $this->ReadUShort();
        
$this->Skip(3*2); // searchRange, entrySelector, rangeShift
        
$this->tables = array();
        for(
$i=0;$i<$numTables;$i++)
        {
            
$tag $this->Read(4);
            
$this->Skip(4); // checkSum
            
$offset $this->ReadULong();
            
$this->Skip(4); // length
            
$this->tables[$tag] = $offset;
        }

        
$this->ParseHead();
        
$this->ParseHhea();
        
$this->ParseMaxp();
        
$this->ParseHmtx();
        
$this->ParseCmap();
        
$this->ParseName();
        
$this->ParseOS2();
        
$this->ParsePost();

        
fclose($this->f);
    }

    function 
ParseHead()
    {
        
$this->Seek('head');
        
$this->Skip(3*4); // version, fontRevision, checkSumAdjustment
        
$magicNumber $this->ReadULong();
        if(
$magicNumber!=0x5F0F3CF5)
            
$this->Error('Incorrect magic number');
        
$this->Skip(2); // flags
        
$this->unitsPerEm $this->ReadUShort();
        
$this->Skip(2*8); // created, modified
        
$this->xMin $this->ReadShort();
        
$this->yMin $this->ReadShort();
        
$this->xMax $this->ReadShort();
        
$this->yMax $this->ReadShort();
    }

    function 
ParseHhea()
    {
        
$this->Seek('hhea');
        
$this->Skip(4+15*2);
        
$this->numberOfHMetrics $this->ReadUShort();
    }

    function 
ParseMaxp()
    {
        
$this->Seek('maxp');
        
$this->Skip(4);
        
$this->numGlyphs $this->ReadUShort();
    }

    function 
ParseHmtx()
    {
        
$this->Seek('hmtx');
        
$this->widths = array();
        for(
$i=0;$i<$this->numberOfHMetrics;$i++)
        {
            
$advanceWidth $this->ReadUShort();
            
$this->Skip(2); // lsb
            
$this->widths[$i] = $advanceWidth;
        }
        if(
$this->numberOfHMetrics<$this->numGlyphs)
        {
            
$lastWidth $this->widths[$this->numberOfHMetrics-1];
            
$this->widths array_pad($this->widths$this->numGlyphs$lastWidth);
        }
    }

    function 
ParseCmap()
    {
        
$this->Seek('cmap');
        
$this->Skip(2); // version
        
$numTables $this->ReadUShort();
        
$offset31 0;
        for(
$i=0;$i<$numTables;$i++)
        {
            
$platformID $this->ReadUShort();
            
$encodingID $this->ReadUShort();
            
$offset $this->ReadULong();
            if(
$platformID==&& $encodingID==1)
                
$offset31 $offset;
        }
        if(
$offset31==0)
            
$this->Error('No Unicode encoding found');

        
$startCount = array();
        
$endCount = array();
        
$idDelta = array();
        
$idRangeOffset = array();
        
$this->chars = array();
        
fseek($this->f$this->tables['cmap']+$offset31SEEK_SET);
        
$format $this->ReadUShort();
        if(
$format!=4)
            
$this->Error('Unexpected subtable format: '.$format);
        
$this->Skip(2*2); // length, language
        
$segCount $this->ReadUShort()/2;
        
$this->Skip(3*2); // searchRange, entrySelector, rangeShift
        
for($i=0;$i<$segCount;$i++)
            
$endCount[$i] = $this->ReadUShort();
        
$this->Skip(2); // reservedPad
        
for($i=0;$i<$segCount;$i++)
            
$startCount[$i] = $this->ReadUShort();
        for(
$i=0;$i<$segCount;$i++)
            
$idDelta[$i] = $this->ReadShort();
        
$offset ftell($this->f);
        for(
$i=0;$i<$segCount;$i++)
            
$idRangeOffset[$i] = $this->ReadUShort();

        for(
$i=0;$i<$segCount;$i++)
        {
            
$c1 $startCount[$i];
            
$c2 $endCount[$i];
            
$d $idDelta[$i];
            
$ro $idRangeOffset[$i];
            if(
$ro>0)
                
fseek($this->f$offset+2*$i+$roSEEK_SET);
            for(
$c=$c1;$c<=$c2;$c++)
            {
                if(
$c==0xFFFF)
                    break;
                if(
$ro>0)
                {
                    
$gid $this->ReadUShort();
                    if(
$gid>0)
                        
$gid += $d;
                }
                else
                    
$gid $c+$d;
                if(
$gid>=65536)
                    
$gid -= 65536;
                if(
$gid>0)
                    
$this->chars[$c] = $gid;
            }
        }
    }

    function 
ParseName()
    {
        
$this->Seek('name');
        
$tableOffset ftell($this->f);
        
$this->postScriptName '';
        
$this->Skip(2); // format
        
$count $this->ReadUShort();
        
$stringOffset $this->ReadUShort();
        for(
$i=0;$i<$count;$i++)
        {
            
$this->Skip(3*2); // platformID, encodingID, languageID
            
$nameID $this->ReadUShort();
            
$length $this->ReadUShort();
            
$offset $this->ReadUShort();
            if(
$nameID==6)
            {
                
// PostScript name
                
fseek($this->f$tableOffset+$stringOffset+$offsetSEEK_SET);
                
$s $this->Read($length);
                
$s str_replace(chr(0), ''$s);
                
$s preg_replace('|[ \[\](){}<>/%]|'''$s);
                
$this->postScriptName $s;
                break;
            }
        }
        if(
$this->postScriptName=='')
            
$this->Error('PostScript name not found');
    }

    function 
ParseOS2()
    {
        
$this->Seek('OS/2');
        
$version $this->ReadUShort();
        
$this->Skip(3*2); // xAvgCharWidth, usWeightClass, usWidthClass
        
$fsType $this->ReadUShort();
        
$this->Embeddable = ($fsType!=2) && ($fsType 0x200)==0;
        
$this->Skip(11*2+10+4*4+4);
        
$fsSelection $this->ReadUShort();
        
$this->Bold = ($fsSelection 32)!=0;
        
$this->Skip(2*2); // usFirstCharIndex, usLastCharIndex
        
$this->typoAscender $this->ReadShort();
        
$this->typoDescender $this->ReadShort();
        if(
$version>=2)
        {
            
$this->Skip(3*2+2*4+2);
            
$this->capHeight $this->ReadShort();
        }
        else
            
$this->capHeight 0;
    }

    function 
ParsePost()
    {
        
$this->Seek('post');
        
$this->Skip(4); // version
        
$this->italicAngle $this->ReadShort();
        
$this->Skip(2); // Skip decimal part
        
$this->underlinePosition $this->ReadShort();
        
$this->underlineThickness $this->ReadShort();
        
$this->isFixedPitch = ($this->ReadULong()!=0);
    }

    function 
Error($msg)
    {
        if(
PHP_SAPI=='cli')
            die(
"Error: $msg\n");
        else
            die(
"<b>Error</b>: $msg");
    }

    function 
Seek($tag)
    {
        if(!isset(
$this->tables[$tag]))
            
$this->Error('Table not found: '.$tag);
        
fseek($this->f$this->tables[$tag], SEEK_SET);
    }

    function 
Skip($n)
    {
        
fseek($this->f$nSEEK_CUR);
    }

    function 
Read($n)
    {
        return 
fread($this->f$n);
    }

    function 
ReadUShort()
    {
        
$a unpack('nn'fread($this->f,2));
        return 
$a['n'];
    }

    function 
ReadShort()
    {
        
$a unpack('nn'fread($this->f,2));
        
$v $a['n'];
        if(
$v>=0x8000)
            
$v -= 65536;
        return 
$v;
    }

    function 
ReadULong()
    {
        
$a unpack('NN'fread($this->f,4));
        return 
$a['N'];
    }
}
?>