diff options
author | Sashi20 | 2020-07-24 10:48:32 +0530 |
---|---|---|
committer | Sashi20 | 2020-07-24 10:48:32 +0530 |
commit | 327f2b89a03717a0e1e6e102c51e547556a15ec9 (patch) | |
tree | f9206fa3594e5f24930260041dd675d2896dc57b /pdf/fpdf/makefont | |
parent | 3a8e8b0f13e38dcd22e6452a12b16d76ac45c877 (diff) | |
download | cfd_case_study-327f2b89a03717a0e1e6e102c51e547556a15ec9.tar.gz cfd_case_study-327f2b89a03717a0e1e6e102c51e547556a15ec9.tar.bz2 cfd_case_study-327f2b89a03717a0e1e6e102c51e547556a15ec9.zip |
Update fpdf version
Diffstat (limited to 'pdf/fpdf/makefont')
-rwxr-xr-x | pdf/fpdf/makefont/makefont.php | 144 | ||||
-rwxr-xr-x | pdf/fpdf/makefont/ttfparser.php | 544 |
2 files changed, 598 insertions, 90 deletions
diff --git a/pdf/fpdf/makefont/makefont.php b/pdf/fpdf/makefont/makefont.php index 78db0aa..fbe8dcf 100755 --- a/pdf/fpdf/makefont/makefont.php +++ b/pdf/fpdf/makefont/makefont.php @@ -2,8 +2,8 @@ /*******************************************************************************
* Utility to generate font definition files *
* *
-* Version: 1.2 *
-* Date: 2011-06-18 *
+* Version: 1.31 *
+* Date: 2019-12-07 *
* Author: Olivier PLATHEY *
*******************************************************************************/
@@ -59,21 +59,40 @@ function LoadMap($enc) return $map;
}
-function GetInfoFromTrueType($file, $embed, $map)
+function GetInfoFromTrueType($file, $embed, $subset, $map)
{
- // Return informations from a TrueType font
- $ttf = new TTFParser();
- $ttf->Parse($file);
+ // Return information from a TrueType font
+ try
+ {
+ $ttf = new TTFParser($file);
+ $ttf->Parse();
+ }
+ catch(Exception $e)
+ {
+ Error($e->getMessage());
+ }
if($embed)
{
- if(!$ttf->Embeddable)
+ if(!$ttf->embeddable)
Error('Font license does not allow embedding');
- $info['Data'] = file_get_contents($file);
- $info['OriginalSize'] = filesize($file);
+ if($subset)
+ {
+ $chars = array();
+ foreach($map as $v)
+ {
+ if($v['name']!='.notdef')
+ $chars[] = $v['uv'];
+ }
+ $ttf->Subset($chars);
+ $info['Data'] = $ttf->Build();
+ }
+ else
+ $info['Data'] = file_get_contents($file);
+ $info['OriginalSize'] = strlen($info['Data']);
}
$k = 1000/$ttf->unitsPerEm;
$info['FontName'] = $ttf->postScriptName;
- $info['Bold'] = $ttf->Bold;
+ $info['Bold'] = $ttf->bold;
$info['ItalicAngle'] = $ttf->italicAngle;
$info['IsFixedPitch'] = $ttf->isFixedPitch;
$info['Ascender'] = round($k*$ttf->typoAscender);
@@ -82,20 +101,20 @@ function GetInfoFromTrueType($file, $embed, $map) $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]);
+ $info['MissingWidth'] = round($k*$ttf->glyphs[0]['w']);
$widths = array_fill(0, 256, $info['MissingWidth']);
- for($c=0;$c<=255;$c++)
+ foreach($map as $c=>$v)
{
- if($map[$c]['name']!='.notdef')
+ if($v['name']!='.notdef')
{
- $uv = $map[$c]['uv'];
- if(isset($ttf->chars[$uv]))
+ if(isset($ttf->chars[$v['uv']]))
{
- $w = $ttf->widths[$ttf->chars[$uv]];
+ $id = $ttf->chars[$v['uv']];
+ $w = $ttf->glyphs[$id]['w'];
$widths[$c] = round($k*$w);
}
else
- Warning('Character '.$map[$c]['name'].' is missing');
+ Warning('Character '.$v['name'].' is missing');
}
}
$info['Widths'] = $widths;
@@ -104,7 +123,7 @@ function GetInfoFromTrueType($file, $embed, $map) function GetInfoFromType1($file, $embed, $map)
{
- // Return informations from a Type1 font
+ // Return information from a Type1 font
if($embed)
{
$f = fopen($file, 'rb');
@@ -172,21 +191,24 @@ function GetInfoFromType1($file, $embed, $map) if(!isset($info['FontName']))
Error('FontName missing in AFM file');
+ if(!isset($info['Ascender']))
+ $info['Ascender'] = $info['FontBBox'][3];
+ if(!isset($info['Descender']))
+ $info['Descender'] = $info['FontBBox'][1];
$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++)
+ foreach($map as $c=>$v)
{
- $name = $map[$c]['name'];
- if($name!='.notdef')
+ if($v['name']!='.notdef')
{
- if(isset($cw[$name]))
- $widths[$c] = $cw[$name];
+ if(isset($cw[$v['name']]))
+ $widths[$c] = $cw[$v['name']];
else
- Warning('Character '.$name.' is missing');
+ Warning('Character '.$v['name'].' is missing');
}
}
$info['Widths'] = $widths;
@@ -272,16 +294,61 @@ function MakeFontEncoding($map) return rtrim($s);
}
+function MakeUnicodeArray($map)
+{
+ // Build mapping to Unicode values
+ $ranges = array();
+ foreach($map as $c=>$v)
+ {
+ $uv = $v['uv'];
+ if($uv!=-1)
+ {
+ if(isset($range))
+ {
+ if($c==$range[1]+1 && $uv==$range[3]+1)
+ {
+ $range[1]++;
+ $range[3]++;
+ }
+ else
+ {
+ $ranges[] = $range;
+ $range = array($c, $c, $uv, $uv);
+ }
+ }
+ else
+ $range = array($c, $c, $uv, $uv);
+ }
+ }
+ $ranges[] = $range;
+
+ foreach($ranges as $range)
+ {
+ if(isset($s))
+ $s .= ',';
+ else
+ $s = 'array(';
+ $s .= $range[0].'=>';
+ $nb = $range[1]-$range[0]+1;
+ if($nb>1)
+ $s .= 'array('.$range[2].','.$nb.')';
+ else
+ $s .= $range[2];
+ }
+ $s .= ')';
+ return $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));
+ fwrite($f, $s);
fclose($f);
}
-function MakeDefinitionFile($file, $type, $enc, $embed, $map, $info)
+function MakeDefinitionFile($file, $type, $enc, $embed, $subset, $map, $info)
{
$s = "<?php\n";
$s .= '$type = \''.$type."';\n";
@@ -294,6 +361,7 @@ function MakeDefinitionFile($file, $type, $enc, $embed, $map, $info) $diff = MakeFontEncoding($map);
if($diff)
$s .= '$diff = \''.$diff."';\n";
+ $s .= '$uv = '.MakeUnicodeArray($map).";\n";
if($embed)
{
$s .= '$file = \''.$info['File']."';\n";
@@ -303,19 +371,19 @@ function MakeDefinitionFile($file, $type, $enc, $embed, $map, $info) $s .= '$size2 = '.$info['Size2'].";\n";
}
else
+ {
$s .= '$originalsize = '.$info['OriginalSize'].";\n";
+ if($subset)
+ $s .= "\$subsetted = true;\n";
+ }
}
$s .= "?>\n";
SaveToFile($file, $s, 't');
}
-function MakeFont($fontfile, $enc='cp1252', $embed=true)
+function MakeFont($fontfile, $enc='cp1252', $embed=true, $subset=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));
@@ -329,7 +397,7 @@ function MakeFont($fontfile, $enc='cp1252', $embed=true) $map = LoadMap($enc);
if($type=='TrueType')
- $info = GetInfoFromTrueType($fontfile, $embed, $map);
+ $info = GetInfoFromTrueType($fontfile, $embed, $subset, $map);
else
$info = GetInfoFromType1($fontfile, $embed, $map);
@@ -346,19 +414,21 @@ function MakeFont($fontfile, $enc='cp1252', $embed=true) else
{
$info['File'] = basename($fontfile);
+ $subset = false;
Notice('Font file could not be compressed (zlib extension not available)');
}
}
- MakeDefinitionFile($basename.'.php', $type, $enc, $embed, $map, $info);
+ MakeDefinitionFile($basename.'.php', $type, $enc, $embed, $subset, $map, $info);
Message('Font definition file generated: '.$basename.'.php');
}
if(PHP_SAPI=='cli')
{
// Command-line interface
+ ini_set('log_errors', '0');
if($argc==1)
- die("Usage: php makefont.php fontfile [enc] [embed]\n");
+ die("Usage: php makefont.php fontfile [encoding] [embed] [subset]\n");
$fontfile = $argv[1];
if($argc>=3)
$enc = $argv[2];
@@ -368,6 +438,10 @@ if(PHP_SAPI=='cli') $embed = ($argv[3]=='true' || $argv[3]=='1');
else
$embed = true;
- MakeFont($fontfile, $enc, $embed);
+ if($argc>=5)
+ $subset = ($argv[4]=='true' || $argv[4]=='1');
+ else
+ $subset = true;
+ MakeFont($fontfile, $enc, $embed, $subset);
}
?>
diff --git a/pdf/fpdf/makefont/ttfparser.php b/pdf/fpdf/makefont/ttfparser.php index 602a543..e6ba321 100755 --- a/pdf/fpdf/makefont/ttfparser.php +++ b/pdf/fpdf/makefont/ttfparser.php @@ -1,39 +1,67 @@ <?php
/*******************************************************************************
-* Utility to parse TTF font files *
+* Class to parse and subset TrueType fonts *
* *
-* Version: 1.0 *
-* Date: 2011-06-18 *
+* Version: 1.1 *
+* Date: 2015-11-29 *
* 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)
+ protected $f;
+ protected $tables;
+ protected $numberOfHMetrics;
+ protected $numGlyphs;
+ protected $glyphNames;
+ protected $indexToLocFormat;
+ protected $subsettedChars;
+ protected $subsettedGlyphs;
+ public $chars;
+ public $glyphs;
+ public $unitsPerEm;
+ public $xMin, $yMin, $xMax, $yMax;
+ public $postScriptName;
+ public $embeddable;
+ public $bold;
+ public $typoAscender;
+ public $typoDescender;
+ public $capHeight;
+ public $italicAngle;
+ public $underlinePosition;
+ public $underlineThickness;
+ public $isFixedPitch;
+
+ function __construct($file)
{
$this->f = fopen($file, 'rb');
if(!$this->f)
$this->Error('Can\'t open file: '.$file);
+ }
+
+ function __destruct()
+ {
+ if(is_resource($this->f))
+ fclose($this->f);
+ }
+
+ function Parse()
+ {
+ $this->ParseOffsetTable();
+ $this->ParseHead();
+ $this->ParseHhea();
+ $this->ParseMaxp();
+ $this->ParseHmtx();
+ $this->ParseLoca();
+ $this->ParseGlyf();
+ $this->ParseCmap();
+ $this->ParseName();
+ $this->ParseOS2();
+ $this->ParsePost();
+ }
+ function ParseOffsetTable()
+ {
$version = $this->Read(4);
if($version=='OTTO')
$this->Error('OpenType fonts based on PostScript outlines are not supported');
@@ -45,23 +73,12 @@ class TTFParser for($i=0;$i<$numTables;$i++)
{
$tag = $this->Read(4);
- $this->Skip(4); // checkSum
+ $checkSum = $this->Read(4);
$offset = $this->ReadULong();
- $this->Skip(4); // length
- $this->tables[$tag] = $offset;
+ $length = $this->ReadULong(4);
+ $this->tables[$tag] = array('offset'=>$offset, 'length'=>$length, 'checkSum'=>$checkSum);
}
-
- $this->ParseHead();
- $this->ParseHhea();
- $this->ParseMaxp();
- $this->ParseHmtx();
- $this->ParseCmap();
- $this->ParseName();
- $this->ParseOS2();
- $this->ParsePost();
-
- fclose($this->f);
- }
+ }
function ParseHead()
{
@@ -77,6 +94,8 @@ class TTFParser $this->yMin = $this->ReadShort();
$this->xMax = $this->ReadShort();
$this->yMax = $this->ReadShort();
+ $this->Skip(3*2); // macStyle, lowestRecPPEM, fontDirectionHint
+ $this->indexToLocFormat = $this->ReadShort();
}
function ParseHhea()
@@ -96,17 +115,79 @@ class TTFParser function ParseHmtx()
{
$this->Seek('hmtx');
- $this->widths = array();
+ $this->glyphs = array();
for($i=0;$i<$this->numberOfHMetrics;$i++)
{
$advanceWidth = $this->ReadUShort();
- $this->Skip(2); // lsb
- $this->widths[$i] = $advanceWidth;
+ $lsb = $this->ReadShort();
+ $this->glyphs[$i] = array('w'=>$advanceWidth, 'lsb'=>$lsb);
}
- if($this->numberOfHMetrics<$this->numGlyphs)
+ for($i=$this->numberOfHMetrics;$i<$this->numGlyphs;$i++)
{
- $lastWidth = $this->widths[$this->numberOfHMetrics-1];
- $this->widths = array_pad($this->widths, $this->numGlyphs, $lastWidth);
+ $lsb = $this->ReadShort();
+ $this->glyphs[$i] = array('w'=>$advanceWidth, 'lsb'=>$lsb);
+ }
+ }
+
+ function ParseLoca()
+ {
+ $this->Seek('loca');
+ $offsets = array();
+ if($this->indexToLocFormat==0)
+ {
+ // Short format
+ for($i=0;$i<=$this->numGlyphs;$i++)
+ $offsets[] = 2*$this->ReadUShort();
+ }
+ else
+ {
+ // Long format
+ for($i=0;$i<=$this->numGlyphs;$i++)
+ $offsets[] = $this->ReadULong();
+ }
+ for($i=0;$i<$this->numGlyphs;$i++)
+ {
+ $this->glyphs[$i]['offset'] = $offsets[$i];
+ $this->glyphs[$i]['length'] = $offsets[$i+1] - $offsets[$i];
+ }
+ }
+
+ function ParseGlyf()
+ {
+ $tableOffset = $this->tables['glyf']['offset'];
+ foreach($this->glyphs as &$glyph)
+ {
+ if($glyph['length']>0)
+ {
+ fseek($this->f, $tableOffset+$glyph['offset'], SEEK_SET);
+ if($this->ReadShort()<0)
+ {
+ // Composite glyph
+ $this->Skip(4*2); // xMin, yMin, xMax, yMax
+ $offset = 5*2;
+ $a = array();
+ do
+ {
+ $flags = $this->ReadUShort();
+ $index = $this->ReadUShort();
+ $a[$offset+2] = $index;
+ if($flags & 1) // ARG_1_AND_2_ARE_WORDS
+ $skip = 2*2;
+ else
+ $skip = 2;
+ if($flags & 8) // WE_HAVE_A_SCALE
+ $skip += 2;
+ elseif($flags & 64) // WE_HAVE_AN_X_AND_Y_SCALE
+ $skip += 2*2;
+ elseif($flags & 128) // WE_HAVE_A_TWO_BY_TWO
+ $skip += 4*2;
+ $this->Skip($skip);
+ $offset += 2*2 + $skip;
+ }
+ while($flags & 32); // MORE_COMPONENTS
+ $glyph['components'] = $a;
+ }
+ }
}
}
@@ -132,7 +213,7 @@ class TTFParser $idDelta = array();
$idRangeOffset = array();
$this->chars = array();
- fseek($this->f, $this->tables['cmap']+$offset31, SEEK_SET);
+ fseek($this->f, $this->tables['cmap']['offset']+$offset31, SEEK_SET);
$format = $this->ReadUShort();
if($format!=4)
$this->Error('Unexpected subtable format: '.$format);
@@ -181,7 +262,7 @@ class TTFParser function ParseName()
{
$this->Seek('name');
- $tableOffset = ftell($this->f);
+ $tableOffset = $this->tables['name']['offset'];
$this->postScriptName = '';
$this->Skip(2); // format
$count = $this->ReadUShort();
@@ -213,10 +294,10 @@ class TTFParser $version = $this->ReadUShort();
$this->Skip(3*2); // xAvgCharWidth, usWeightClass, usWidthClass
$fsType = $this->ReadUShort();
- $this->Embeddable = ($fsType!=2) && ($fsType & 0x200)==0;
+ $this->embeddable = ($fsType!=2) && ($fsType & 0x200)==0;
$this->Skip(11*2+10+4*4+4);
$fsSelection = $this->ReadUShort();
- $this->Bold = ($fsSelection & 32)!=0;
+ $this->bold = ($fsSelection & 32)!=0;
$this->Skip(2*2); // usFirstCharIndex, usLastCharIndex
$this->typoAscender = $this->ReadShort();
$this->typoDescender = $this->ReadShort();
@@ -232,27 +313,362 @@ class TTFParser function ParsePost()
{
$this->Seek('post');
- $this->Skip(4); // version
+ $version = $this->ReadULong();
$this->italicAngle = $this->ReadShort();
$this->Skip(2); // Skip decimal part
$this->underlinePosition = $this->ReadShort();
$this->underlineThickness = $this->ReadShort();
$this->isFixedPitch = ($this->ReadULong()!=0);
+ if($version==0x20000)
+ {
+ // Extract glyph names
+ $this->Skip(4*4); // min/max usage
+ $this->Skip(2); // numberOfGlyphs
+ $glyphNameIndex = array();
+ $names = array();
+ $numNames = 0;
+ for($i=0;$i<$this->numGlyphs;$i++)
+ {
+ $index = $this->ReadUShort();
+ $glyphNameIndex[] = $index;
+ if($index>=258 && $index-257>$numNames)
+ $numNames = $index-257;
+ }
+ for($i=0;$i<$numNames;$i++)
+ {
+ $len = ord($this->Read(1));
+ $names[] = $this->Read($len);
+ }
+ foreach($glyphNameIndex as $i=>$index)
+ {
+ if($index>=258)
+ $this->glyphs[$i]['name'] = $names[$index-258];
+ else
+ $this->glyphs[$i]['name'] = $index;
+ }
+ $this->glyphNames = true;
+ }
+ else
+ $this->glyphNames = false;
}
- function Error($msg)
+ function Subset($chars)
+ {
+/* $chars = array_keys($this->chars);
+ $this->subsettedChars = $chars;
+ $this->subsettedGlyphs = array();
+ for($i=0;$i<$this->numGlyphs;$i++)
+ {
+ $this->subsettedGlyphs[] = $i;
+ $this->glyphs[$i]['ssid'] = $i;
+ }*/
+
+ $this->AddGlyph(0);
+ $this->subsettedChars = array();
+ foreach($chars as $char)
+ {
+ if(isset($this->chars[$char]))
+ {
+ $this->subsettedChars[] = $char;
+ $this->AddGlyph($this->chars[$char]);
+ }
+ }
+ }
+
+ function AddGlyph($id)
{
- if(PHP_SAPI=='cli')
- die("Error: $msg\n");
+ if(!isset($this->glyphs[$id]['ssid']))
+ {
+ $this->glyphs[$id]['ssid'] = count($this->subsettedGlyphs);
+ $this->subsettedGlyphs[] = $id;
+ if(isset($this->glyphs[$id]['components']))
+ {
+ foreach($this->glyphs[$id]['components'] as $cid)
+ $this->AddGlyph($cid);
+ }
+ }
+ }
+
+ function Build()
+ {
+ $this->BuildCmap();
+ $this->BuildHhea();
+ $this->BuildHmtx();
+ $this->BuildLoca();
+ $this->BuildGlyf();
+ $this->BuildMaxp();
+ $this->BuildPost();
+ return $this->BuildFont();
+ }
+
+ function BuildCmap()
+ {
+ if(!isset($this->subsettedChars))
+ return;
+
+ // Divide charset in contiguous segments
+ $chars = $this->subsettedChars;
+ sort($chars);
+ $segments = array();
+ $segment = array($chars[0], $chars[0]);
+ for($i=1;$i<count($chars);$i++)
+ {
+ if($chars[$i]>$segment[1]+1)
+ {
+ $segments[] = $segment;
+ $segment = array($chars[$i], $chars[$i]);
+ }
+ else
+ $segment[1]++;
+ }
+ $segments[] = $segment;
+ $segments[] = array(0xFFFF, 0xFFFF);
+ $segCount = count($segments);
+
+ // Build a Format 4 subtable
+ $startCount = array();
+ $endCount = array();
+ $idDelta = array();
+ $idRangeOffset = array();
+ $glyphIdArray = '';
+ for($i=0;$i<$segCount;$i++)
+ {
+ list($start, $end) = $segments[$i];
+ $startCount[] = $start;
+ $endCount[] = $end;
+ if($start!=$end)
+ {
+ // Segment with multiple chars
+ $idDelta[] = 0;
+ $idRangeOffset[] = strlen($glyphIdArray) + ($segCount-$i)*2;
+ for($c=$start;$c<=$end;$c++)
+ {
+ $ssid = $this->glyphs[$this->chars[$c]]['ssid'];
+ $glyphIdArray .= pack('n', $ssid);
+ }
+ }
+ else
+ {
+ // Segment with a single char
+ if($start<0xFFFF)
+ $ssid = $this->glyphs[$this->chars[$start]]['ssid'];
+ else
+ $ssid = 0;
+ $idDelta[] = $ssid - $start;
+ $idRangeOffset[] = 0;
+ }
+ }
+ $entrySelector = 0;
+ $n = $segCount;
+ while($n!=1)
+ {
+ $n = $n>>1;
+ $entrySelector++;
+ }
+ $searchRange = (1<<$entrySelector)*2;
+ $rangeShift = 2*$segCount - $searchRange;
+ $cmap = pack('nnnn', 2*$segCount, $searchRange, $entrySelector, $rangeShift);
+ foreach($endCount as $val)
+ $cmap .= pack('n', $val);
+ $cmap .= pack('n', 0); // reservedPad
+ foreach($startCount as $val)
+ $cmap .= pack('n', $val);
+ foreach($idDelta as $val)
+ $cmap .= pack('n', $val);
+ foreach($idRangeOffset as $val)
+ $cmap .= pack('n', $val);
+ $cmap .= $glyphIdArray;
+
+ $data = pack('nn', 0, 1); // version, numTables
+ $data .= pack('nnN', 3, 1, 12); // platformID, encodingID, offset
+ $data .= pack('nnn', 4, 6+strlen($cmap), 0); // format, length, language
+ $data .= $cmap;
+ $this->SetTable('cmap', $data);
+ }
+
+ function BuildHhea()
+ {
+ $this->LoadTable('hhea');
+ $numberOfHMetrics = count($this->subsettedGlyphs);
+ $data = substr_replace($this->tables['hhea']['data'], pack('n',$numberOfHMetrics), 4+15*2, 2);
+ $this->SetTable('hhea', $data);
+ }
+
+ function BuildHmtx()
+ {
+ $data = '';
+ foreach($this->subsettedGlyphs as $id)
+ {
+ $glyph = $this->glyphs[$id];
+ $data .= pack('nn', $glyph['w'], $glyph['lsb']);
+ }
+ $this->SetTable('hmtx', $data);
+ }
+
+ function BuildLoca()
+ {
+ $data = '';
+ $offset = 0;
+ foreach($this->subsettedGlyphs as $id)
+ {
+ if($this->indexToLocFormat==0)
+ $data .= pack('n', $offset/2);
+ else
+ $data .= pack('N', $offset);
+ $offset += $this->glyphs[$id]['length'];
+ }
+ if($this->indexToLocFormat==0)
+ $data .= pack('n', $offset/2);
+ else
+ $data .= pack('N', $offset);
+ $this->SetTable('loca', $data);
+ }
+
+ function BuildGlyf()
+ {
+ $tableOffset = $this->tables['glyf']['offset'];
+ $data = '';
+ foreach($this->subsettedGlyphs as $id)
+ {
+ $glyph = $this->glyphs[$id];
+ fseek($this->f, $tableOffset+$glyph['offset'], SEEK_SET);
+ $glyph_data = $this->Read($glyph['length']);
+ if(isset($glyph['components']))
+ {
+ // Composite glyph
+ foreach($glyph['components'] as $offset=>$cid)
+ {
+ $ssid = $this->glyphs[$cid]['ssid'];
+ $glyph_data = substr_replace($glyph_data, pack('n',$ssid), $offset, 2);
+ }
+ }
+ $data .= $glyph_data;
+ }
+ $this->SetTable('glyf', $data);
+ }
+
+ function BuildMaxp()
+ {
+ $this->LoadTable('maxp');
+ $numGlyphs = count($this->subsettedGlyphs);
+ $data = substr_replace($this->tables['maxp']['data'], pack('n',$numGlyphs), 4, 2);
+ $this->SetTable('maxp', $data);
+ }
+
+ function BuildPost()
+ {
+ $this->Seek('post');
+ if($this->glyphNames)
+ {
+ // Version 2.0
+ $numberOfGlyphs = count($this->subsettedGlyphs);
+ $numNames = 0;
+ $names = '';
+ $data = $this->Read(2*4+2*2+5*4);
+ $data .= pack('n', $numberOfGlyphs);
+ foreach($this->subsettedGlyphs as $id)
+ {
+ $name = $this->glyphs[$id]['name'];
+ if(is_string($name))
+ {
+ $data .= pack('n', 258+$numNames);
+ $names .= chr(strlen($name)).$name;
+ $numNames++;
+ }
+ else
+ $data .= pack('n', $name);
+ }
+ $data .= $names;
+ }
else
- die("<b>Error</b>: $msg");
+ {
+ // Version 3.0
+ $this->Skip(4);
+ $data = "\x00\x03\x00\x00";
+ $data .= $this->Read(4+2*2+5*4);
+ }
+ $this->SetTable('post', $data);
+ }
+
+ function BuildFont()
+ {
+ $tags = array();
+ foreach(array('cmap', 'cvt ', 'fpgm', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post', 'prep') as $tag)
+ {
+ if(isset($this->tables[$tag]))
+ $tags[] = $tag;
+ }
+ $numTables = count($tags);
+ $offset = 12 + 16*$numTables;
+ foreach($tags as $tag)
+ {
+ if(!isset($this->tables[$tag]['data']))
+ $this->LoadTable($tag);
+ $this->tables[$tag]['offset'] = $offset;
+ $offset += strlen($this->tables[$tag]['data']);
+ }
+// $this->tables['head']['data'] = substr_replace($this->tables['head']['data'], "\x00\x00\x00\x00", 8, 4);
+
+ // Build offset table
+ $entrySelector = 0;
+ $n = $numTables;
+ while($n!=1)
+ {
+ $n = $n>>1;
+ $entrySelector++;
+ }
+ $searchRange = 16*(1<<$entrySelector);
+ $rangeShift = 16*$numTables - $searchRange;
+ $offsetTable = pack('nnnnnn', 1, 0, $numTables, $searchRange, $entrySelector, $rangeShift);
+ foreach($tags as $tag)
+ {
+ $table = $this->tables[$tag];
+ $offsetTable .= $tag.$table['checkSum'].pack('NN', $table['offset'], $table['length']);
+ }
+
+ // Compute checkSumAdjustment (0xB1B0AFBA - font checkSum)
+ $s = $this->CheckSum($offsetTable);
+ foreach($tags as $tag)
+ $s .= $this->tables[$tag]['checkSum'];
+ $a = unpack('n2', $this->CheckSum($s));
+ $high = 0xB1B0 + ($a[1]^0xFFFF);
+ $low = 0xAFBA + ($a[2]^0xFFFF) + 1;
+ $checkSumAdjustment = pack('nn', $high+($low>>16), $low);
+ $this->tables['head']['data'] = substr_replace($this->tables['head']['data'], $checkSumAdjustment, 8, 4);
+
+ $font = $offsetTable;
+ foreach($tags as $tag)
+ $font .= $this->tables[$tag]['data'];
+
+ return $font;
+ }
+
+ function LoadTable($tag)
+ {
+ $this->Seek($tag);
+ $length = $this->tables[$tag]['length'];
+ $n = $length % 4;
+ if($n>0)
+ $length += 4 - $n;
+ $this->tables[$tag]['data'] = $this->Read($length);
+ }
+
+ function SetTable($tag, $data)
+ {
+ $length = strlen($data);
+ $n = $length % 4;
+ if($n>0)
+ $data = str_pad($data, $length+4-$n, "\x00");
+ $this->tables[$tag]['data'] = $data;
+ $this->tables[$tag]['length'] = $length;
+ $this->tables[$tag]['checkSum'] = $this->CheckSum($data);
}
function Seek($tag)
{
if(!isset($this->tables[$tag]))
$this->Error('Table not found: '.$tag);
- fseek($this->f, $this->tables[$tag], SEEK_SET);
+ fseek($this->f, $this->tables[$tag]['offset'], SEEK_SET);
}
function Skip($n)
@@ -262,7 +678,7 @@ class TTFParser function Read($n)
{
- return fread($this->f, $n);
+ return $n>0 ? fread($this->f, $n) : '';
}
function ReadUShort()
@@ -285,5 +701,23 @@ class TTFParser $a = unpack('NN', fread($this->f,4));
return $a['N'];
}
+
+ function CheckSum($s)
+ {
+ $n = strlen($s);
+ $high = 0;
+ $low = 0;
+ for($i=0;$i<$n;$i+=4)
+ {
+ $high += (ord($s[$i])<<8) + ord($s[$i+1]);
+ $low += (ord($s[$i+2])<<8) + ord($s[$i+3]);
+ }
+ return pack('nn', $high+($low>>16), $low);
+ }
+
+ function Error($msg)
+ {
+ throw new Exception($msg);
+ }
}
?>
|