/**
  * 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;
 }
 static function create_index(&$fh, $cff_offset, $charstringtype)
 {
     $index = new CFFFontDictIndex();
     $index->cff_offset = $cff_offset;
     $index->setup($fh);
     $index->CharStringType = $charstringtype;
     return $index;
 }