private static function get_TTF_glyph_for_index($font, $char, $index, $matrix = array(0, 0, 1, 0, 0, 1))
 {
     // echo "glyph index for $char: $index\n";
     // step zero: does this char exist?
     if ($index == GlyphFetcher::$NOTSET) {
         $index = $font->get_index($char);
     }
     if ($index === false) {
         return false;
     }
     // there was no cache yet. Perform the real lookup.
     require_once OTTTFONT::$TTFlocation . "ttfglyphdata.php";
     $font->log("This is a TTF font. consulting 'index to location' table.");
     $fh = $font->open();
     $head =& $font->getOTFTableLoader()->get_head_table($font);
     $indexToLocFormat = $head->indexToLocFormat;
     // tells us whether the 'loca' table uses USHORT or ULONG data fields
     // "Index to Location" table, which will tell us where in the "glyf" table we can find the glyph
     $loca =& $font->tables['loca'];
     // "Glyph Data" table, which should give us the glyph's actual outline data (if there is any)
     $glyf =& $font->tables['glyf'];
     // navigate to position $index
     $glyphpointer = 0;
     $next = 0;
     if ($indexToLocFormat == 0) {
         // USHORT entries = seek based on 2 byte jumps (since a USHORT is 16 bit)
         $offset = $loca->offset + $index * 2;
         rewind($fh);
         fseek($fh, $offset);
         // pointer values are stored as half of what they really are in the short table
         $glyphpointer = 2 * FileRead::read_USHORT($fh);
         $next = 2 * FileRead::read_USHORT($fh);
     } elseif ($indexToLocFormat == 1) {
         // ULONG entries = seek based on 4 byte jumps (since a ULONG is 32 bit)
         $step = $index * 4;
         $offset = $loca->offset + $step;
         rewind($fh);
         fseek($fh, $offset);
         // pointer values are stored normally in the long table
         $glyphpointer = FileRead::read_ULONG($fh);
         $next = FileRead::read_ULONG($fh);
     }
     // if the two pointer values are the same, the glyph has no outline
     // (there are a number of invisible characters, mostly spacers).
     $empty = false;
     if ($glyphpointer == $next) {
         $empty = true;
         $font->log("glyph has no outline data in " . $font->fontfilelocation);
     }
     $data = new TTFGlyphData();
     // if there is no outline data, we need to fill in the zero-valued metrics
     if ($empty) {
         $data->xMin = 0;
         $data->yMin = 0;
         $data->xMax = 0;
         $data->yMax = 0;
         $data->height = 0;
         require_once OTTTFont::$GDlocation . "glyphrules.php";
         $data->glyphdata = new Type2GlyphRules();
     } else {
         rewind($fh);
         $offset = $glyf->offset + $glyphpointer;
         $fs = filesize($font->fontfilelocation);
         if ($offset > $fs) {
             echo "ERROR: tried to move the pointer " . ($offset - $fs) . " bytes beyond the end of file!\n";
             return false;
         }
         fseek($fh, $offset);
         // read glyph data (see http://www.microsoft.com/typography/otspec/glyf.htm)
         $data->unitsPerEm = $head->unitsPerEm;
         $numberOfContours = FileRead::read_SHORT($fh);
         // If the number of contours is greater than zero, this is a single glyph;
         // if negative, this is a composite glyph.
         $xMin = FileRead::read_SHORT($fh);
         // Minimum x for coordinate data.
         $yMin = FileRead::read_SHORT($fh);
         // Minimum y for coordinate data.
         $xMax = FileRead::read_SHORT($fh);
         // Maximum x for coordinate data.
         $yMax = FileRead::read_SHORT($fh);
         // Maximum y for coordinate data.
         $width = $xMax - $xMin;
         $height = $yMax - $yMin;
         $data->numberOfContours = $numberOfContours;
         $data->xMin = $xMin;
         $data->yMin = $yMin;
         $data->xMax = $xMax;
         $data->yMax = $yMax;
         $data->width = $width;
         $data->height = $height;
         $font->log("glyph has {$numberOfContours} contours ({$width}x{$height}), x/y min/max: ({$xMin},{$yMin},{$xMax},{$yMax}) in " . $font->fontfilelocation);
         // simple glyph
         if ($numberOfContours >= 0) {
             // first things first: if there is only one contour point, and it's really small, it's probably not actually a glyph
             if ($numberOfContours == 1 && $width < 120 && $height < 120) {
                 return false;
             }
             // read the glyph data
             $endPtsOfContours = array();
             for ($i = 0; $i < $numberOfContours; $i++) {
                 $endPtsOfContours[] = FileRead::read_USHORT($fh);
             }
             $data->endPtsOfContours = $endPtsOfContours;
             // it's much easier to also have access to contour startpoints, rather than just endpoints
             $startPtsOfContours = array(0);
             for ($i = 0; $i < count($endPtsOfContours) - 1; $i++) {
                 $startPtsOfContours[] = $endPtsOfContours[$i] + 1;
             }
             $data->startPtsOfContours = $startPtsOfContours;
             // get instructions
             $instructionLength = FileRead::read_USHORT($fh);
             $data->instructionLength = $instructionLength;
             $instructions = array();
             for ($i = 0; $i < $instructionLength; $i++) {
                 $instructions[] = FileRead::read_BYTE($fh);
             }
             $data->instructions = $instructions;
             // get the coordinate information
             $count = $endPtsOfContours[$numberOfContours - 1] + 1;
             // get all the coordinate flags (code based on Apache's batik java code)
             $data->flags = array();
             for ($flag = 0; $flag < $count; $flag++) {
                 $data->flags[$flag] = FileRead::read_BYTE($fh);
                 if ($data->flag_repeats($flag)) {
                     $repeats = FileRead::read_BYTE($fh);
                     for ($i = 1; $i <= $repeats; $i++) {
                         $data->flags[$flag + $i] = $data->flags[$flag];
                     }
                     $flag += $repeats;
                 }
             }
             // x-coordinates (relative, code based on Apache's batik java code)
             $xCoordinates = array();
             for ($i = 0; $i < $count; $i++) {
                 $x = 0;
                 $xShort = $data->x_is_byte($i);
                 $xDual = $data->x_dual_set($i);
                 // If x-Short Vector is set, xDual describes the sign of the value, with 1 equalling positive and 0 negative.
                 if ($xShort) {
                     if ($xDual) {
                         $x += FileRead::read_BYTE($fh);
                     } else {
                         $x -= FileRead::read_BYTE($fh);
                     }
                 } elseif (!$xDual) {
                     $x += FileRead::read_SHORT($fh);
                 }
                 // correct for offset
                 $xCoordinates[$i] = $x - $xMin;
             }
             // y-coordinates (relative, code based on Apache's batik java code)
             $yCoordinates = array();
             for ($i = 0; $i < $count; $i++) {
                 $y = 0;
                 $yShort = $data->y_is_byte($i);
                 $yDual = $data->y_dual_set($i);
                 // If y-Short Vector is set, yDual describes the sign of the value, with 1 equalling positive and 0 negative.
                 if ($yShort) {
                     if ($yDual) {
                         $y += FileRead::read_BYTE($fh);
                     } else {
                         $y -= FileRead::read_BYTE($fh);
                     }
                 } elseif (!$yDual) {
                     $y += FileRead::read_SHORT($fh);
                 }
                 // correct for offset and flip y coordinate
                 $yCoordinates[$i] = $y - $yMin;
             }
             // bind data and form a glyphrules object
             $data->xCoordinates = $xCoordinates;
             $data->yCoordinates = $yCoordinates;
             $data->formGlyphRules($matrix);
         } else {
             $ARG_1_AND_2_ARE_WORDS = 1;
             // If this is set, the arguments are words; otherwise, they are bytes.
             $ARGS_ARE_XY_VALUES = 2;
             // If this is set, the arguments are xy values; otherwise, they are points.
             $ROUND_XY_TO_GRID = 4;
             // For the xy values if the preceding is true.
             $WE_HAVE_A_SCALE = 8;
             // This indicates that there is a simple scale for the component. Otherwise, scale = 1.0.
             $MORE_COMPONENTS = 32;
             // Indicates at least one more glyph after this one.
             $WE_HAVE_AN_X_AND_Y_SCALE = 64;
             // The x direction will use a different scale from the y direction.
             $WE_HAVE_A_TWO_BY_TWO = 128;
             // There is a 2 by 2 transformation that will be used to scale the component.
             $WE_HAVE_INSTRUCTIONS = 256;
             // Following the last component are instructions for the composite character.
             $USE_MY_METRICS = 512;
             // If set, this forces the aw and lsb (and rsb) for the composite to be equal to those from this original glyph.
             $old_xoffset = 0;
             $old_yoffset = 0;
             $old_xdiff = 0;
             $old_ydiff = 0;
             $last = false;
             $current = false;
             $flags = "";
             do {
                 $flags = FileRead::read_USHORT($fh);
                 $glyphIndex = FileRead::read_USHORT($fh);
                 $arg1 = "";
                 $arg2 = "";
                 // read in argument 1 and 2
                 if (masks($flags, $ARG_1_AND_2_ARE_WORDS)) {
                     $arg1 = FileRead::read_SHORT($fh);
                     $arg2 = FileRead::read_SHORT($fh);
                 } else {
                     $arg1 = FileRead::read_BYTE($fh);
                     $arg2 = FileRead::read_SBYTE($fh);
                 }
                 $xscale = 1;
                 $scale01 = 0;
                 $scale10 = 0;
                 $yscale = 1;
                 if (masks($flags, $WE_HAVE_A_SCALE)) {
                     $xscale = FileRead::read_F2DOT14($fh);
                     $yscale = $xscale;
                 } elseif (masks($flags, $WE_HAVE_AN_X_AND_Y_SCALE)) {
                     $xscale = FileRead::read_F2DOT14($fh);
                     $yscale = FileRead::read_F2DOT14($fh);
                 } elseif (masks($flags, $WE_HAVE_A_TWO_BY_TWO)) {
                     $xscale = FileRead::read_F2DOT14($fh);
                     $scale01 = FileRead::read_F2DOT14($fh);
                     $scale10 = FileRead::read_F2DOT14($fh);
                     $yscale = FileRead::read_F2DOT14($fh);
                 }
                 $xoffset = $arg1;
                 $yoffset = $arg2;
                 // Merge data: if not masked, the arguments indicate how the glyphs link up:
                 // 	- arg1 is the connecting point in the "current" glyph
                 // 	- arg2 is the connecting point in the "next" glyph
                 // We can use these to derive the x/y offset for the linked glyph
                 if (!masks($flags, $ARGS_ARE_XY_VALUES)) {
                     // TODO: this has not yet been implemented
                     trigger_error("point matching not yet implemented\n");
                 }
                 // push the administrative values through.
                 $last = $current;
                 $matrix = array($xoffset, $yoffset, $xscale, $scale01, $scale10, $yscale);
                 $mark = ftell($fh);
                 $current = GlyphFetcher::get_TTF_glyph_for_index($font, $char, $glyphIndex, $matrix);
                 $old_xoffset = $xoffset;
                 $old_yoffset = $yoffset;
                 rewind($fh);
                 fseek($fh, $mark);
                 $data->merge($current);
             } while (masks($flags, $MORE_COMPONENTS));
             // if there are instructions, we read them in but don't do anything with them,
             // because this parser does not process instructions.
             if (masks($flags, $WE_HAVE_INSTRUCTIONS)) {
                 $numInstr = FileRead::read_USHORT($fh);
                 $bytes = array();
                 for ($n = 0; $n < $numInstr; $n++) {
                     $bytes[] = FileRead::read_BYTE($fh);
                 }
             }
         }
     }
     fclose($fh);
     return $data;
 }
 /**
  * Get the glyph data for a particular character
  */
 function get_glyph($char)
 {
     if (isset($this->glyphcache[$char])) {
         return $this->glyphcache[$char];
     }
     require_once OTTTFont::$GDlocation . "glyphfetcher.php";
     // get index. This will also implicitly cache the glyph data,
     // so subsequent get_glyph calls will retrieved the cached
     // data rather than searching the font again.
     $index = $this->get_index($char);
     if ($index === false) {
         return false;
     }
     $data = GlyphFetcher::get_glyph($this, $char, $index);
     $this->cache_glyph($char, $data);
     return $data;
 }