From 327f2b89a03717a0e1e6e102c51e547556a15ec9 Mon Sep 17 00:00:00 2001 From: Sashi20 Date: Fri, 24 Jul 2020 10:48:32 +0530 Subject: Update fpdf version --- pdf/fpdf/makefont/makefont.php | 144 ++++++++--- pdf/fpdf/makefont/ttfparser.php | 544 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 598 insertions(+), 90 deletions(-) (limited to 'pdf/fpdf/makefont') 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 = "\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 @@ 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$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("Error: $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); + } } ?> -- cgit