/** * Get the glyph data from an OTF font with CFF content. * This uses a hell of a lot of tables, in a marvelously interwoven manner. */ private static function get_CFF_glyph($font, $char, $index) { $data = false; // step zero: does this char exist? if ($index == GlyphFetcher::$NOTSET) { $index = $font->get_index($char); } // how about now? if ($index === false) { // nope, no index. This character is not supported by this font. return false; } // okay we're still not there, but we have an index. Load up the CFF parser and get to work require_once OTTTFont::$CFFlocation . "CFFBlockClasses.php"; // navigate to the CFF table $fh = $font->open(); $cff =& $font->tables['CFF ']; $offset = $cff->offset; fseek($fh, $offset); // read header $version_major = FileRead::read_BYTE($fh); $version_minor = FileRead::read_BYTE($fh); $hdrSize = FileRead::read_BYTE($fh); $offSize = FileRead::read_BYTE($fh); // read in the name index rewind($fh); fseek($fh, $offset + $hdrSize); $nameindex = CFFIndex::create_index($fh); // echo "Name INDEX:\n" . $nameindex->toString() . "\n---\n"; // read in the top dict index rewind($fh); fseek($fh, $nameindex->nexttable); $topdictindex = CFFTopDictIndex::create_index($fh); // echo "Top DICT INDEX:\n" . $topdictindex->toString() . "\n---\n"; // read in the string index (FIXME: resolving strings is rather cumbersome, and not working a.t.m.) rewind($fh); fseek($fh, $topdictindex->nexttable); $stringindex = CFFStringIndex::create_index($fh); // echo "String INDEX:\n" . $stringindex->toString() . "\n---\n"; // read in the global subroutine index. global subroutines can be referenced by any glyph rewind($fh); fseek($fh, $stringindex->nexttable); $globalsubindex = CFFIndex::create_index($fh); // echo "Global Subr INDEX:\n" . $globalsubindex->toString() . "\n---\n"; // move to the next table rewind($fh); fseek($fh, $globalsubindex->nexttable); // Is there a charset that we might find our character in? if ($topdictindex->charset != 0) { // find the fontdict that we need to look at in order to find this character's // description. we do this by accessing the "FDarray" offset if ($topdictindex->is_CIDFont()) { $mark = ftell($fh); // is there a cache entry for the "charstringindex" that contains the subroutines used by // the fontdict datastructure that this character is found in? $charstringindex = ""; if (isset(GlyphFetcher::$charstringindices[$index])) { $charstringindex = GlyphFetcher::$charstringindices[$index]; } else { rewind($fh); fseek($fh, $cff->offset + $topdictindex->CharStrings); $charstringsindex = CFFIndex::create_index($fh); $charstringindices[$index] = $charstringindex; } // echo "Char Strings INDEX:\n" . $charstringsindex->toString() . "\n---\n"; rewind($fh); fseek($fh, $cff->offset + $topdictindex->FDArray); $fontdictindex = CFFFontDictIndex::create_index($fh, $cff->offset, $topdictindex->CharstringType); // echo "Font DICT INDEX:\n" . $fontdictindex->toString() . "\n---\n"; // resolve the FDSelect field, so that we can figure out which fontdict to look in // (which will also tell us where we can find subroutines used only in this fontdict) rewind($fh); fseek($fh, $cff->offset + $topdictindex->FDSelect); $fdselectindex = new FDSelectIndex($fh); $fd = $fdselectindex->get_FD($index); // now that we have the correct fontdict, we can properly parse the type2 charstring for this glyph $charstringdata = $charstringsindex->get_data($fh, $index); $fontdict = $fontdictindex->get_font_dict($fd); // the following line is a debug comment, but a fairly important one. glyphs have a default width, // which can be overriden by an instruction at the start of the glyph's outline charstring. In order // for this to yield a sensible metric, the nominal width has to have been correctly computable. // //echo "nominal width: " . $fontdict->privatedict->nominalWidthX . ", default width: " . $fontdict->privatedict->defaultWidthX . "\n"; // grab the data, and make sure the width metric is corrected. $data = new Type2GlyphData($fh, $charstringdata, $fontdict, $globalsubindex); if ($data->differenceWithNominalWidthX != 0) { //echo "width = " . $fontdict->privatedict->nominalWidthX . " + " .$data->differenceWithNominalWidthX . "\n"; $data->width = $fontdict->privatedict->nominalWidthX + $data->differenceWithNominalWidthX; } else { $data->width = $fontdict->privatedict->defaultWidthX; } } else { // for non-CID fonts, the top dict acts as font dict. $topdictindex->load_private_dict($fh, $offset, $topdictindex->CharstringType); // get the charstring data block rewind($fh); fseek($fh, $cff->offset + $topdictindex->CharStrings); $charstringsindex = CFFIndex::create_index($fh); //echo "CHARSTRING INDEX:\n" . $charstringsindex->toString() . "\n---\n"; $charstringdata = $charstringsindex->get_data($fh, $index); // and then resolve the Type2 charstring to glyph outline data $data = new Type2GlyphData($fh, $charstringdata, $topdictindex, $globalsubindex); } } else { die("ERROR: no charset information could be found in this font.\n"); } // construct a new Glyph object, cache it, and then return it $ret = new Glyph(); $ret->hash = $data->glyphdata->hash; $ret->glyphrules = $data->glyphdata; $ret->width = $data->width; $ret->computeBounds(); $ret->bounds["width"] = $ret->bounds["maxx"] - $ret->bounds["minx"]; $ret->bounds["height"] = $ret->bounds["maxy"] - $ret->bounds["miny"]; // FIXME: this might not be the correct way to determine LSB $ret->lsb = $ret->bounds["minx"]; $ret->rsb = $data->width - ($ret->bounds["maxx"] - $ret->bounds["minx"] + $ret->lsb); // set the height based on the bounding box. // FIXME: this should really be the vertical metric value instead. $ret->height = $ret->bounds["maxy"] - $ret->bounds["miny"]; return $ret; }
function toString() { $ret = parent::toString(); $ret .= "FONT DICT BLOCKS:\n"; foreach ($this->fontdicts as $fontdict) { $ret .= $fontdict->toString(); } return $ret; }