summaryrefslogtreecommitdiff
path: root/pdf/fpdf/makefont/ttfparser.php
diff options
context:
space:
mode:
Diffstat (limited to 'pdf/fpdf/makefont/ttfparser.php')
-rwxr-xr-xpdf/fpdf/makefont/ttfparser.php289
1 files changed, 289 insertions, 0 deletions
diff --git a/pdf/fpdf/makefont/ttfparser.php b/pdf/fpdf/makefont/ttfparser.php
new file mode 100755
index 0000000..602a543
--- /dev/null
+++ b/pdf/fpdf/makefont/ttfparser.php
@@ -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'];
+ }
+}
+?>