1
This commit is contained in:
289
vendor/fpdf/makefont/ttfparser.php
vendored
Normal file
289
vendor/fpdf/makefont/ttfparser.php
vendored
Normal file
@@ -0,0 +1,289 @@
|
||||
<?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->f = 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==3 && $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']+$offset31, SEEK_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+$ro, SEEK_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+$offset, SEEK_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, $n, SEEK_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'];
|
||||
}
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user