/lib/tcpdf/include/tcpdf_fonts.php
PHP | 2562 lines | 1943 code | 47 blank | 572 comment | 545 complexity | 60dfdd884343a68963a363deb165cac3 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, LGPL-2.1, Apache-2.0, BSD-3-Clause, AGPL-3.0
Large files files are truncated, but you can click here to view the full file
- <?php
- //============================================================+
- // File name : tcpdf_fonts.php
- // Version : 1.0.009
- // Begin : 2008-01-01
- // Last Update : 2013-09-04
- // Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
- // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
- // -------------------------------------------------------------------
- // Copyright (C) 2008-2013 Nicola Asuni - Tecnick.com LTD
- //
- // This file is part of TCPDF software library.
- //
- // TCPDF is free software: you can redistribute it and/or modify it
- // under the terms of the GNU Lesser General Public License as
- // published by the Free Software Foundation, either version 3 of the
- // License, or (at your option) any later version.
- //
- // TCPDF is distributed in the hope that it will be useful, but
- // WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- // See the GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with TCPDF. If not, see <http://www.gnu.org/licenses/>.
- //
- // See LICENSE.TXT file for more information.
- // -------------------------------------------------------------------
- //
- // Description :Font methods for TCPDF library.
- //
- //============================================================+
- /**
- * @file
- * Unicode data and font methods for TCPDF library.
- * @author Nicola Asuni
- * @package com.tecnick.tcpdf
- */
- /**
- * @class TCPDF_FONTS
- * Font methods for TCPDF library.
- * @package com.tecnick.tcpdf
- * @version 1.0.009
- * @author Nicola Asuni - info@tecnick.com
- */
- class TCPDF_FONTS {
- /**
- * Convert and add the selected TrueType or Type1 font to the fonts folder (that must be writeable).
- * @param $fontfile (string) Font file (full path).
- * @param $fonttype (string) Font type. Leave empty for autodetect mode. Valid values are: TrueTypeUnicode, TrueType, Type1, CID0JP = CID-0 Japanese, CID0KR = CID-0 Korean, CID0CS = CID-0 Chinese Simplified, CID0CT = CID-0 Chinese Traditional.
- * @param $enc (string) Name of the encoding table to use. Leave empty for default mode. Omit this parameter for TrueType Unicode and symbolic fonts like Symbol or ZapfDingBats.
- * @param $flags (int) Unsigned 32-bit integer containing flags specifying various characteristics of the font (PDF32000:2008 - 9.8.2 Font Descriptor Flags): +1 for fixed font; +4 for symbol or +32 for non-symbol; +64 for italic. Fixed and Italic mode are generally autodetected so you have to set it to 32 = non-symbolic font (default) or 4 = symbolic font.
- * @param $outpath (string) Output path for generated font files (must be writeable by the web server). Leave empty for default font folder.
- * @param $platid (int) Platform ID for CMAP table to extract (when building a Unicode font for Windows this value should be 3, for Macintosh should be 1).
- * @param $encid (int) Encoding ID for CMAP table to extract (when building a Unicode font for Windows this value should be 1, for Macintosh should be 0). When Platform ID is 3, legal values for Encoding ID are: 0=Symbol, 1=Unicode, 2=ShiftJIS, 3=PRC, 4=Big5, 5=Wansung, 6=Johab, 7=Reserved, 8=Reserved, 9=Reserved, 10=UCS-4.
- * @param $addcbbox (boolean) If true includes the character bounding box information on the php font file.
- * @param $link (boolean) If true link to system font instead of copying the font data (not transportable) - Note: do not work with Type1 fonts.
- * @return (string) TCPDF font name or boolean false in case of error.
- * @author Nicola Asuni
- * @since 5.9.123 (2010-09-30)
- * @public static
- */
- public static function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false, $link=false) {
- if (!file_exists($fontfile)) {
- // Could not find file
- return false;
- }
- // font metrics
- $fmetric = array();
- // build new font name for TCPDF compatibility
- $font_path_parts = pathinfo($fontfile);
- if (!isset($font_path_parts['filename'])) {
- $font_path_parts['filename'] = substr($font_path_parts['basename'], 0, -(strlen($font_path_parts['extension']) + 1));
- }
- $font_name = strtolower($font_path_parts['filename']);
- $font_name = preg_replace('/[^a-z0-9_]/', '', $font_name);
- $search = array('bold', 'oblique', 'italic', 'regular');
- $replace = array('b', 'i', 'i', '');
- $font_name = str_replace($search, $replace, $font_name);
- if (empty($font_name)) {
- // set generic name
- $font_name = 'tcpdffont';
- }
- // set output path
- if (empty($outpath)) {
- $outpath = self::_getfontpath();
- }
- // check if this font already exist
- if (@file_exists($outpath.$font_name.'.php')) {
- // this font already exist (delete it from fonts folder to rebuild it)
- return $font_name;
- }
- $fmetric['file'] = $font_name;
- $fmetric['ctg'] = $font_name.'.ctg.z';
- // get font data
- $font = file_get_contents($fontfile);
- $fmetric['originalsize'] = strlen($font);
- // autodetect font type
- if (empty($fonttype)) {
- if (TCPDF_STATIC::_getULONG($font, 0) == 0x10000) {
- // True Type (Unicode or not)
- $fonttype = 'TrueTypeUnicode';
- } elseif (substr($font, 0, 4) == 'OTTO') {
- // Open Type (Unicode or not)
- //Unsupported font format: OpenType with CFF data
- return false;
- } else {
- // Type 1
- $fonttype = 'Type1';
- }
- }
- // set font type
- switch ($fonttype) {
- case 'CID0CT':
- case 'CID0CS':
- case 'CID0KR':
- case 'CID0JP': {
- $fmetric['type'] = 'cidfont0';
- break;
- }
- case 'Type1': {
- $fmetric['type'] = 'Type1';
- if (empty($enc) AND (($flags & 4) == 0)) {
- $enc = 'cp1252';
- }
- break;
- }
- case 'TrueType': {
- $fmetric['type'] = 'TrueType';
- break;
- }
- case 'TrueTypeUnicode':
- default: {
- $fmetric['type'] = 'TrueTypeUnicode';
- break;
- }
- }
- // set encoding maps (if any)
- $fmetric['enc'] = preg_replace('/[^A-Za-z0-9_\-]/', '', $enc);
- $fmetric['diff'] = '';
- if (($fmetric['type'] == 'TrueType') OR ($fmetric['type'] == 'Type1')) {
- if (!empty($enc) AND ($enc != 'cp1252') AND isset(TCPDF_FONT_DATA::$encmap[$enc])) {
- // build differences from reference encoding
- $enc_ref = TCPDF_FONT_DATA::$encmap['cp1252'];
- $enc_target = TCPDF_FONT_DATA::$encmap[$enc];
- $last = 0;
- for ($i = 32; $i <= 255; ++$i) {
- if ($enc_target != $enc_ref[$i]) {
- if ($i != ($last + 1)) {
- $fmetric['diff'] .= $i.' ';
- }
- $last = $i;
- $fmetric['diff'] .= '/'.$enc_target[$i].' ';
- }
- }
- }
- }
- // parse the font by type
- if ($fmetric['type'] == 'Type1') {
- // ---------- TYPE 1 ----------
- // read first segment
- $a = unpack('Cmarker/Ctype/Vsize', substr($font, 0, 6));
- if ($a['marker'] != 128) {
- // Font file is not a valid binary Type1
- return false;
- }
- $fmetric['size1'] = $a['size'];
- $data = substr($font, 6, $fmetric['size1']);
- // read second segment
- $a = unpack('Cmarker/Ctype/Vsize', substr($font, (6 + $fmetric['size1']), 6));
- if ($a['marker'] != 128) {
- // Font file is not a valid binary Type1
- return false;
- }
- $fmetric['size2'] = $a['size'];
- $encrypted = substr($font, (12 + $fmetric['size1']), $fmetric['size2']);
- $data .= $encrypted;
- // store compressed font
- $fmetric['file'] .= '.z';
- $fp = fopen($outpath.$fmetric['file'], 'wb');
- fwrite($fp, gzcompress($data));
- fclose($fp);
- // get font info
- $fmetric['Flags'] = $flags;
- preg_match ('#/FullName[\s]*\(([^\)]*)#', $font, $matches);
- $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $matches[1]);
- preg_match('#/FontBBox[\s]*{([^}]*)#', $font, $matches);
- $fmetric['bbox'] = trim($matches[1]);
- $bv = explode(' ', $fmetric['bbox']);
- $fmetric['Ascent'] = intval($bv[3]);
- $fmetric['Descent'] = intval($bv[1]);
- preg_match('#/ItalicAngle[\s]*([0-9\+\-]*)#', $font, $matches);
- $fmetric['italicAngle'] = intval($matches[1]);
- if ($fmetric['italicAngle'] != 0) {
- $fmetric['Flags'] |= 64;
- }
- preg_match('#/UnderlinePosition[\s]*([0-9\+\-]*)#', $font, $matches);
- $fmetric['underlinePosition'] = intval($matches[1]);
- preg_match('#/UnderlineThickness[\s]*([0-9\+\-]*)#', $font, $matches);
- $fmetric['underlineThickness'] = intval($matches[1]);
- preg_match('#/isFixedPitch[\s]*([^\s]*)#', $font, $matches);
- if ($matches[1] == 'true') {
- $fmetric['Flags'] |= 1;
- }
- // get internal map
- $imap = array();
- if (preg_match_all('#dup[\s]([0-9]+)[\s]*/([^\s]*)[\s]put#sU', $font, $fmap, PREG_SET_ORDER) > 0) {
- foreach ($fmap as $v) {
- $imap[$v[2]] = $v[1];
- }
- }
- // decrypt eexec encrypted part
- $r = 55665; // eexec encryption constant
- $c1 = 52845;
- $c2 = 22719;
- $elen = strlen($encrypted);
- $eplain = '';
- for ($i = 0; $i < $elen; ++$i) {
- $chr = ord($encrypted[$i]);
- $eplain .= chr($chr ^ ($r >> 8));
- $r = ((($chr + $r) * $c1 + $c2) % 65536);
- }
- if (preg_match('#/ForceBold[\s]*([^\s]*)#', $eplain, $matches) > 0) {
- if ($matches[1] == 'true') {
- $fmetric['Flags'] |= 0x40000;
- }
- }
- if (preg_match('#/StdVW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
- $fmetric['StemV'] = intval($matches[1]);
- } else {
- $fmetric['StemV'] = 70;
- }
- if (preg_match('#/StdHW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
- $fmetric['StemH'] = intval($matches[1]);
- } else {
- $fmetric['StemH'] = 30;
- }
- if (preg_match('#/BlueValues[\s]*\[([^\]]*)#', $eplain, $matches) > 0) {
- $bv = explode(' ', $matches[1]);
- if (count($bv) >= 6) {
- $v1 = intval($bv[2]);
- $v2 = intval($bv[4]);
- if ($v1 <= $v2) {
- $fmetric['XHeight'] = $v1;
- $fmetric['CapHeight'] = $v2;
- } else {
- $fmetric['XHeight'] = $v2;
- $fmetric['CapHeight'] = $v1;
- }
- } else {
- $fmetric['XHeight'] = 450;
- $fmetric['CapHeight'] = 700;
- }
- } else {
- $fmetric['XHeight'] = 450;
- $fmetric['CapHeight'] = 700;
- }
- // get the number of random bytes at the beginning of charstrings
- if (preg_match('#/lenIV[\s]*([0-9]*)#', $eplain, $matches) > 0) {
- $lenIV = intval($matches[1]);
- } else {
- $lenIV = 4;
- }
- $fmetric['Leading'] = 0;
- // get charstring data
- $eplain = substr($eplain, (strpos($eplain, '/CharStrings') + 1));
- preg_match_all('#/([A-Za-z0-9\.]*)[\s][0-9]+[\s]RD[\s](.*)[\s]ND#sU', $eplain, $matches, PREG_SET_ORDER);
- if (!empty($enc) AND isset(TCPDF_FONT_DATA::$encmap[$enc])) {
- $enc_map = TCPDF_FONT_DATA::$encmap[$enc];
- } else {
- $enc_map = false;
- }
- $fmetric['cw'] = '';
- $fmetric['MaxWidth'] = 0;
- $cwidths = array();
- foreach ($matches as $k => $v) {
- $cid = 0;
- if (isset($imap[$v[1]])) {
- $cid = $imap[$v[1]];
- } elseif ($enc_map !== false) {
- $cid = array_search($v[1], $enc_map);
- if ($cid === false) {
- $cid = 0;
- } elseif ($cid > 1000) {
- $cid -= 1000;
- }
- }
- // decrypt charstring encrypted part
- $r = 4330; // charstring encryption constant
- $c1 = 52845;
- $c2 = 22719;
- $cd = $v[2];
- $clen = strlen($cd);
- $ccom = array();
- for ($i = 0; $i < $clen; ++$i) {
- $chr = ord($cd[$i]);
- $ccom[] = ($chr ^ ($r >> 8));
- $r = ((($chr + $r) * $c1 + $c2) % 65536);
- }
- // decode numbers
- $cdec = array();
- $ck = 0;
- $i = $lenIV;
- while ($i < $clen) {
- if ($ccom[$i] < 32) {
- $cdec[$ck] = $ccom[$i];
- if (($ck > 0) AND ($cdec[$ck] == 13)) {
- // hsbw command: update width
- $cwidths[$cid] = $cdec[($ck - 1)];
- }
- ++$i;
- } elseif (($ccom[$i] >= 32) AND ($ccom[$i] <= 246)) {
- $cdec[$ck] = ($ccom[$i] - 139);
- ++$i;
- } elseif (($ccom[$i] >= 247) AND ($ccom[$i] <= 250)) {
- $cdec[$ck] = ((($ccom[$i] - 247) * 256) + $ccom[($i + 1)] + 108);
- $i += 2;
- } elseif (($ccom[$i] >= 251) AND ($ccom[$i] <= 254)) {
- $cdec[$ck] = ((-($ccom[$i] - 251) * 256) - $ccom[($i + 1)] - 108);
- $i += 2;
- } elseif ($ccom[$i] == 255) {
- $sval = chr($ccom[($i + 1)]).chr($ccom[($i + 2)]).chr($ccom[($i + 3)]).chr($ccom[($i + 4)]);
- $vsval = unpack('li', $sval);
- $cdec[$ck] = $vsval['i'];
- $i += 5;
- }
- ++$ck;
- }
- } // end for each matches
- $fmetric['MissingWidth'] = $cwidths[0];
- $fmetric['MaxWidth'] = $fmetric['MissingWidth'];
- $fmetric['AvgWidth'] = 0;
- // set chars widths
- for ($cid = 0; $cid <= 255; ++$cid) {
- if (isset($cwidths[$cid])) {
- if ($cwidths[$cid] > $fmetric['MaxWidth']) {
- $fmetric['MaxWidth'] = $cwidths[$cid];
- }
- $fmetric['AvgWidth'] += $cwidths[$cid];
- $fmetric['cw'] .= ','.$cid.'=>'.$cwidths[$cid];
- } else {
- $fmetric['cw'] .= ','.$cid.'=>'.$fmetric['MissingWidth'];
- }
- }
- $fmetric['AvgWidth'] = round($fmetric['AvgWidth'] / count($cwidths));
- } else {
- // ---------- TRUE TYPE ----------
- if ($fmetric['type'] != 'cidfont0') {
- if ($link) {
- // creates a symbolic link to the existing font
- symlink($fontfile, $outpath.$fmetric['file']);
- } else {
- // store compressed font
- $fmetric['file'] .= '.z';
- $fp = fopen($outpath.$fmetric['file'], 'wb');
- fwrite($fp, gzcompress($font));
- fclose($fp);
- }
- }
- $offset = 0; // offset position of the font data
- if (TCPDF_STATIC::_getULONG($font, $offset) != 0x10000) {
- // sfnt version must be 0x00010000 for TrueType version 1.0.
- return false;
- }
- $offset += 4;
- // get number of tables
- $numTables = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- // skip searchRange, entrySelector and rangeShift
- $offset += 6;
- // tables array
- $table = array();
- // ---------- get tables ----------
- for ($i = 0; $i < $numTables; ++$i) {
- // get table info
- $tag = substr($font, $offset, 4);
- $offset += 4;
- $table[$tag] = array();
- $table[$tag]['checkSum'] = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- $table[$tag]['offset'] = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- $table[$tag]['length'] = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- }
- // check magicNumber
- $offset = $table['head']['offset'] + 12;
- if (TCPDF_STATIC::_getULONG($font, $offset) != 0x5F0F3CF5) {
- // magicNumber must be 0x5F0F3CF5
- return false;
- }
- $offset += 4;
- $offset += 2; // skip flags
- // get FUnits
- $fmetric['unitsPerEm'] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- // units ratio constant
- $urk = (1000 / $fmetric['unitsPerEm']);
- $offset += 16; // skip created, modified
- $xMin = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
- $offset += 2;
- $yMin = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
- $offset += 2;
- $xMax = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
- $offset += 2;
- $yMax = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
- $offset += 2;
- $fmetric['bbox'] = ''.$xMin.' '.$yMin.' '.$xMax.' '.$yMax.'';
- $macStyle = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- // PDF font flags
- $fmetric['Flags'] = $flags;
- if (($macStyle & 2) == 2) {
- // italic flag
- $fmetric['Flags'] |= 64;
- }
- // get offset mode (indexToLocFormat : 0 = short, 1 = long)
- $offset = $table['head']['offset'] + 50;
- $short_offset = (TCPDF_STATIC::_getSHORT($font, $offset) == 0);
- $offset += 2;
- // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table
- $indexToLoc = array();
- $offset = $table['loca']['offset'];
- if ($short_offset) {
- // short version
- $tot_num_glyphs = floor($table['loca']['length'] / 2); // numGlyphs + 1
- for ($i = 0; $i < $tot_num_glyphs; ++$i) {
- $indexToLoc[$i] = TCPDF_STATIC::_getUSHORT($font, $offset) * 2;
- $offset += 2;
- }
- } else {
- // long version
- $tot_num_glyphs = floor($table['loca']['length'] / 4); // numGlyphs + 1
- for ($i = 0; $i < $tot_num_glyphs; ++$i) {
- $indexToLoc[$i] = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- }
- }
- // get glyphs indexes of chars from cmap table
- $offset = $table['cmap']['offset'] + 2;
- $numEncodingTables = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $encodingTables = array();
- for ($i = 0; $i < $numEncodingTables; ++$i) {
- $encodingTables[$i]['platformID'] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $encodingTables[$i]['encodingID'] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $encodingTables[$i]['offset'] = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- }
- // ---------- get os/2 metrics ----------
- $offset = $table['OS/2']['offset'];
- $offset += 2; // skip version
- // xAvgCharWidth
- $fmetric['AvgWidth'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
- $offset += 2;
- // usWeightClass
- $usWeightClass = round(TCPDF_STATIC::_getUFWORD($font, $offset) * $urk);
- // estimate StemV and StemH (400 = usWeightClass for Normal - Regular font)
- $fmetric['StemV'] = round((70 * $usWeightClass) / 400);
- $fmetric['StemH'] = round((30 * $usWeightClass) / 400);
- $offset += 2;
- $offset += 2; // usWidthClass
- $fsType = TCPDF_STATIC::_getSHORT($font, $offset);
- $offset += 2;
- if ($fsType == 2) {
- // This Font cannot be modified, embedded or exchanged in any manner without first obtaining permission of the legal owner.
- return false;
- }
- // ---------- get font name ----------
- $fmetric['name'] = '';
- $offset = $table['name']['offset'];
- $offset += 2; // skip Format selector (=0).
- // Number of NameRecords that follow n.
- $numNameRecords = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- // Offset to start of string storage (from start of table).
- $stringStorageOffset = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- for ($i = 0; $i < $numNameRecords; ++$i) {
- $offset += 6; // skip Platform ID, Platform-specific encoding ID, Language ID.
- // Name ID.
- $nameID = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- if ($nameID == 6) {
- // String length (in bytes).
- $stringLength = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- // String offset from start of storage area (in bytes).
- $stringOffset = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $offset = ($table['name']['offset'] + $stringStorageOffset + $stringOffset);
- $fmetric['name'] = substr($font, $offset, $stringLength);
- $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $fmetric['name']);
- break;
- } else {
- $offset += 4; // skip String length, String offset
- }
- }
- if (empty($fmetric['name'])) {
- $fmetric['name'] = $font_name;
- }
- // ---------- get post data ----------
- $offset = $table['post']['offset'];
- $offset += 4; // skip Format Type
- $fmetric['italicAngle'] = TCPDF_STATIC::_getFIXED($font, $offset);
- $offset += 4;
- $fmetric['underlinePosition'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
- $offset += 2;
- $fmetric['underlineThickness'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
- $offset += 2;
- $isFixedPitch = (TCPDF_STATIC::_getULONG($font, $offset) == 0) ? false : true;
- $offset += 2;
- if ($isFixedPitch) {
- $fmetric['Flags'] |= 1;
- }
- // ---------- get hhea data ----------
- $offset = $table['hhea']['offset'];
- $offset += 4; // skip Table version number
- // Ascender
- $fmetric['Ascent'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
- $offset += 2;
- // Descender
- $fmetric['Descent'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
- $offset += 2;
- // LineGap
- $fmetric['Leading'] = round(TCPDF_STATIC::_getFWORD($font, $offset) * $urk);
- $offset += 2;
- // advanceWidthMax
- $fmetric['MaxWidth'] = round(TCPDF_STATIC::_getUFWORD($font, $offset) * $urk);
- $offset += 2;
- $offset += 22; // skip some values
- // get the number of hMetric entries in hmtx table
- $numberOfHMetrics = TCPDF_STATIC::_getUSHORT($font, $offset);
- // ---------- get maxp data ----------
- $offset = $table['maxp']['offset'];
- $offset += 4; // skip Table version number
- // get the the number of glyphs in the font.
- $numGlyphs = TCPDF_STATIC::_getUSHORT($font, $offset);
- // ---------- get CIDToGIDMap ----------
- $ctg = array();
- foreach ($encodingTables as $enctable) {
- // get only specified Platform ID and Encoding ID
- if (($enctable['platformID'] == $platid) AND ($enctable['encodingID'] == $encid)) {
- $offset = $table['cmap']['offset'] + $enctable['offset'];
- $format = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- switch ($format) {
- case 0: { // Format 0: Byte encoding table
- $offset += 4; // skip length and version/language
- for ($c = 0; $c < 256; ++$c) {
- $g = TCPDF_STATIC::_getBYTE($font, $offset);
- $ctg[$c] = $g;
- ++$offset;
- }
- break;
- }
- case 2: { // Format 2: High-byte mapping through table
- $offset += 4; // skip length and version/language
- $numSubHeaders = 0;
- for ($i = 0; $i < 256; ++$i) {
- // Array that maps high bytes to subHeaders: value is subHeader index * 8.
- $subHeaderKeys[$i] = (TCPDF_STATIC::_getUSHORT($font, $offset) / 8);
- $offset += 2;
- if ($numSubHeaders < $subHeaderKeys[$i]) {
- $numSubHeaders = $subHeaderKeys[$i];
- }
- }
- // the number of subHeaders is equal to the max of subHeaderKeys + 1
- ++$numSubHeaders;
- // read subHeader structures
- $subHeaders = array();
- $numGlyphIndexArray = 0;
- for ($k = 0; $k < $numSubHeaders; ++$k) {
- $subHeaders[$k]['firstCode'] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $subHeaders[$k]['entryCount'] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $subHeaders[$k]['idDelta'] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $subHeaders[$k]['idRangeOffset'] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8));
- $subHeaders[$k]['idRangeOffset'] /= 2;
- $numGlyphIndexArray += $subHeaders[$k]['entryCount'];
- }
- for ($k = 0; $k < $numGlyphIndexArray; ++$k) {
- $glyphIndexArray[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- }
- for ($i = 0; $i < 256; ++$i) {
- $k = $subHeaderKeys[$i];
- if ($k == 0) {
- // one byte code
- $c = $i;
- $g = $glyphIndexArray[0];
- $ctg[$c] = $g;
- } else {
- // two bytes code
- $start_byte = $subHeaders[$k]['firstCode'];
- $end_byte = $start_byte + $subHeaders[$k]['entryCount'];
- for ($j = $start_byte; $j < $end_byte; ++$j) {
- // combine high and low bytes
- $c = (($i << 8) + $j);
- $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']);
- $g = ($glyphIndexArray[$idRangeOffset] + $idDelta[$k]) % 65536;
- if ($g < 0) {
- $g = 0;
- }
- $ctg[$c] = $g;
- }
- }
- }
- break;
- }
- case 4: { // Format 4: Segment mapping to delta values
- $length = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $offset += 2; // skip version/language
- $segCount = floor(TCPDF_STATIC::_getUSHORT($font, $offset) / 2);
- $offset += 2;
- $offset += 6; // skip searchRange, entrySelector, rangeShift
- $endCount = array(); // array of end character codes for each segment
- for ($k = 0; $k < $segCount; ++$k) {
- $endCount[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- }
- $offset += 2; // skip reservedPad
- $startCount = array(); // array of start character codes for each segment
- for ($k = 0; $k < $segCount; ++$k) {
- $startCount[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- }
- $idDelta = array(); // delta for all character codes in segment
- for ($k = 0; $k < $segCount; ++$k) {
- $idDelta[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- }
- $idRangeOffset = array(); // Offsets into glyphIdArray or 0
- for ($k = 0; $k < $segCount; ++$k) {
- $idRangeOffset[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- }
- $gidlen = (floor($length / 2) - 8 - (4 * $segCount));
- $glyphIdArray = array(); // glyph index array
- for ($k = 0; $k < $gidlen; ++$k) {
- $glyphIdArray[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- }
- for ($k = 0; $k < $segCount; ++$k) {
- for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) {
- if ($idRangeOffset[$k] == 0) {
- $g = ($idDelta[$k] + $c) % 65536;
- } else {
- $gid = (floor($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k));
- $g = ($glyphIdArray[$gid] + $idDelta[$k]) % 65536;
- }
- if ($g < 0) {
- $g = 0;
- }
- $ctg[$c] = $g;
- }
- }
- break;
- }
- case 6: { // Format 6: Trimmed table mapping
- $offset += 4; // skip length and version/language
- $firstCode = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $entryCount = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- for ($k = 0; $k < $entryCount; ++$k) {
- $c = ($k + $firstCode);
- $g = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $ctg[$c] = $g;
- }
- break;
- }
- case 8: { // Format 8: Mixed 16-bit and 32-bit coverage
- $offset += 10; // skip reserved, length and version/language
- for ($k = 0; $k < 8192; ++$k) {
- $is32[$k] = TCPDF_STATIC::_getBYTE($font, $offset);
- ++$offset;
- }
- $nGroups = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- for ($i = 0; $i < $nGroups; ++$i) {
- $startCharCode = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- $endCharCode = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- $startGlyphID = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- for ($k = $startCharCode; $k <= $endCharCode; ++$k) {
- $is32idx = floor($c / 8);
- if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) {
- $c = $k;
- } else {
- // 32 bit format
- // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4)
- //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232
- //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888
- $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888;
- }
- $ctg[$c] = 0;
- ++$startGlyphID;
- }
- }
- break;
- }
- case 10: { // Format 10: Trimmed array
- $offset += 10; // skip reserved, length and version/language
- $startCharCode = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- $numChars = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- for ($k = 0; $k < $numChars; ++$k) {
- $c = ($k + $startCharCode);
- $g = TCPDF_STATIC::_getUSHORT($font, $offset);
- $ctg[$c] = $g;
- $offset += 2;
- }
- break;
- }
- case 12: { // Format 12: Segmented coverage
- $offset += 10; // skip length and version/language
- $nGroups = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- for ($k = 0; $k < $nGroups; ++$k) {
- $startCharCode = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- $endCharCode = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- $startGlyphCode = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- for ($c = $startCharCode; $c <= $endCharCode; ++$c) {
- $ctg[$c] = $startGlyphCode;
- ++$startGlyphCode;
- }
- }
- break;
- }
- case 13: { // Format 13: Many-to-one range mappings
- // to be implemented ...
- break;
- }
- case 14: { // Format 14: Unicode Variation Sequences
- // to be implemented ...
- break;
- }
- }
- }
- }
- if (!isset($ctg[0])) {
- $ctg[0] = 0;
- }
- // get xHeight (height of x)
- $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[120]] + 4);
- $yMin = TCPDF_STATIC::_getFWORD($font, $offset);
- $offset += 4;
- $yMax = TCPDF_STATIC::_getFWORD($font, $offset);
- $offset += 2;
- $fmetric['XHeight'] = round(($yMax - $yMin) * $urk);
- // get CapHeight (height of H)
- $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[72]] + 4);
- $yMin = TCPDF_STATIC::_getFWORD($font, $offset);
- $offset += 4;
- $yMax = TCPDF_STATIC::_getFWORD($font, $offset);
- $offset += 2;
- $fmetric['CapHeight'] = round(($yMax - $yMin) * $urk);
- // ceate widths array
- $cw = array();
- $offset = $table['hmtx']['offset'];
- for ($i = 0 ; $i < $numberOfHMetrics; ++$i) {
- $cw[$i] = round(TCPDF_STATIC::_getUFWORD($font, $offset) * $urk);
- $offset += 4; // skip lsb
- }
- if ($numberOfHMetrics < $numGlyphs) {
- // fill missing widths with the last value
- $cw = array_pad($cw, $numGlyphs, $cw[($numberOfHMetrics - 1)]);
- }
- $fmetric['MissingWidth'] = $cw[0];
- $fmetric['cw'] = '';
- for ($cid = 0; $cid <= 65535; ++$cid) {
- if (isset($ctg[$cid])) {
- if (isset($cw[$ctg[$cid]])) {
- $fmetric['cw'] .= ','.$cid.'=>'.$cw[$ctg[$cid]];
- }
- if ($addcbbox AND isset($indexToLoc[$ctg[$cid]])) {
- $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[$cid]]);
- $xMin = round(TCPDF_STATIC::_getFWORD($font, $offset + 2)) * $urk;
- $yMin = round(TCPDF_STATIC::_getFWORD($font, $offset + 4)) * $urk;
- $xMax = round(TCPDF_STATIC::_getFWORD($font, $offset + 6)) * $urk;
- $yMax = round(TCPDF_STATIC::_getFWORD($font, $offset + 8)) * $urk;
- $fmetric['cbbox'] .= ','.$cid.'=>array('.$xMin.','.$yMin.','.$xMax.','.$yMax.')';
- }
- }
- }
- } // end of true type
- if (($fmetric['type'] == 'TrueTypeUnicode') AND (count($ctg) == 256)) {
- $fmetric['type'] == 'TrueType';
- }
- // ---------- create php font file ----------
- $pfile = '<'.'?'.'php'."\n";
- $pfile .= '// TCPDF FONT FILE DESCRIPTION'."\n";
- $pfile .= '$type=\''.$fmetric['type'].'\';'."\n";
- $pfile .= '$name=\''.$fmetric['name'].'\';'."\n";
- $pfile .= '$up='.$fmetric['underlinePosition'].';'."\n";
- $pfile .= '$ut='.$fmetric['underlineThickness'].';'."\n";
- if ($fmetric['MissingWidth'] > 0) {
- $pfile .= '$dw='.$fmetric['MissingWidth'].';'."\n";
- } else {
- $pfile .= '$dw='.$fmetric['AvgWidth'].';'."\n";
- }
- $pfile .= '$diff=\''.$fmetric['diff'].'\';'."\n";
- if ($fmetric['type'] == 'Type1') {
- // Type 1
- $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n";
- $pfile .= '$file=\''.$fmetric['file'].'\';'."\n";
- $pfile .= '$size1='.$fmetric['size1'].';'."\n";
- $pfile .= '$size2='.$fmetric['size2'].';'."\n";
- } else {
- $pfile .= '$originalsize='.$fmetric['originalsize'].';'."\n";
- if ($fmetric['type'] == 'cidfont0') {
- // CID-0
- switch ($fonttype) {
- case 'CID0JP': {
- $pfile .= '// Japanese'."\n";
- $pfile .= '$enc=\'UniJIS-UTF16-H\';'."\n";
- $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Japan1\',\'Supplement\'=>5);'."\n";
- $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n";
- break;
- }
- case 'CID0KR': {
- $pfile .= '// Korean'."\n";
- $pfile .= '$enc=\'UniKS-UTF16-H\';'."\n";
- $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Korea1\',\'Supplement\'=>0);'."\n";
- $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ak12.php\');'."\n";
- break;
- }
- case 'CID0CS': {
- $pfile .= '// Chinese Simplified'."\n";
- $pfile .= '$enc=\'UniGB-UTF16-H\';'."\n";
- $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'GB1\',\'Supplement\'=>2);'."\n";
- $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ag15.php\');'."\n";
- break;
- }
- case 'CID0CT':
- default: {
- $pfile .= '// Chinese Traditional'."\n";
- $pfile .= '$enc=\'UniCNS-UTF16-H\';'."\n";
- $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'CNS1\',\'Supplement\'=>0);'."\n";
- $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n";
- break;
- }
- }
- } else {
- // TrueType
- $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n";
- $pfile .= '$file=\''.$fmetric['file'].'\';'."\n";
- $pfile .= '$ctg=\''.$fmetric['ctg'].'\';'."\n";
- // create CIDToGIDMap
- $cidtogidmap = str_pad('', 131072, "\x00"); // (256 * 256 * 2) = 131072
- foreach ($ctg as $cid => $gid) {
- $cidtogidmap = self::updateCIDtoGIDmap($cidtogidmap, $cid, $ctg[$cid]);
- }
- // store compressed CIDToGIDMap
- $fp = fopen($outpath.$fmetric['ctg'], 'wb');
- fwrite($fp, gzcompress($cidtogidmap));
- fclose($fp);
- }
- }
- $pfile .= '$desc=array(';
- $pfile .= '\'Flags\'=>'.$fmetric['Flags'].',';
- $pfile .= '\'FontBBox\'=>\'['.$fmetric['bbox'].']\',';
- $pfile .= '\'ItalicAngle\'=>'.$fmetric['italicAngle'].',';
- $pfile .= '\'Ascent\'=>'.$fmetric['Ascent'].',';
- $pfile .= '\'Descent\'=>'.$fmetric['Descent'].',';
- $pfile .= '\'Leading\'=>'.$fmetric['Leading'].',';
- $pfile .= '\'CapHeight\'=>'.$fmetric['CapHeight'].',';
- $pfile .= '\'XHeight\'=>'.$fmetric['XHeight'].',';
- $pfile .= '\'StemV\'=>'.$fmetric['StemV'].',';
- $pfile .= '\'StemH\'=>'.$fmetric['StemH'].',';
- $pfile .= '\'AvgWidth\'=>'.$fmetric['AvgWidth'].',';
- $pfile .= '\'MaxWidth\'=>'.$fmetric['MaxWidth'].',';
- $pfile .= '\'MissingWidth\'=>'.$fmetric['MissingWidth'].'';
- $pfile .= ');'."\n";
- if (isset($fmetric['cbbox'])) {
- $pfile .= '$cbbox=array('.substr($fmetric['cbbox'], 1).');'."\n";
- }
- $pfile .= '$cw=array('.substr($fmetric['cw'], 1).');'."\n";
- $pfile .= '// --- EOF ---'."\n";
- // store file
- $fp = fopen($outpath.$font_name.'.php', 'w');
- fwrite($fp, $pfile);
- fclose($fp);
- // return TCPDF font name
- return $font_name;
- }
- /**
- * Returs the checksum of a TTF table.
- * @param $table (string) table to check
- * @param $length (int) length of table in bytes
- * @return int checksum
- * @author Nicola Asuni
- * @since 5.2.000 (2010-06-02)
- * @public static
- */
- public static function _getTTFtableChecksum($table, $length) {
- $sum = 0;
- $tlen = ($length / 4);
- $offset = 0;
- for ($i = 0; $i < $tlen; ++$i) {
- $v = unpack('Ni', substr($table, $offset, 4));
- $sum += $v['i'];
- $offset += 4;
- }
- $sum = unpack('Ni', pack('N', $sum));
- return $sum['i'];
- }
- /**
- * Returns a subset of the TrueType font data without the unused glyphs.
- * @param $font (string) TrueType font data.
- * @param $subsetchars (array) Array of used characters (the glyphs to keep).
- * @return (string) A subset of TrueType font data without the unused glyphs.
- * @author Nicola Asuni
- * @since 5.2.000 (2010-06-02)
- * @public static
- */
- public static function _getTrueTypeFontSubset($font, $subsetchars) {
- ksort($subsetchars);
- $offset = 0; // offset position of the font data
- if (TCPDF_STATIC::_getULONG($font, $offset) != 0x10000) {
- // sfnt version must be 0x00010000 for TrueType version 1.0.
- return $font;
- }
- $offset += 4;
- // get number of tables
- $numTables = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- // skip searchRange, entrySelector and rangeShift
- $offset += 6;
- // tables array
- $table = array();
- // for each table
- for ($i = 0; $i < $numTables; ++$i) {
- // get table info
- $tag = substr($font, $offset, 4);
- $offset += 4;
- $table[$tag] = array();
- $table[$tag]['checkSum'] = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- $table[$tag]['offset'] = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- $table[$tag]['length'] = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- }
- // check magicNumber
- $offset = $table['head']['offset'] + 12;
- if (TCPDF_STATIC::_getULONG($font, $offset) != 0x5F0F3CF5) {
- // magicNumber must be 0x5F0F3CF5
- return $font;
- }
- $offset += 4;
- // get offset mode (indexToLocFormat : 0 = short, 1 = long)
- $offset = $table['head']['offset'] + 50;
- $short_offset = (TCPDF_STATIC::_getSHORT($font, $offset) == 0);
- $offset += 2;
- // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table
- $indexToLoc = array();
- $offset = $table['loca']['offset'];
- if ($short_offset) {
- // short version
- $tot_num_glyphs = floor($table['loca']['length'] / 2); // numGlyphs + 1
- for ($i = 0; $i < $tot_num_glyphs; ++$i) {
- $indexToLoc[$i] = TCPDF_STATIC::_getUSHORT($font, $offset) * 2;
- $offset += 2;
- }
- } else {
- // long version
- $tot_num_glyphs = ($table['loca']['length'] / 4); // numGlyphs + 1
- for ($i = 0; $i < $tot_num_glyphs; ++$i) {
- $indexToLoc[$i] = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- }
- }
- // get glyphs indexes of chars from cmap table
- $subsetglyphs = array(); // glyph IDs on key
- $subsetglyphs[0] = true; // character codes that do not correspond to any glyph in the font should be mapped to glyph index 0
- $offset = $table['cmap']['offset'] + 2;
- $numEncodingTables = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $encodingTables = array();
- for ($i = 0; $i < $numEncodingTables; ++$i) {
- $encodingTables[$i]['platformID'] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $encodingTables[$i]['encodingID'] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $encodingTables[$i]['offset'] = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- }
- foreach ($encodingTables as $enctable) {
- // get all platforms and encodings
- $offset = $table['cmap']['offset'] + $enctable['offset'];
- $format = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- switch ($format) {
- case 0: { // Format 0: Byte encoding table
- $offset += 4; // skip length and version/language
- for ($c = 0; $c < 256; ++$c) {
- if (isset($subsetchars[$c])) {
- $g = TCPDF_STATIC::_getBYTE($font, $offset);
- $subsetglyphs[$g] = true;
- }
- ++$offset;
- }
- break;
- }
- case 2: { // Format 2: High-byte mapping through table
- $offset += 4; // skip length and version/language
- $numSubHeaders = 0;
- for ($i = 0; $i < 256; ++$i) {
- // Array that maps high bytes to subHeaders: value is subHeader index * 8.
- $subHeaderKeys[$i] = (TCPDF_STATIC::_getUSHORT($font, $offset) / 8);
- $offset += 2;
- if ($numSubHeaders < $subHeaderKeys[$i]) {
- $numSubHeaders = $subHeaderKeys[$i];
- }
- }
- // the number of subHeaders is equal to the max of subHeaderKeys + 1
- ++$numSubHeaders;
- // read subHeader structures
- $subHeaders = array();
- $numGlyphIndexArray = 0;
- for ($k = 0; $k < $numSubHeaders; ++$k) {
- $subHeaders[$k]['firstCode'] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $subHeaders[$k]['entryCount'] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $subHeaders[$k]['idDelta'] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $subHeaders[$k]['idRangeOffset'] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8));
- $subHeaders[$k]['idRangeOffset'] /= 2;
- $numGlyphIndexArray += $subHeaders[$k]['entryCount'];
- }
- for ($k = 0; $k < $numGlyphIndexArray; ++$k) {
- $glyphIndexArray[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- }
- for ($i = 0; $i < 256; ++$i) {
- $k = $subHeaderKeys[$i];
- if ($k == 0) {
- // one byte code
- $c = $i;
- if (isset($subsetchars[$c])) {
- $g = $glyphIndexArray[0];
- $subsetglyphs[$g] = true;
- }
- } else {
- // two bytes code
- $start_byte = $subHeaders[$k]['firstCode'];
- $end_byte = $start_byte + $subHeaders[$k]['entryCount'];
- for ($j = $start_byte; $j < $end_byte; ++$j) {
- // combine high and low bytes
- $c = (($i << 8) + $j);
- if (isset($subsetchars[$c])) {
- $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']);
- $g = ($glyphIndexArray[$idRangeOffset] + $idDelta[$k]) % 65536;
- if ($g < 0) {
- $g = 0;
- }
- $subsetglyphs[$g] = true;
- }
- }
- }
- }
- break;
- }
- case 4: { // Format 4: Segment mapping to delta values
- $length = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $offset += 2; // skip version/language
- $segCount = floor(TCPDF_STATIC::_getUSHORT($font, $offset) / 2);
- $offset += 2;
- $offset += 6; // skip searchRange, entrySelector, rangeShift
- $endCount = array(); // array of end character codes for each segment
- for ($k = 0; $k < $segCount; ++$k) {
- $endCount[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- }
- $offset += 2; // skip reservedPad
- $startCount = array(); // array of start character codes for each segment
- for ($k = 0; $k < $segCount; ++$k) {
- $startCount[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- }
- $idDelta = array(); // delta for all character codes in segment
- for ($k = 0; $k < $segCount; ++$k) {
- $idDelta[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- }
- $idRangeOffset = array(); // Offsets into glyphIdArray or 0
- for ($k = 0; $k < $segCount; ++$k) {
- $idRangeOffset[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- }
- $gidlen = (floor($length / 2) - 8 - (4 * $segCount));
- $glyphIdArray = array(); // glyph index array
- for ($k = 0; $k < $gidlen; ++$k) {
- $glyphIdArray[$k] = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- }
- for ($k = 0; $k < $segCount; ++$k) {
- for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) {
- if (isset($subsetchars[$c])) {
- if ($idRangeOffset[$k] == 0) {
- $g = ($idDelta[$k] + $c) % 65536;
- } else {
- $gid = (floor($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k));
- $g = ($glyphIdArray[$gid] + $idDelta[$k]) % 65536;
- }
- if ($g < 0) {
- $g = 0;
- }
- $subsetglyphs[$g] = true;
- }
- }
- }
- break;
- }
- case 6: { // Format 6: Trimmed table mapping
- $offset += 4; // skip length and version/language
- $firstCode = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $entryCount = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- for ($k = 0; $k < $entryCount; ++$k) {
- $c = ($k + $firstCode);
- if (isset($subsetchars[$c])) {
- $g = TCPDF_STATIC::_getUSHORT($font, $offset);
- $subsetglyphs[$g] = true;
- }
- $offset += 2;
- }
- break;
- }
- case 8: { // Format 8: Mixed 16-bit and 32-bit coverage
- $offset += 10; // skip reserved, length and version/language
- for ($k = 0; $k < 8192; ++$k) {
- $is32[$k] = TCPDF_STATIC::_getBYTE($font, $offset);
- ++$offset;
- }
- $nGroups = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- for ($i = 0; $i < $nGroups; ++$i) {
- $startCharCode = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- $endCharCode = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- $startGlyphID = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- for ($k = $startCharCode; $k <= $endCharCode; ++$k) {
- $is32idx = floor($c / 8);
- if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) {
- $c = $k;
- } else {
- // 32 bit format
- // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4)
- //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232
- //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888
- $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888;
- }
- if (isset($subsetchars[$c])) {
- $subsetglyphs[$startGlyphID] = true;
- }
- ++$startGlyphID;
- }
- }
- break;
- }
- case 10: { // Format 10: Trimmed array
- $offset += 10; // skip reserved, length and version/language
- $startCharCode = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- $numChars = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- for ($k = 0; $k < $numChars; ++$k) {
- $c = ($k + $startCharCode);
- if (isset($subsetchars[$c])) {
- $g = TCPDF_STATIC::_getUSHORT($font, $offset);
- $subsetglyphs[$g] = true;
- }
- $offset += 2;
- }
- break;
- }
- case 12: { // Format 12: Segmented coverage
- $offset += 10; // skip length and version/language
- $nGroups = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- for ($k = 0; $k < $nGroups; ++$k) {
- $startCharCode = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- $endCharCode = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- $startGlyphCode = TCPDF_STATIC::_getULONG($font, $offset);
- $offset += 4;
- for ($c = $startCharCode; $c <= $endCharCode; ++$c) {
- if (isset($subsetchars[$c])) {
- $subsetglyphs[$startGlyphCode] = true;
- }
- ++$startGlyphCode;
- }
- }
- break;
- }
- case 13: { // Format 13: Many-to-one range mappings
- // to be implemented ...
- break;
- }
- case 14: { // Format 14: Unicode Variation Sequences
- // to be implemented ...
- break;
- }
- }
- }
- // include all parts of composite glyphs
- $new_sga = $subsetglyphs;
- while (!empty($new_sga)) {
- $sga = $new_sga;
- $new_sga = array();
- foreach ($sga as $key => $val) {
- if (isset($indexToLoc[$key])) {
- $offset = ($table['glyf']['offset'] + $indexToLoc[$key]);
- $numberOfContours = TCPDF_STATIC::_getSHORT($font, $offset);
- $offset += 2;
- if ($numberOfContours < 0) { // composite glyph
- $offset += 8; // skip xMin, yMin, xMax, yMax
- do {
- $flags = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- $glyphIndex = TCPDF_STATIC::_getUSHORT($font, $offset);
- $offset += 2;
- if (!isset($subsetglyphs[$glyphIndex])) {
- // add missing glyphs
- $new_sga[$glyphIndex] = true;
- }
- // skip some bytes by case
- if ($flags & 1) {
- $offset += 4;
- } else {
- $offset += 2;
- }
- if ($flags & 8) {
- $offset += 2;
- } elseif ($flags & 64) {
- $offset += 4;
- } elseif ($flags & 128) {
- $offset += 8;
- }
- } while ($flags & 32);
- }
- }
- }
- $subsetglyphs += $new_sga;
- }
- // sort glyphs by key (and remove duplicates)
- ksort($subsetglyphs);
- // build new glyf and loca tables
- $glyf = '';
- $loca = '';
- $offset = 0;
- $glyf_offset = $table['glyf']['offset'];
- for ($i = 0; $i < $tot_num_glyphs; ++$i) {
- if (isset($subsetglyphs[$i])) {
- $length = ($indexToLoc[($i + 1)] - $indexToLoc[$i]);
- $glyf .= substr($font, ($glyf_offset + $indexToLoc[$i]), $length);
- } else {
- $length = 0;
- }
- if ($short_offset) {
- $loca .= pack('n', floor($offset / 2));
- } else {
- $loca .= pack('N', $offset);
- }
- $offset += $length;
- }
- // array of table names to preserve (loca and glyf tables will be added later)
- // the cmap table is not needed and shall not be present, since the mapping from character codes to glyph descriptions is provided separately
- $table_names = array ('head', 'hhea', 'hmtx', 'maxp', 'cvt ', 'fpgm', 'prep'); // minimum required table names
- // get the tables to preserve
- $offset = 12;
- foreach ($table as $tag => $val) {
- if (in_array($tag, $table_names)) {
- $table[$tag]['data'] = substr($font, $table[$tag]['offset'], $table[$tag]['length']);
- if ($tag == 'head') {
- // set the checkSumAdjustment to 0
- $table[$tag]['data'] = substr($table[$tag]['data'], 0, 8)."\x0\x0\x0\x0".substr($table[$tag]['data'], 12);
- }
- $pad = 4 - ($table[$tag]['length'] % 4);
- if ($pad != 4) {
- // the length of a table must be a multiple of four bytes
- $table[$tag]['length'] += $pad;
- $table[$tag]['data'] .= str_repeat("\x0", $pad);
- }
- $table[$tag]['offset'] = $offset;
- $offset += $table[$tag]['length'];
- // check sum is not changed (so keep the following line commented)
- //$table[$tag]['checkSum'] = self::_getTTFtableChecksum($table[$tag]['data'], $table[$tag]['length']);
- } else {
- unset($table[$tag]);
- }
- }
- // add loca
- $table['loca']['data'] = $loca;
- $table['loca']['length'] = strlen($loca);
- $pad = 4 - ($table['loca']['length'] % 4);
- if ($pad != 4) {
- // the length of a table must be a multiple of four bytes
- $table['loca']['length'] += $pad;
- $table['loca']['data'] .= str_repeat("\x0", $pad);
- }
- $table['loca']['offset'] = $offset;
- $table['loca']['checkSum'] = self::_getTTFtableChecksum($table['loca']['data'], $table['loca']['length']);
- $offset += $table['loca']['length'];
- // add glyf
- $table['glyf']['data'] = $glyf;
- $table['glyf']['length'] = strlen($glyf);
- $pad = 4 - ($table['glyf']['length'] % 4);
- if ($pad != 4) {
- // the length of a table must be a multiple of four bytes
- $table['glyf']['length'] += $pad;
- $table['glyf']['data'] .= str_repeat("\x0", $pad);
- }
- $table['glyf']['offset'] = $offset;
- $table['glyf']['checkSum'] = self::_getTTFtableChecksum($table['glyf']['data'], $table['glyf']['length']);
- // rebuild font
- …
Large files files are truncated, but you can click here to view the full file