/** * 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] + $subHeaders[$k]['idDelta']) % 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'] = ''; $fmetric['cbbox'] = ''; 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 (!empty($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; }