ttgload.c   [plain text]


/*******************************************************************
 *
 *  ttgload.c                                                   1.0
 *
 *    TrueType Glyph Loader.
 *
 *  Copyright 1996-1999 by
 *  David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 *  This file is part of the FreeType project, and may only be used
 *  modified and distributed under the terms of the FreeType project
 *  license, LICENSE.TXT.  By continuing to use, modify, or distribute
 *  this file you indicate that you have read the license and
 *  understand and accept it fully.
 *
 ******************************************************************/

#include "tttypes.h"
#include "ttdebug.h"
#include "ttcalc.h"
#include "ttfile.h"

#include "tttables.h"
#include "ttobjs.h"
#include "ttgload.h"

#include "ttmemory.h"
#include "tttags.h"
#include "ttload.h"

/* required by the tracing mode */
#undef  TT_COMPONENT
#define TT_COMPONENT  trace_gload


/* composite font flags */

#define ARGS_ARE_WORDS       0x001
#define ARGS_ARE_XY_VALUES   0x002
#define ROUND_XY_TO_GRID     0x004
#define WE_HAVE_A_SCALE      0x008
/* reserved                  0x010 */
#define MORE_COMPONENTS      0x020
#define WE_HAVE_AN_XY_SCALE  0x040
#define WE_HAVE_A_2X2        0x080
#define WE_HAVE_INSTR        0x100
#define USE_MY_METRICS       0x200


/********************************************************/
/* Return horizontal or vertical metrics in font units  */
/* for a given glyph.  The metrics are the left side    */
/* bearing (resp. top side bearing) and advance width   */
/* (resp. advance height).                              */
/*                                                      */
/* This function will much probably move to another     */
/* component in the short future, but I haven't decided */
/* which yet...                                         */

  LOCAL_FUNC
  void  TT_Get_Metrics( TT_Horizontal_Header*  header,
                        UShort                 index,
                        Short*                 bearing,
                        UShort*                advance )
  {
    PLongMetrics  longs_m;

    UShort  k = header->number_Of_HMetrics;


    if ( index < k )
    {
      longs_m = (PLongMetrics)header->long_metrics + index;
      *bearing = longs_m->bearing;
      *advance = longs_m->advance;
    }
    else
    {
      *bearing = ((PShortMetrics)header->short_metrics)[index - k];
      *advance = ((PLongMetrics)header->long_metrics)[k - 1].advance;
    }
  }


/********************************************************/
/* Return horizontal metrics in font units for a given  */
/* glyph.  If `check' is true, take care of mono-spaced */
/* fonts by returning the advance width max.            */

  static void Get_HMetrics( PFace    face,
                            UShort   index,
                            Bool     check,
                            Short*   lsb,
                            UShort*  aw )
  {
    TT_Get_Metrics( &face->horizontalHeader, index, lsb, aw );

    if ( check && face->postscript.isFixedPitch )
      *aw = face->horizontalHeader.advance_Width_Max;
  }


/********************************************************/
/* Return advance width table for a given pixel size    */
/* if it is found in the font's `hdmx' table (if any).  */

  static PByte  Get_Advance_Widths( PFace   face,
                                    UShort  ppem )
  {
    UShort  n;


    for ( n = 0; n < face->hdmx.num_records; n++ )
      if ( face->hdmx.records[n].ppem == ppem )
        return face->hdmx.records[n].widths;

    return NULL;
  }


/********************************************************/
/* Copy current glyph into original one.                */

#define cur_to_org( n, zone ) \
          MEM_Copy( (zone)->org, (zone)->cur, (n) * sizeof ( TT_Vector ) )

/********************************************************/
/* copy original glyph into current one                 */

#define org_to_cur( n, zone ) \
          MEM_Copy( (zone)->cur, (zone)->org, (n) * sizeof ( TT_Vector ) )

/********************************************************/
/* translate an array of coordinates                    */

  static void  translate_array( UShort     n,
                                TT_Vector* coords,
                                TT_Pos     delta_x,
                                TT_Pos     delta_y )
  {
    UShort  k;


    if ( delta_x )
      for ( k = 0; k < n; k++ )
        coords[k].x += delta_x;

    if ( delta_y )
      for ( k = 0; k < n; k++ )
        coords[k].y += delta_y;
  }


/********************************************************/
/* mount one zone on top of another                     */

  static void  mount_zone( PGlyph_Zone  source,
                           PGlyph_Zone  target )
  {
    UShort  np;
    Short   nc;

    np = source->n_points;
    nc = source->n_contours;

    target->org   = source->org + np;
    target->cur   = source->cur + np;
    target->touch = source->touch + np;

    target->contours = source->contours + nc;

    target->n_points   = 0;
    target->n_contours = 0;
  }


/*******************************************************************
 *
 *  Function:  Load_Simple_Glyph
 *
 ******************************************************************/

  static TT_Error  Load_Simple_Glyph( PExecution_Context  exec,
                                      TT_Stream           input,
                                      Short               n_contours,
                                      Short               left_contours,
                                      UShort              left_points,
                                      UShort              load_flags,
                                      PSubglyph_Record    subg )
  {
    DEFINE_LOAD_LOCALS( input );

    PGlyph_Zone  pts;
    Short        k;
    UShort       j;
    UShort       n_points, n_ins;
    PFace        face;
    Byte*        flag;
    TT_Vector*   vec;
    TT_F26Dot6   x, y;


    face = exec->face;

    /* simple check */
    if ( n_contours > left_contours )
    {
      PTRACE0(( "ERROR: Glyph index %ld has %d contours > left %d\n",
                   subg->index, n_contours, left_contours ));
      return TT_Err_Too_Many_Contours;
    }


    /* preparing the execution context */
    mount_zone( &subg->zone, &exec->pts );

    /* reading the contours endpoints */
    if ( ACCESS_Frame( (n_contours + 1) * 2L ) )
      return error;

    PTRACE4(( " Contour endpoints:" ));

    for ( k = 0; k < n_contours; k++ )
    {
      exec->pts.contours[k] = GET_UShort();
      PTRACE4(( " %d", exec->pts.contours[k] ));
    }
    PTRACE4(( "\n" ));

    if ( n_contours > 0 )
      n_points = exec->pts.contours[n_contours - 1] + 1;
    else
      n_points = 0;

    n_ins = GET_UShort();

    FORGET_Frame();

    if ( n_points > left_points )
    {
      PTRACE0(( "ERROR: Too many points in glyph %ld\n", subg->index ));
      return TT_Err_Too_Many_Points;
    }

    /* loading instructions */

    PTRACE4(( " Instructions size: %d\n", n_ins ));

    if ( n_ins > face->maxProfile.maxSizeOfInstructions )
    {
      PTRACE0(( "ERROR: Too many instructions!\n" ));
      return TT_Err_Too_Many_Ins;
    }

    if ( FILE_Read( exec->glyphIns, n_ins ) )
      return error;

    if ( (error = Set_CodeRange( exec,
                                 TT_CodeRange_Glyph,
                                 exec->glyphIns,
                                 n_ins )) != TT_Err_Ok )
      return error;


    /* read the flags */

    if ( CHECK_ACCESS_Frame( n_points * 5L ) )
      return error;

    j    = 0;
    flag = exec->pts.touch;

    while ( j < n_points )
    {
      Byte  c, cnt;

      flag[j] = c = GET_Byte();
      j++;

      if ( c & 8 )
      {
        cnt = GET_Byte();
        while( cnt > 0 )
        {
          flag[j++] = c;
          cnt--;
        }
      }
    }

    /* read the X */

    x    = 0;
    vec  = exec->pts.org;

    for ( j = 0; j < n_points; j++ )
    {
      if ( flag[j] & 2 )
      {
        if ( flag[j] & 16 )
          x += GET_Byte();
        else
          x -= GET_Byte();
      }
      else
      {
        if ( (flag[j] & 16) == 0 )
          x += GET_Short();
      }

      vec[j].x = x;
    }


   /* read the Y */

    y    = 0;

    for ( j = 0; j < n_points; j++ )
    {
      if ( flag[j] & 4 )
      {
        if ( flag[j] & 32 )
          y += GET_Byte();
        else
          y -= GET_Byte();
      }
      else
      {
        if ( (flag[j] & 32) == 0 )
          y += GET_Short();
      }

      vec[j].y = y;
    }

    FORGET_Frame();

    /* Now add the two shadow points at n and n + 1.    */
    /* We need the left side bearing and advance width. */

    /* pp1 = xMin - lsb */
    vec[n_points].x = subg->metrics.bbox.xMin - subg->metrics.horiBearingX;
    vec[n_points].y = 0;

    /* pp2 = pp1 + aw */
    vec[n_points+1].x = vec[n_points].x + subg->metrics.horiAdvance;
    vec[n_points+1].y = 0;

    /* clear the touch flags */

    for ( j = 0; j < n_points; j++ )
      exec->pts.touch[j] &= TT_Flag_On_Curve;

    exec->pts.touch[n_points    ] = 0;
    exec->pts.touch[n_points + 1] = 0;

    /* Note that we return two more points that are not */
    /* part of the glyph outline.                       */

    n_points += 2;

    /* now eventually scale and hint the glyph */

    pts = &exec->pts;
    pts->n_points   = n_points;
    pts->n_contours = n_contours;

    if ( (load_flags & TTLOAD_SCALE_GLYPH) == 0 )
    {
      /* no scaling, just copy the orig arrays into the cur ones */
      org_to_cur( n_points, pts );
    }
    else
    {
     /* first scale the glyph points */

      for ( j = 0; j < n_points; j++ )
      {
        pts->org[j].x = Scale_X( &exec->metrics, pts->org[j].x );
        pts->org[j].y = Scale_Y( &exec->metrics, pts->org[j].y );
      }

      /* if hinting, round pp1, and shift the glyph accordingly */
      if ( subg->is_hinted )
      {
        x = pts->org[n_points - 2].x;
        x = ((x+32) & -64) - x;
        translate_array( n_points, pts->org, x, 0 );

        org_to_cur( n_points, pts );

        pts->cur[n_points - 1].x = (pts->cur[n_points - 1].x + 32) & -64;

        /* now consider hinting */
        if ( n_ins > 0 )
        {
          exec->is_composite     = FALSE;
          exec->pedantic_hinting = load_flags & TTLOAD_PEDANTIC;

          error = Context_Run( exec, FALSE );
          if (error && exec->pedantic_hinting)
            return error;
        }
      }
      else
        org_to_cur( n_points, pts );
    }

    /* save glyph phantom points */
    if (!subg->preserve_pps)
    {
      subg->pp1 = pts->cur[n_points - 2];
      subg->pp2 = pts->cur[n_points - 1];
    }

    return TT_Err_Ok;
  }


/*******************************************************************
 *
 *  Function    :  Load_Composite_End
 *
 ******************************************************************/

  static
  TT_Error  Load_Composite_End( UShort              n_points,
                                Short               n_contours,
                                PExecution_Context  exec,
                                PSubglyph_Record    subg,
                                UShort              load_flags,
                                TT_Stream           input )
  {
    DEFINE_LOAD_LOCALS( input );

    UShort       k, n_ins;
    PGlyph_Zone  pts;


    if ( subg->is_hinted                    &&
         subg->element_flag & WE_HAVE_INSTR )
    {
      if ( ACCESS_Frame( 2L ) )
        return error;

      n_ins = GET_UShort();     /* read size of instructions */
      FORGET_Frame();

      PTRACE4(( " Instructions size: %d\n", n_ins ));

      if ( n_ins > exec->face->maxProfile.maxSizeOfInstructions )
      {
        PTRACE0(( "ERROR: Too many instructions in composite glyph %ld\n",
                  subg->index ));
        return TT_Err_Too_Many_Ins;
      }

      if ( FILE_Read( exec->glyphIns, n_ins ) )
        return error;

      error = Set_CodeRange( exec,
                             TT_CodeRange_Glyph,
                             exec->glyphIns,
                             n_ins );

      if ( error )
        return error;
    }
    else
      n_ins = 0;


    /* prepare the execution context */
    n_points += 2;
    exec->pts = subg->zone;
    pts       = &exec->pts;

    pts->n_points   = n_points;
    pts->n_contours = n_contours;

    /* add phantom points */
    pts->cur[n_points - 2] = subg->pp1;
    pts->cur[n_points - 1] = subg->pp2;

    pts->touch[n_points - 1] = 0;
    pts->touch[n_points - 2] = 0;

    /* if hinting, round the phantom points */
    if ( subg->is_hinted )
    {
      pts->cur[n_points - 2].x = (subg->pp1.x + 32) & -64;
      pts->cur[n_points - 1].x = (subg->pp2.x + 32) & -64;
    }

    for ( k = 0; k < n_points; k++ )
      pts->touch[k] &= TT_Flag_On_Curve;

    cur_to_org( n_points, pts );

    /* now consider hinting */
    if ( subg->is_hinted && n_ins > 0 )
    {
      exec->is_composite     = TRUE;
      exec->pedantic_hinting = load_flags & TTLOAD_PEDANTIC;

      error = Context_Run( exec, FALSE );
      if (error && exec->pedantic_hinting)
        return error;
    }

    /* save glyph origin and advance points */
    subg->pp1 = pts->cur[n_points - 2];
    subg->pp2 = pts->cur[n_points - 1];

    return TT_Err_Ok;
  }


/*******************************************************************
 *
 *  Function    :  Init_Glyph_Component
 *
 ******************************************************************/

  static
  void  Init_Glyph_Component( PSubglyph_Record    element,
                              PSubglyph_Record    original,
                              PExecution_Context  exec )
  {
    element->index     = -1;
    element->is_scaled = FALSE;
    element->is_hinted = FALSE;

    if ( original )
      mount_zone( &original->zone, &element->zone );
    else
      element->zone = exec->pts;

    element->zone.n_contours = 0;
    element->zone.n_points   = 0;

    element->arg1 = 0;
    element->arg2 = 0;

    element->element_flag = 0;
    element->preserve_pps = FALSE;

    element->transform.xx = 1L << 16;
    element->transform.xy = 0;
    element->transform.yx = 0;
    element->transform.yy = 1L << 16;

    element->transform.ox = 0;
    element->transform.oy = 0;

    element->metrics.horiBearingX = 0;
    element->metrics.horiAdvance  = 0;
  }



  LOCAL_FUNC
  TT_Error  Load_TrueType_Glyph(  PInstance   instance,
                                  PGlyph      glyph,
                                  UShort      glyph_index,
                                  UShort      load_flags )
  {
    enum TPhases_
    {
      Load_Exit,
      Load_Glyph,
      Load_Header,
      Load_Simple,
      Load_Composite,
      Load_End
    };

    typedef enum TPhases_  TPhases;

    DEFINE_ALL_LOCALS;

    PFace   face;

    UShort  num_points;
    Short   num_contours;
    UShort  left_points;
    Short   left_contours;
    UShort  num_elem_points;

    Long    table;
    UShort  load_top;
    Long    k, l;
    UShort  new_flags;
    Long    index;
    UShort  u, v;

    Long  glyph_offset, offset;

    TT_F26Dot6  x, y, nx, ny;

    Fixed  xx, xy, yx, yy;

    PExecution_Context  exec;

    PSubglyph_Record  subglyph, subglyph2;

    TGlyph_Zone base_pts;

    TPhases     phase;
    PByte       widths;

/*  TT_Glyph_Loader_Callback  cacheCb;        */
/*  TT_Outline                cached_outline; */


    /* first of all, check arguments */
    if ( !glyph )
      return TT_Err_Invalid_Glyph_Handle;

    face = glyph->face;
    if ( !face )
      return TT_Err_Invalid_Glyph_Handle;

    if ( glyph_index >= face->numGlyphs )
      return TT_Err_Invalid_Glyph_Index;

    if ( instance && (load_flags & TTLOAD_SCALE_GLYPH) == 0 )
    {
      instance    = 0;
      load_flags &= ~( TTLOAD_SCALE_GLYPH | TTLOAD_HINT_GLYPH );
    }

    table = TT_LookUp_Table( face, TTAG_glyf );
    if ( table < 0 )
    {
      PTRACE0(( "ERROR: There is no glyph table in this font file!\n" ));
      return TT_Err_Glyf_Table_Missing;
    }

    glyph_offset = face->dirTables[table].Offset;

    /* query new execution context */

    if ( instance && instance->debug )
      exec = instance->context;
    else
      exec = New_Context( face );

    if ( !exec )
      return TT_Err_Could_Not_Find_Context;

    Context_Load( exec, face, instance );

    if ( instance )
    {
      if ( instance->GS.instruct_control & 2 )
        exec->GS = Default_GraphicsState;
      else
        exec->GS = instance->GS;
      /* load default graphics state */

      glyph->outline.high_precision = ( instance->metrics.y_ppem < 24 );
    }

    /* save its critical pointers, as they'll be modified during load */
    base_pts = exec->pts;

    /* init variables */
    left_points   = face->maxPoints;
    left_contours = face->maxContours;

    num_points   = 0;
    num_contours = 0;

    load_top = 0;
    subglyph = exec->loadStack;

    Init_Glyph_Component( subglyph, NULL, exec );

    subglyph->index     = glyph_index;
    subglyph->is_hinted = load_flags & TTLOAD_HINT_GLYPH;

    /* when the cvt program has disabled hinting, the argument */
    /* is ignored.                                             */
    if ( instance && instance->GS.instruct_control & 1 )
      subglyph->is_hinted = FALSE;


    /* now access stream */

    if ( USE_Stream( face->stream, stream ) )
      goto Fin;

    /* Main loading loop */

    phase = Load_Glyph;
    index = 0;

    while ( phase != Load_Exit )
    {
      subglyph = exec->loadStack + load_top;

      switch ( phase )
      {
        /************************************************************/
        /*                                                          */
        /* Load_Glyph state                                         */
        /*                                                          */
        /*   reading a glyph's generic header to determine          */
        /*   whether it's simple or composite                       */
        /*                                                          */
        /* exit states: Load_Header and Load_End                    */

      case Load_Glyph:
        /* check glyph index and table */

        index = subglyph->index;
        if ( index < 0 || index >= face->numGlyphs )
        {
          error = TT_Err_Invalid_Glyph_Index;
          goto Fail;
        }

        /* get horizontal metrics */

        {
          Short   left_bearing;
          UShort  advance_width;


          Get_HMetrics( face, (UShort)index,
                        !(load_flags & TTLOAD_IGNORE_GLOBAL_ADVANCE_WIDTH),
                        &left_bearing,
                        &advance_width );

          subglyph->metrics.horiBearingX = left_bearing;
          subglyph->metrics.horiAdvance  = advance_width;
        }

        phase = Load_Header;


        /* The cache callback isn't part of the FreeType release yet */
        /* It is discarded for the moment..                          */
        /*                                                           */
#if 0
        if ( instance )
        {
          /* is the glyph in an outline cache ? */
          cacheCb = instance->owner->engine->glCallback;
          if ( cacheCb && 0 )   /* disabled */
          {
            /* we have a callback */
            error = cacheCb( instance->generic,
                             index, &cached_outline, &x, &y );
            if ( !error )
            {
              /* no error, then append the outline to the current subglyph */
              /* error = Append_Outline( subglyph,
                                         &left_points,
                                         &left_contours,
                                         &cached_outline ); */
              phase = Load_End;
            }
          }
        }
#endif
        break;


        /************************************************************/
        /*                                                          */
        /* Load_Header state                                        */
        /*                                                          */
        /*   reading a glyph's generic header to determine          */
        /*   wether it's simple or composite                        */
        /*                                                          */
        /* exit states: Load_Simple and Load_Composite              */
        /*                                                          */

      case Load_Header: /* load glyph */

        if ( index + 1 < face->numLocations &&
             face->glyphLocations[index] == face->glyphLocations[index + 1] )
        {
          /* as described by Frederic Loyer, these are spaces, and */
          /* not the unknown glyph.                                */

          num_contours = 0;
          num_points   = 0;

          subglyph->metrics.bbox.xMin = 0;
          subglyph->metrics.bbox.xMax = 0;
          subglyph->metrics.bbox.yMin = 0;
          subglyph->metrics.bbox.yMax = 0;

          subglyph->pp1.x = 0;
          subglyph->pp2.x = subglyph->metrics.horiAdvance;
          if (load_flags & TTLOAD_SCALE_GLYPH)
            subglyph->pp2.x = Scale_X( &exec->metrics, subglyph->pp2.x );

          exec->glyphSize = 0;
          phase = Load_End;
          break;
        }

        offset = glyph_offset + face->glyphLocations[index];

        /* read first glyph header */
        if ( FILE_Seek( offset ) ||
             ACCESS_Frame( 10L ) )
          goto Fail_File;

        num_contours = GET_Short();

        subglyph->metrics.bbox.xMin = GET_Short();
        subglyph->metrics.bbox.yMin = GET_Short();
        subglyph->metrics.bbox.xMax = GET_Short();
        subglyph->metrics.bbox.yMax = GET_Short();

        FORGET_Frame();

        PTRACE6(( "Glyph %ld:\n", index ));
        PTRACE6(( " # of contours: %d\n", num_contours ));
        PTRACE6(( " xMin: %4d  xMax: %4d\n",
                     subglyph->metrics.bbox.xMin,
                     subglyph->metrics.bbox.xMax ));
        PTRACE6(( " yMin: %4d  yMax: %4d\n",
                     subglyph->metrics.bbox.yMin,
                     subglyph->metrics.bbox.yMax ));

        if ( num_contours > left_contours )
        {
          PTRACE0(( "ERROR: Too many contours for glyph %ld\n", index ));
          error = TT_Err_Too_Many_Contours;
          goto Fail;
        }

        subglyph->pp1.x = subglyph->metrics.bbox.xMin -
                          subglyph->metrics.horiBearingX;
        subglyph->pp1.y = 0;
        subglyph->pp2.x = subglyph->pp1.x + subglyph->metrics.horiAdvance;
        if (load_flags & TTLOAD_SCALE_GLYPH)
        {
          subglyph->pp1.x = Scale_X( &exec->metrics, subglyph->pp1.x );
          subglyph->pp2.x = Scale_X( &exec->metrics, subglyph->pp2.x );
        }

        /* is it a simple glyph ? */
        if ( num_contours > 0 )
          phase = Load_Simple;
        else
          phase = Load_Composite;

        break;


        /************************************************************/
        /*                                                          */
        /* Load_Simple state                                        */
        /*                                                          */
        /*   reading a simple glyph (num_contours must be set to    */
        /*   the glyph's number of contours.)                       */
        /*                                                          */
        /* exit states : Load_End                                   */
        /*                                                          */

      case Load_Simple:
        new_flags = load_flags;

        /* disable hinting when scaling */
        if ( !subglyph->is_hinted )
          new_flags &= ~TTLOAD_HINT_GLYPH;

        error = Load_Simple_Glyph( exec,
                                   stream,
                                   num_contours,
                                   left_contours,
                                   left_points,
                                   new_flags,
                                   subglyph );
        if ( error )
          goto Fail;

        /* Note: We could have put the simple loader source there */
        /*       but the code is fat enough already :-)           */

        num_points = exec->pts.n_points - 2;

        phase = Load_End;

        break;


        /************************************************************/
        /*                                                          */
        /* Load_Composite state                                     */
        /*                                                          */
        /*   reading a composite glyph header a pushing a new       */
        /*   load element on the stack.                             */
        /*                                                          */
        /* exit states: Load_Glyph                                  */
        /*                                                          */

      case Load_Composite:

        /* create a new element on the stack */
        load_top++;

        if ( load_top > face->maxComponents )
        {
          error = TT_Err_Invalid_Composite;
          goto Fail;
        }

        subglyph2 = exec->loadStack + load_top;

        Init_Glyph_Component( subglyph2, subglyph, NULL );
        subglyph2->is_hinted = subglyph->is_hinted;

        /* now read composite header */

        if ( ACCESS_Frame( 4L ) )
          goto Fail_File;

        subglyph->element_flag = new_flags = GET_UShort();

        subglyph2->index = GET_UShort();

        FORGET_Frame();

        k = 1 + 1;

        if ( new_flags & ARGS_ARE_WORDS )
          k *= 2;

        if ( new_flags & WE_HAVE_A_SCALE )
          k += 2;

        else if ( new_flags & WE_HAVE_AN_XY_SCALE )
          k += 4;

        else if ( new_flags & WE_HAVE_A_2X2 )
          k += 8;

        if ( ACCESS_Frame( k ) )
          goto Fail_File;

        if ( new_flags & ARGS_ARE_WORDS )
        {
          k = GET_Short();
          l = GET_Short();
        }
        else
        {
          k = GET_Char();
          l = GET_Char();
        }

        subglyph->arg1 = k;
        subglyph->arg2 = l;

        if ( new_flags & ARGS_ARE_XY_VALUES )
        {
          subglyph->transform.ox = k;
          subglyph->transform.oy = l;
        }

        xx = 1L << 16;
        xy = 0;
        yx = 0;
        yy = 1L << 16;

        if ( new_flags & WE_HAVE_A_SCALE )
        {
          xx = (Fixed)GET_Short() << 2;
          yy = xx;
          subglyph2->is_scaled = TRUE;
        }
        else if ( new_flags & WE_HAVE_AN_XY_SCALE )
        {
          xx = (Fixed)GET_Short() << 2;
          yy = (Fixed)GET_Short() << 2;
          subglyph2->is_scaled = TRUE;
        }
        else if ( new_flags & WE_HAVE_A_2X2 )
        {
          xx = (Fixed)GET_Short() << 2;
          xy = (Fixed)GET_Short() << 2;
          yx = (Fixed)GET_Short() << 2;
          yy = (Fixed)GET_Short() << 2;
          subglyph2->is_scaled = TRUE;
        }

        FORGET_Frame();

        subglyph->transform.xx = xx;
        subglyph->transform.xy = xy;
        subglyph->transform.yx = yx;
        subglyph->transform.yy = yy;

        k = TT_MulFix( xx, yy ) -  TT_MulFix( xy, yx );

        /* disable hinting in case of scaling/slanting */
        if ( ABS( k ) != (1L << 16) )
          subglyph2->is_hinted = FALSE;

        subglyph->file_offset = FILE_Pos();

        phase = Load_Glyph;

        break;


        /************************************************************/
        /*                                                          */
        /* Load_End state                                           */
        /*                                                          */
        /*   after loading a glyph, apply transformation and offset */
        /*   where necessary, pops element and continue or          */
        /*   stop process.                                          */
        /*                                                          */
        /* exit states : Load_Composite and Load_Exit               */
        /*                                                          */

      case Load_End:
        if ( load_top > 0 )
        {
          subglyph2 = subglyph;

          load_top--;
          subglyph = exec->loadStack + load_top;

          /* check advance width and left side bearing */

          if ( !subglyph->preserve_pps &&
               subglyph->element_flag & USE_MY_METRICS )
          {
            subglyph->metrics.horiBearingX = subglyph2->metrics.horiBearingX;
            subglyph->metrics.horiAdvance  = subglyph2->metrics.horiAdvance;

            subglyph->pp1 = subglyph2->pp1;
            subglyph->pp2 = subglyph2->pp2;

            subglyph->preserve_pps = TRUE;
          }

          /* apply scale */

          if ( subglyph2->is_scaled )
          {
            TT_Vector*  cur = subglyph2->zone.cur;
            TT_Vector*  org = subglyph2->zone.org;

            for ( u = 0; u < num_points; u++ )
            {
              nx = TT_MulFix( cur->x, subglyph->transform.xx ) +
                   TT_MulFix( cur->y, subglyph->transform.yx );

              ny = TT_MulFix( cur->x, subglyph->transform.xy ) +
                   TT_MulFix( cur->y, subglyph->transform.yy );

              cur->x = nx;
              cur->y = ny;

              nx = TT_MulFix( org->x, subglyph->transform.xx ) +
                   TT_MulFix( org->y, subglyph->transform.yx );

              ny = TT_MulFix( org->x, subglyph->transform.xy ) +
                   TT_MulFix( org->y, subglyph->transform.yy );

              org->x = nx;
              org->y = ny;

              cur++;
              org++;
            }
          }

          /* adjust counts */

          num_elem_points = subglyph->zone.n_points;

          for ( k = 0; k < num_contours; k++ )
            subglyph2->zone.contours[k] += num_elem_points;

          subglyph->zone.n_points   += num_points;
          subglyph->zone.n_contours += num_contours;

          left_points   -= num_points;
          left_contours -= num_contours;

          if ( !(subglyph->element_flag & ARGS_ARE_XY_VALUES) )
          {
            /* move second glyph according to control points */
            /* the attach points are relative to the specific component */

            u = (UShort)subglyph->arg1;
            v = (UShort)subglyph->arg2;

            if ( u >= num_elem_points ||
                 v >= num_points )
            {
              error = TT_Err_Invalid_Composite;
              goto Fail;
            }

            /* adjust count */
            v += num_elem_points;

            x = subglyph->zone.cur[u].x - subglyph->zone.cur[v].x;
            y = subglyph->zone.cur[u].y - subglyph->zone.cur[v].y;
          }
          else
          {
            /* apply offset */

            x = subglyph->transform.ox;
            y = subglyph->transform.oy;

            if ( load_flags & TTLOAD_SCALE_GLYPH )
            {
              x = Scale_X( &exec->metrics, x );
              y = Scale_Y( &exec->metrics, y );

              if ( subglyph->element_flag & ROUND_XY_TO_GRID )
              {
                x = (x+32) & -64;
                y = (y+32) & -64;
              }
            }
          }

          translate_array( num_points, subglyph2->zone.cur, x, y );

          cur_to_org( num_points, &subglyph2->zone );

          num_points   = subglyph->zone.n_points;
          num_contours = subglyph->zone.n_contours;

          /* check for last component */

          if ( FILE_Seek( subglyph->file_offset ) )
            goto Fail_File;

          if ( subglyph->element_flag & MORE_COMPONENTS )
            phase = Load_Composite;
          else
          {
            error = Load_Composite_End( num_points,
                                        num_contours,
                                        exec,
                                        subglyph,
                                        load_flags,
                                        stream );
            if ( error )
              goto Fail;

            phase = Load_End;
          }
        }
        else
          phase = Load_Exit;

        break;


      case Load_Exit:
        break;
      }
    }

    /* finally, copy the points arrays to the glyph object */

    exec->pts = base_pts;

    for ( u = 0; u < num_points + 2; u++ )
    {
      glyph->outline.points[u] = exec->pts.cur[u];
      glyph->outline.flags [u] = exec->pts.touch[u];
    }

    for ( k = 0; k < num_contours; k++ )
      glyph->outline.contours[k] = exec->pts.contours[k];

    glyph->outline.n_points    = num_points;
    glyph->outline.n_contours  = num_contours;
    glyph->outline.second_pass = TRUE;

    /* translate array so that (0,0) is the glyph's origin */
    translate_array( num_points + 2,
                     glyph->outline.points,
                     -subglyph->pp1.x,
                     0 );

    TT_Get_Outline_BBox( &glyph->outline, &glyph->metrics.bbox );

    if ( subglyph->is_hinted )
    {
      /* grid-fit the bounding box */
      glyph->metrics.bbox.xMin &= -64;
      glyph->metrics.bbox.yMin &= -64;
      glyph->metrics.bbox.xMax  = (glyph->metrics.bbox.xMax+63) & -64;
      glyph->metrics.bbox.yMax  = (glyph->metrics.bbox.yMax+63) & -64;
    }

    /* get the device-independent scaled horizontal metrics */
    /* take care of fixed-pitch fonts...                    */
    {
      TT_Pos  left_bearing;
      TT_Pos  advance;


      left_bearing = subglyph->metrics.horiBearingX;
      advance      = subglyph->metrics.horiAdvance;

      if ( face->postscript.isFixedPitch )
        advance = face->horizontalHeader.advance_Width_Max;

      if ( load_flags & TTLOAD_SCALE_GLYPH )
      {
        left_bearing = Scale_X( &exec->metrics, left_bearing );
        advance      = Scale_X( &exec->metrics, advance      );
      }

      glyph->metrics.linearHoriBearingX = left_bearing;
      glyph->metrics.linearHoriAdvance  = advance;
    }

    glyph->metrics.horiBearingX = glyph->metrics.bbox.xMin;
    glyph->metrics.horiBearingY = glyph->metrics.bbox.yMax;
    glyph->metrics.horiAdvance  = subglyph->pp2.x - subglyph->pp1.x;

    /* Now take care of vertical metrics.  In the case where there is    */
    /* no vertical information within the font (relatively common), make */
    /* up some metrics `by hand' ...                                     */

    {
      Short   top_bearing;    /* vertical top side bearing (EM units) */
      UShort  advance_height; /* vertical advance height (EM units)   */

      TT_Pos  left;     /* scaled vertical left side bearing          */
      TT_Pos  Top;      /* scaled original vertical top side bearing  */
      TT_Pos  top;      /* scaled vertical top side bearing           */
      TT_Pos  advance;  /* scaled vertical advance height             */


      /* Get the unscaled `tsb' and `ah' values */
      if ( face->verticalInfo                          &&
           face->verticalHeader.number_Of_VMetrics > 0 )
      {
        /* Don't assume that both the vertical header and vertical */
        /* metrics are present in the same font :-)                */

        TT_Get_Metrics( (TT_Horizontal_Header*)&face->verticalHeader,
                        glyph_index,
                        &top_bearing,
                        &advance_height );
      }
      else
      {
        /* Make up the distances from the horizontal header..     */

        /* NOTE: The OS/2 values are the only `portable' ones,    */
        /*       which is why we use them...                      */
        /*                                                        */
        /* NOTE2: The sTypoDescender is negative, which is why    */
        /*        we compute the baseline-to-baseline distance    */
        /*        here with :                                     */
        /*             ascender - descender + linegap             */
        /*                                                        */
        top_bearing    = (Short) (face->os2.sTypoLineGap / 2);
        advance_height = (UShort)(face->os2.sTypoAscender -
                                  face->os2.sTypoDescender +
                                  face->os2.sTypoLineGap);
      }

      /* We must adjust the top_bearing value from the bounding box given
         in the glyph header to te bounding box calculated with
         TT_Get_Outline_BBox()                                            */

      /* scale the metrics */
      if ( load_flags & TTLOAD_SCALE_GLYPH )
      {
        Top     = Scale_Y( &exec->metrics, top_bearing );
        top     = Scale_Y( &exec->metrics,
                           top_bearing + subglyph->metrics.bbox.yMax ) -
                    glyph->metrics.bbox.yMax;
        advance = Scale_Y( &exec->metrics, advance_height );
      }
      else
      {
        Top     = top_bearing;
        top     = top_bearing + subglyph->metrics.bbox.yMax -
                    glyph->metrics.bbox.yMax;
        advance = advance_height;
      }

      glyph->metrics.linearVertBearingY = Top;
      glyph->metrics.linearVertAdvance  = advance;

      /* XXX : for now, we have no better algo for the lsb, but it should */
      /*       work ok..                                                  */
      /*                                                                  */
      left = ( glyph->metrics.bbox.xMin - glyph->metrics.bbox.xMax ) / 2;

      /* grid-fit them if necessary */
      if ( subglyph->is_hinted )
      {
        left   &= -64;
        top     = (top + 63) & -64;
        advance = (advance + 32) & -64;
      }

      glyph->metrics.vertBearingX = left;
      glyph->metrics.vertBearingY = top;
      glyph->metrics.vertAdvance  = advance;
    }

    /* Adjust advance width to the value contained in the hdmx table. */
    if ( !exec->face->postscript.isFixedPitch && instance &&
         subglyph->is_hinted )
    {
      widths = Get_Advance_Widths( exec->face,
                                   exec->instance->metrics.x_ppem );
      if ( widths )
        glyph->metrics.horiAdvance = widths[glyph_index] << 6;
    }

    glyph->outline.dropout_mode = (Char)exec->GS.scan_type;

    error = TT_Err_Ok;

  Fail_File:
  Fail:
    DONE_Stream( stream );

  Fin:

    /* reset the execution context */
    exec->pts = base_pts;

    if ( !instance || !instance->debug )
      Done_Context( exec );

    return error;
  }


/* END */