123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- <?php
- require_once __DIR__ . '/../MpdfException.php';
- require_once __DIR__ . '/ttfontsuni.php';
- class TTFontFile_Analysis extends TTFontFile
- {
- // Used to get font information from files in directory
- function extractCoreInfo($file, $TTCfontID = 0)
- {
- $this->filename = $file;
- $this->fh = fopen($file, 'rb');
- if (!$this->fh) {
- return ('ERROR - Can\'t open file ' . $file);
- }
- $this->_pos = 0;
- $this->charWidths = '';
- $this->glyphPos = array();
- $this->charToGlyph = array();
- $this->tables = array();
- $this->otables = array();
- $this->ascent = 0;
- $this->descent = 0;
- $this->numTTCFonts = 0;
- $this->TTCFonts = array();
- $this->version = $version = $this->read_ulong();
- $this->panose = array(); // mPDF 5.0
- if ($version == 0x4F54544F) {
- throw new MpdfException('ERROR - NOT ADDED as Postscript outlines are not supported - ' . $file);
- }
- if ($version == 0x74746366) {
- if ($TTCfontID > 0) {
- $this->version = $version = $this->read_ulong(); // TTC Header version now
- if (!in_array($version, array(0x00010000, 0x00020000))) {
- throw new MpdfException("ERROR - NOT ADDED as Error parsing TrueType Collection: version=" . $version . " - " . $file);
- }
- } else {
- throw new MpdfException("ERROR - Error parsing TrueType Collection - " . $file);
- }
- $this->numTTCFonts = $this->read_ulong();
- for ($i = 1; $i <= $this->numTTCFonts; $i++) {
- $this->TTCFonts[$i]['offset'] = $this->read_ulong();
- }
- $this->seek($this->TTCFonts[$TTCfontID]['offset']);
- $this->version = $version = $this->read_ulong(); // TTFont version again now
- $this->readTableDirectory(false);
- } else {
- if (!in_array($version, array(0x00010000, 0x74727565))) {
- throw new MpdfException("ERROR - NOT ADDED as Not a TrueType font: version=" . $version . " - " . $file);
- }
- $this->readTableDirectory(false);
- }
- /* Included for testing...
- $cmap_offset = $this->seek_table("cmap");
- $this->skip(2);
- $cmapTableCount = $this->read_ushort();
- $unicode_cmap_offset = 0;
- for ($i=0;$i<$cmapTableCount;$i++) {
- $x[$i]['platformId'] = $this->read_ushort();
- $x[$i]['encodingId'] = $this->read_ushort();
- $x[$i]['offset'] = $this->read_ulong();
- $save_pos = $this->_pos;
- $x[$i]['format'] = $this->get_ushort($cmap_offset + $x[$i]['offset'] );
- $this->seek($save_pos );
- }
- print_r($x); exit;
- */
- ///////////////////////////////////
- // name - Naming table
- ///////////////////////////////////
- /* Test purposes - displays table of names
- $name_offset = $this->seek_table("name");
- $format = $this->read_ushort();
- if ($format != 0 && $format != 1) // mPDF 5.3.73
- die("Unknown name table format ".$format);
- $numRecords = $this->read_ushort();
- $string_data_offset = $name_offset + $this->read_ushort();
- for ($i=0;$i<$numRecords; $i++) {
- $x[$i]['platformId'] = $this->read_ushort();
- $x[$i]['encodingId'] = $this->read_ushort();
- $x[$i]['languageId'] = $this->read_ushort();
- $x[$i]['nameId'] = $this->read_ushort();
- $x[$i]['length'] = $this->read_ushort();
- $x[$i]['offset'] = $this->read_ushort();
- $N = '';
- if ($x[$i]['platformId'] == 1 && $x[$i]['encodingId'] == 0 && $x[$i]['languageId'] == 0) { // Roman
- $opos = $this->_pos;
- $N = $this->get_chunk($string_data_offset + $x[$i]['offset'] , $x[$i]['length'] );
- $this->_pos = $opos;
- $this->seek($opos);
- }
- else { // Unicode
- $opos = $this->_pos;
- $this->seek($string_data_offset + $x[$i]['offset'] );
- $length = $x[$i]['length'] ;
- if ($length % 2 != 0)
- $length -= 1;
- // die("PostScript name is UTF-16BE string of odd length");
- $length /= 2;
- $N = '';
- while ($length > 0) {
- $char = $this->read_ushort();
- $N .= (chr($char));
- $length -= 1;
- }
- $this->_pos = $opos;
- $this->seek($opos);
- }
- $x[$i]['names'][$nameId] = $N;
- }
- print_r($x); exit;
- */
- $name_offset = $this->seek_table("name");
- $format = $this->read_ushort();
- if ($format != 0 && $format != 1) // mPDF 5.3.73
- throw new MpdfException("ERROR - NOT ADDED as Unknown name table format " . $format . " - " . $file);
- $numRecords = $this->read_ushort();
- $string_data_offset = $name_offset + $this->read_ushort();
- $names = array(1 => '', 2 => '', 3 => '', 4 => '', 6 => '');
- $K = array_keys($names);
- $nameCount = count($names);
- for ($i = 0; $i < $numRecords; $i++) {
- $platformId = $this->read_ushort();
- $encodingId = $this->read_ushort();
- $languageId = $this->read_ushort();
- $nameId = $this->read_ushort();
- $length = $this->read_ushort();
- $offset = $this->read_ushort();
- if (!in_array($nameId, $K))
- continue;
- $N = '';
- if ($platformId == 3 && $encodingId == 1 && $languageId == 0x409) { // Microsoft, Unicode, US English, PS Name
- $opos = $this->_pos;
- $this->seek($string_data_offset + $offset);
- if ($length % 2 != 0)
- $length += 1;
- $length /= 2;
- $N = '';
- while ($length > 0) {
- $char = $this->read_ushort();
- $N .= (chr($char));
- $length -= 1;
- }
- $this->_pos = $opos;
- $this->seek($opos);
- } else if ($platformId == 1 && $encodingId == 0 && $languageId == 0) { // Macintosh, Roman, English, PS Name
- $opos = $this->_pos;
- $N = $this->get_chunk($string_data_offset + $offset, $length);
- $this->_pos = $opos;
- $this->seek($opos);
- }
- if ($N && $names[$nameId] == '') {
- $names[$nameId] = $N;
- $nameCount -= 1;
- if ($nameCount == 0)
- break;
- }
- }
- if ($names[6])
- $psName = preg_replace('/ /', '-', $names[6]);
- else if ($names[4])
- $psName = preg_replace('/ /', '-', $names[4]);
- else if ($names[1])
- $psName = preg_replace('/ /', '-', $names[1]);
- else
- $psName = '';
- if (!$names[1] && !$psName)
- throw new MpdfException("ERROR - NOT ADDED as Could not find valid font name - " . $file);
- $this->name = $psName;
- if ($names[1]) {
- $this->familyName = $names[1];
- } else {
- $this->familyName = $psName;
- }
- if ($names[2]) {
- $this->styleName = $names[2];
- } else {
- $this->styleName = 'Regular';
- }
- ///////////////////////////////////
- // head - Font header table
- ///////////////////////////////////
- $this->seek_table("head");
- $ver_maj = $this->read_ushort();
- $ver_min = $this->read_ushort();
- if ($ver_maj != 1)
- throw new MpdfException('ERROR - NOT ADDED as Unknown head table version ' . $ver_maj . '.' . $ver_min . " - " . $file);
- $this->fontRevision = $this->read_ushort() . $this->read_ushort();
- $this->skip(4);
- $magic = $this->read_ulong();
- if ($magic != 0x5F0F3CF5)
- throw new MpdfException('ERROR - NOT ADDED as Invalid head table magic ' . $magic . " - " . $file);
- $this->skip(2);
- $this->unitsPerEm = $unitsPerEm = $this->read_ushort();
- $scale = 1000 / $unitsPerEm;
- $this->skip(24);
- $macStyle = $this->read_short();
- $this->skip(4);
- $indexLocFormat = $this->read_short();
- ///////////////////////////////////
- // OS/2 - OS/2 and Windows metrics table
- ///////////////////////////////////
- $sFamily = '';
- $panose = '';
- $fsSelection = '';
- if (isset($this->tables["OS/2"])) {
- $this->seek_table("OS/2");
- $this->skip(30);
- $sF = $this->read_short();
- $sFamily = ($sF >> 8);
- $this->_pos += 10; //PANOSE = 10 byte length
- $panose = fread($this->fh, 10);
- $this->panose = array();
- for ($p = 0; $p < strlen($panose); $p++) {
- $this->panose[] = ord($panose[$p]);
- }
- $this->skip(20);
- $fsSelection = $this->read_short();
- }
- ///////////////////////////////////
- // post - PostScript table
- ///////////////////////////////////
- $this->seek_table("post");
- $this->skip(4);
- $this->italicAngle = $this->read_short() + $this->read_ushort() / 65536.0;
- $this->skip(4);
- $isFixedPitch = $this->read_ulong();
- ///////////////////////////////////
- // cmap - Character to glyph index mapping table
- ///////////////////////////////////
- $cmap_offset = $this->seek_table("cmap");
- $this->skip(2);
- $cmapTableCount = $this->read_ushort();
- $unicode_cmap_offset = 0;
- for ($i = 0; $i < $cmapTableCount; $i++) {
- $platformID = $this->read_ushort();
- $encodingID = $this->read_ushort();
- $offset = $this->read_ulong();
- $save_pos = $this->_pos;
- if (($platformID == 3 && $encodingID == 1) || $platformID == 0) { // Microsoft, Unicode
- $format = $this->get_ushort($cmap_offset + $offset);
- if ($format == 4) {
- if (!$unicode_cmap_offset)
- $unicode_cmap_offset = $cmap_offset + $offset;
- }
- }
- else if ((($platformID == 3 && $encodingID == 10) || $platformID == 0)) { // Microsoft, Unicode Format 12 table HKCS
- $format = $this->get_ushort($cmap_offset + $offset);
- if ($format == 12) {
- $unicode_cmap_offset = $cmap_offset + $offset;
- break;
- }
- }
- $this->seek($save_pos);
- }
- if (!$unicode_cmap_offset)
- throw new MpdfException('ERROR - Font (' . $this->filename . ') NOT ADDED as it is not Unicode encoded, and cannot be used by mPDF');
- $rtl = false;
- $indic = false;
- $cjk = false;
- $sip = false;
- $smp = false;
- $pua = false;
- $puaag = false;
- $glyphToChar = array();
- $unAGlyphs = '';
- // Format 12 CMAP does characters above Unicode BMP i.e. some HKCS characters U+20000 and above
- if ($format == 12) {
- $this->seek($unicode_cmap_offset + 4);
- $length = $this->read_ulong();
- $limit = $unicode_cmap_offset + $length;
- $this->skip(4);
- $nGroups = $this->read_ulong();
- for ($i = 0; $i < $nGroups; $i++) {
- $startCharCode = $this->read_ulong();
- $endCharCode = $this->read_ulong();
- $startGlyphCode = $this->read_ulong();
- if (($endCharCode > 0x20000 && $endCharCode < 0x2A6DF) || ($endCharCode > 0x2F800 && $endCharCode < 0x2FA1F)) {
- $sip = true;
- }
- if ($endCharCode > 0x10000 && $endCharCode < 0x1FFFF) {
- $smp = true;
- }
- if (($endCharCode > 0x0590 && $endCharCode < 0x077F) || ($endCharCode > 0xFE70 && $endCharCode < 0xFEFF) || ($endCharCode > 0xFB50 && $endCharCode < 0xFDFF)) {
- $rtl = true;
- }
- if ($endCharCode > 0x0900 && $endCharCode < 0x0DFF) {
- $indic = true;
- }
- if ($endCharCode > 0xE000 && $endCharCode < 0xF8FF) {
- $pua = true;
- if ($endCharCode > 0xF500 && $endCharCode < 0xF7FF) {
- $puaag = true;
- }
- }
- if (($endCharCode > 0x2E80 && $endCharCode < 0x4DC0) || ($endCharCode > 0x4E00 && $endCharCode < 0xA4CF) || ($endCharCode > 0xAC00 && $endCharCode < 0xD7AF) || ($endCharCode > 0xF900 && $endCharCode < 0xFAFF) || ($endCharCode > 0xFE30 && $endCharCode < 0xFE4F)) {
- $cjk = true;
- }
- $offset = 0;
- // Get each glyphToChar - only point if going to analyse un-mapped Arabic Glyphs
- if (isset($this->tables['post'])) {
- for ($unichar = $startCharCode; $unichar <= $endCharCode; $unichar++) {
- $glyph = $startGlyphCode + $offset;
- $offset++;
- $glyphToChar[$glyph][] = $unichar;
- }
- }
- }
- } else { // Format 4 CMap
- $this->seek($unicode_cmap_offset + 2);
- $length = $this->read_ushort();
- $limit = $unicode_cmap_offset + $length;
- $this->skip(2);
- $segCount = $this->read_ushort() / 2;
- $this->skip(6);
- $endCount = array();
- for ($i = 0; $i < $segCount; $i++) {
- $endCount[] = $this->read_ushort();
- }
- $this->skip(2);
- $startCount = array();
- for ($i = 0; $i < $segCount; $i++) {
- $startCount[] = $this->read_ushort();
- }
- $idDelta = array();
- for ($i = 0; $i < $segCount; $i++) {
- $idDelta[] = $this->read_short();
- }
- $idRangeOffset_start = $this->_pos;
- $idRangeOffset = array();
- for ($i = 0; $i < $segCount; $i++) {
- $idRangeOffset[] = $this->read_ushort();
- }
- for ($n = 0; $n < $segCount; $n++) {
- if (($endCount[$n] > 0x0590 && $endCount[$n] < 0x077F) || ($endCount[$n] > 0xFE70 && $endCount[$n] < 0xFEFF) || ($endCount[$n] > 0xFB50 && $endCount[$n] < 0xFDFF)) {
- $rtl = true;
- }
- if ($endCount[$n] > 0x0900 && $endCount[$n] < 0x0DFF) {
- $indic = true;
- }
- if (($endCount[$n] > 0x2E80 && $endCount[$n] < 0x4DC0) || ($endCount[$n] > 0x4E00 && $endCount[$n] < 0xA4CF) || ($endCount[$n] > 0xAC00 && $endCount[$n] < 0xD7AF) || ($endCount[$n] > 0xF900 && $endCount[$n] < 0xFAFF) || ($endCount[$n] > 0xFE30 && $endCount[$n] < 0xFE4F)) {
- $cjk = true;
- }
- if ($endCount[$n] > 0xE000 && $endCount[$n] < 0xF8FF) {
- $pua = true;
- if ($endCount[$n] > 0xF500 && $endCount[$n] < 0xF7FF) {
- $puaag = true;
- }
- }
- // Get each glyphToChar - only point if going to analyse un-mapped Arabic Glyphs
- if (isset($this->tables['post'])) {
- $endpoint = ($endCount[$n] + 1);
- for ($unichar = $startCount[$n]; $unichar < $endpoint; $unichar++) {
- if ($idRangeOffset[$n] == 0)
- $glyph = ($unichar + $idDelta[$n]) & 0xFFFF;
- else {
- $offset = ($unichar - $startCount[$n]) * 2 + $idRangeOffset[$n];
- $offset = $idRangeOffset_start + 2 * $n + $offset;
- if ($offset >= $limit)
- $glyph = 0;
- else {
- $glyph = $this->get_ushort($offset);
- if ($glyph != 0)
- $glyph = ($glyph + $idDelta[$n]) & 0xFFFF;
- }
- }
- $glyphToChar[$glyph][] = $unichar;
- }
- }
- }
- }
- $bold = false;
- $italic = false;
- $ftype = '';
- if ($macStyle & (1 << 0)) {
- $bold = true;
- } // bit 0 bold
- else if ($fsSelection & (1 << 5)) {
- $bold = true;
- } // 5 BOLD Characters are emboldened
- if ($macStyle & (1 << 1)) {
- $italic = true;
- } // bit 1 italic
- else if ($fsSelection & (1 << 0)) {
- $italic = true;
- } // 0 ITALIC Font contains Italic characters, otherwise they are upright
- else if ($this->italicAngle <> 0) {
- $italic = true;
- }
- if ($isFixedPitch) {
- $ftype = 'mono';
- } else if ($sFamily > 0 && $sFamily < 8) {
- $ftype = 'serif';
- } else if ($sFamily == 8) {
- $ftype = 'sans';
- } else if ($sFamily == 10) {
- $ftype = 'cursive';
- }
- // Use PANOSE
- if ($panose) {
- $bFamilyType = ord($panose[0]);
- if ($bFamilyType == 2) {
- $bSerifStyle = ord($panose[1]);
- if (!$ftype) {
- if ($bSerifStyle > 1 && $bSerifStyle < 11) {
- $ftype = 'serif';
- } else if ($bSerifStyle > 10) {
- $ftype = 'sans';
- }
- }
- $bProportion = ord($panose[3]);
- if ($bProportion == 9 || $bProportion == 1) {
- $ftype = 'mono';
- } // ==1 i.e. No Fit needed for OCR-a and -b
- } else if ($bFamilyType == 3) {
- $ftype = 'cursive';
- }
- }
- fclose($this->fh);
- return array($this->familyName, $bold, $italic, $ftype, $TTCfontID, $rtl, $indic, $cjk, $sip, $smp, $puaag, $pua, $unAGlyphs);
- }
- }
|