text: /***************************************************************************/
  /*                                                                         */
  /*  pfrgload.c                                                             */
  /*                                                                         */
  /*    FreeType PFR glyph loader (body).                                    */
  /*                                                                         */
  /*  Copyright 2002-2018 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 "pfrgload.h"
  #include "pfrsbit.h"
  #include "pfrload.h"            /* for macro definitions */
  #include FT_INTERNAL_DEBUG_H
  
  #include "pfrerror.h"
  
  #undef  FT_COMPONENT
  #define FT_COMPONENT  trace_pfr
  
  
    /*************************************************************************/
    /*************************************************************************/
    /*****                                                               *****/
    /*****                      PFR GLYPH BUILDER                        *****/
    /*****                                                               *****/
    /*************************************************************************/
    /*************************************************************************/
  
  
    FT_LOCAL_DEF( void )
    pfr_glyph_init( PFR_Glyph       glyph,
                    FT_GlyphLoader  loader )
    {
      FT_ZERO( glyph );
  
      glyph->loader     = loader;
      glyph->path_begun = 0;
  
      FT_GlyphLoader_Rewind( loader );
    }
  
  
    FT_LOCAL_DEF( void )
    pfr_glyph_done( PFR_Glyph  glyph )
    {
      FT_Memory  memory = glyph->loader->memory;
  
  
      FT_FREE( glyph->x_control );
      glyph->y_control = NULL;
  
      glyph->max_xy_control = 0;
  #if 0
      glyph->num_x_control  = 0;
      glyph->num_y_control  = 0;
  #endif
  
      FT_FREE( glyph->subs );
  
      glyph->max_subs = 0;
      glyph->num_subs = 0;
  
      glyph->loader     = NULL;
      glyph->path_begun = 0;
    }
  
  
    /* close current contour, if any */
    static void
    pfr_glyph_close_contour( PFR_Glyph  glyph )
    {
      FT_GlyphLoader  loader  = glyph->loader;
      FT_Outline*     outline = &loader->current.outline;
      FT_Int          last, first;
  
  
      if ( !glyph->path_begun )
        return;
  
      /* compute first and last point indices in current glyph outline */
      last  = outline->n_points - 1;
      first = 0;
      if ( outline->n_contours > 0 )
        first = outline->contours[outline->n_contours - 1];
  
      /* if the last point falls on the same location as the first one */
      /* we need to delete it                                          */
      if ( last > first )
      {
        FT_Vector*  p1 = outline->points + first;
        FT_Vector*  p2 = outline->points + last;
  
  
        if ( p1->x == p2->x && p1->y == p2->y )
        {
          outline->n_points--;
          last--;
        }
      }
  
      /* don't add empty contours */
      if ( last >= first )
        outline->contours[outline->n_contours++] = (short)last;
  
      glyph->path_begun = 0;
    }
  
  
    /* reset glyph to start the loading of a new glyph */
    static void
    pfr_glyph_start( PFR_Glyph  glyph )
    {
      glyph->path_begun = 0;
    }
  
  
    static FT_Error
    pfr_glyph_line_to( PFR_Glyph   glyph,
                       FT_Vector*  to )
    {
      FT_GlyphLoader  loader  = glyph->loader;
      FT_Outline*     outline = &loader->current.outline;
      FT_Error        error;
  
  
      /* check that we have begun a new path */
      if ( !glyph->path_begun )
      {
        error = FT_THROW( Invalid_Table );
        FT_ERROR(( "pfr_glyph_line_to: invalid glyph data\n" ));
        goto Exit;
      }
  
      error = FT_GLYPHLOADER_CHECK_POINTS( loader, 1, 0 );
      if ( !error )
      {
        FT_Int  n = outline->n_points;
  
  
        outline->points[n] = *to;
        outline->tags  [n] = FT_CURVE_TAG_ON;
  
        outline->n_points++;
      }
  
    Exit:
      return error;
    }
  
  
    static FT_Error
    pfr_glyph_curve_to( PFR_Glyph   glyph,
                        FT_Vector*  control1,
                        FT_Vector*  control2,
                        FT_Vector*  to )
    {
      FT_GlyphLoader  loader  = glyph->loader;
      FT_Outline*     outline = &loader->current.outline;
      FT_Error        error;
  
  
      /* check that we have begun a new path */
      if ( !glyph->path_begun )
      {
        error = FT_THROW( Invalid_Table );
        FT_ERROR(( "pfr_glyph_line_to: invalid glyph data\n" ));
        goto Exit;
      }
  
      error = FT_GLYPHLOADER_CHECK_POINTS( loader, 3, 0 );
      if ( !error )
      {
        FT_Vector*  vec = outline->points         + outline->n_points;
        FT_Byte*    tag = (FT_Byte*)outline->tags + outline->n_points;
  
  
        vec[0] = *control1;
        vec[1] = *control2;
        vec[2] = *to;
        tag[0] = FT_CURVE_TAG_CUBIC;
        tag[1] = FT_CURVE_TAG_CUBIC;
        tag[2] = FT_CURVE_TAG_ON;
  
        outline->n_points = (FT_Short)( outline->n_points + 3 );
      }
  
    Exit:
      return error;
    }
  
  
    static FT_Error
    pfr_glyph_move_to( PFR_Glyph   glyph,
                       FT_Vector*  to )
    {
      FT_GlyphLoader  loader  = glyph->loader;
      FT_Error        error;
  
  
      /* close current contour if any */
      pfr_glyph_close_contour( glyph );
  
      /* indicate that a new contour has started */
      glyph->path_begun = 1;
  
      /* check that there is space for a new contour and a new point */
      error = FT_GLYPHLOADER_CHECK_POINTS( loader, 1, 1 );
      if ( !error )
      {
        /* add new start point */
        error = pfr_glyph_line_to( glyph, to );
      }
  
      return error;
    }
  
  
    static void
    pfr_glyph_end( PFR_Glyph  glyph )
    {
      /* close current contour if any */
      pfr_glyph_close_contour( glyph );
  
      /* merge the current glyph into the stack */
      FT_GlyphLoader_Add( glyph->loader );
    }
  
  
    /*************************************************************************/
    /*************************************************************************/
    /*****                                                               *****/
    /*****                      PFR GLYPH LOADER                         *****/
    /*****                                                               *****/
    /*************************************************************************/
    /*************************************************************************/
  
  
    /* load a simple glyph */
    static FT_Error
    pfr_glyph_load_simple( PFR_Glyph  glyph,
                           FT_Byte*   p,
                           FT_Byte*   limit )
    {
      FT_Error   error  = FT_Err_Ok;
      FT_Memory  memory = glyph->loader->memory;
      FT_UInt    flags, x_count, y_count, i, count, mask;
      FT_Int     x;
  
  
      PFR_CHECK( 1 );
      flags = PFR_NEXT_BYTE( p );
  
      /* test for composite glyphs */
      if ( flags & PFR_GLYPH_IS_COMPOUND )
        goto Failure;
  
      x_count = 0;
      y_count = 0;
  
      if ( flags & PFR_GLYPH_1BYTE_XYCOUNT )
      {
        PFR_CHECK( 1 );
        count   = PFR_NEXT_BYTE( p );
        x_count = count & 15;
        y_count = count >> 4;
      }
      else
      {
        if ( flags & PFR_GLYPH_XCOUNT )
        {
          PFR_CHECK( 1 );
          x_count = PFR_NEXT_BYTE( p );
        }
  
        if ( flags & PFR_GLYPH_YCOUNT )
        {
          PFR_CHECK( 1 );
          y_count = PFR_NEXT_BYTE( p );
        }
      }
  
      count = x_count + y_count;
  
      /* re-allocate array when necessary */
      if ( count > glyph->max_xy_control )
      {
        FT_UInt  new_max = FT_PAD_CEIL( count, 8 );
  
  
        if ( FT_RENEW_ARRAY( glyph->x_control,
                             glyph->max_xy_control,
                             new_max ) )
          goto Exit;
  
        glyph->max_xy_control = new_max;
      }
  
      glyph->y_control = glyph->x_control + x_count;
  
      mask = 0;
      x    = 0;
  
      for ( i = 0; i < count; i++ )
      {
        if ( ( i & 7 ) == 0 )
        {
          PFR_CHECK( 1 );
          mask = PFR_NEXT_BYTE( p );
        }
  
        if ( mask & 1 )
        {
          PFR_CHECK( 2 );
          x = PFR_NEXT_SHORT( p );
        }
        else
        {
          PFR_CHECK( 1 );
          x += PFR_NEXT_BYTE( p );
        }
  
        glyph->x_control[i] = x;
  
        mask >>= 1;
      }
  
      /* XXX: we ignore the secondary stroke and edge definitions */
      /*      since we don't support native PFR hinting           */
      /*                                                          */
      if ( flags & PFR_GLYPH_SINGLE_EXTRA_ITEMS )
      {
        error = pfr_extra_items_skip( &p, limit );
        if ( error )
          goto Exit;
      }
  
      pfr_glyph_start( glyph );
  
      /* now load a simple glyph */
      {
        FT_Vector   pos[4];
        FT_Vector*  cur;
  
  
        pos[0].x = pos[0].y = 0;
        pos[3]   = pos[0];
  
        for (;;)
        {
          FT_UInt  format, format_low, args_format = 0, args_count, n;
  
  
          /***************************************************************/
          /*  read instruction                                           */
          /*                                                             */
          PFR_CHECK( 1 );
          format     = PFR_NEXT_BYTE( p );
          format_low = format & 15;
  
          switch ( format >> 4 )
          {
          case 0:                                               /* end glyph */
            FT_TRACE6(( "- end glyph" ));
            args_count = 0;
            break;
  
          case 1:                                  /* general line operation */
            FT_TRACE6(( "- general line" ));
            goto Line1;
  
          case 4:                                 /* move to inside contour  */
            FT_TRACE6(( "- move to inside" ));
            goto Line1;
  
          case 5:                                 /* move to outside contour */
            FT_TRACE6(( "- move to outside" ));
          Line1:
            args_format = format_low;
            args_count  = 1;
            break;
  
          case 2:                                      /* horizontal line to */
            FT_TRACE6(( "- horizontal line to cx.%d", format_low ));
            if ( format_low >= x_count )
              goto Failure;
            pos[0].x   = glyph->x_control[format_low];
            pos[0].y   = pos[3].y;
            pos[3]     = pos[0];
            args_count = 0;
            break;
  
          case 3:                                        /* vertical line to */
            FT_TRACE6(( "- vertical line to cy.%d", format_low ));
            if ( format_low >= y_count )
              goto Failure;
            pos[0].x   = pos[3].x;
            pos[0].y   = glyph->y_control[format_low];
            pos[3]     = pos[0];
            args_count = 0;
            break;
  
          case 6:                            /* horizontal to vertical curve */
            FT_TRACE6(( "- hv curve " ));
            args_format = 0xB8E;
            args_count  = 3;
            break;
  
          case 7:                            /* vertical to horizontal curve */
            FT_TRACE6(( "- vh curve" ));
            args_format = 0xE2B;
            args_count  = 3;
            break;
  
          default:                                       /* general curve to */
            FT_TRACE6(( "- general curve" ));
            args_count  = 4;
            args_format = format_low;
          }
  
          /***********************************************************/
          /*  now read arguments                                     */
          /*                                                         */
          cur = pos;
          for ( n = 0; n < args_count; n++ )
          {
            FT_UInt  idx;
            FT_Int   delta;
  
  
            /* read the X argument */
            switch ( args_format & 3 )
            {
            case 0:                           /* 8-bit index */
              PFR_CHECK( 1 );
              idx = PFR_NEXT_BYTE( p );
              if ( idx >= x_count )
                goto Failure;
              cur->x = glyph->x_control[idx];
              FT_TRACE7(( " cx#%d", idx ));
              break;
  
            case 1:                           /* 16-bit absolute value */
              PFR_CHECK( 2 );
              cur->x = PFR_NEXT_SHORT( p );
              FT_TRACE7(( " x.%d", cur->x ));
              break;
  
            case 2:                           /* 8-bit delta */
              PFR_CHECK( 1 );
              delta  = PFR_NEXT_INT8( p );
              cur->x = pos[3].x + delta;
              FT_TRACE7(( " dx.%d", delta ));
              break;
  
            default:
              FT_TRACE7(( " |" ));
              cur->x = pos[3].x;
            }
  
            /* read the Y argument */
            switch ( ( args_format >> 2 ) & 3 )
            {
            case 0:                           /* 8-bit index */
              PFR_CHECK( 1 );
              idx  = PFR_NEXT_BYTE( p );
              if ( idx >= y_count )
                goto Failure;
              cur->y = glyph->y_control[idx];
              FT_TRACE7(( " cy#%d", idx ));
              break;
  
            case 1:                           /* 16-bit absolute value */
              PFR_CHECK( 2 );
              cur->y = PFR_NEXT_SHORT( p );
              FT_TRACE7(( " y.%d", cur->y ));
              break;
  
            case 2:                           /* 8-bit delta */
              PFR_CHECK( 1 );
              delta  = PFR_NEXT_INT8( p );
              cur->y = pos[3].y + delta;
              FT_TRACE7(( " dy.%d", delta ));
              break;
  
            default:
              FT_TRACE7(( " -" ));
              cur->y = pos[3].y;
            }
  
            /* read the additional format flag for the general curve */
            if ( n == 0 && args_count == 4 )
            {
              PFR_CHECK( 1 );
              args_format = PFR_NEXT_BYTE( p );
              args_count--;
            }
            else
              args_format >>= 4;
  
            /* save the previous point */
            pos[3] = cur[0];
            cur++;
          }
  
          FT_TRACE7(( "\n" ));
  
          /***********************************************************/
          /*  finally, execute instruction                           */
          /*                                                         */
          switch ( format >> 4 )
          {
          case 0:                                       /* end glyph => EXIT */
            pfr_glyph_end( glyph );
            goto Exit;
  
          case 1:                                         /* line operations */
          case 2:
          case 3:
            error = pfr_glyph_line_to( glyph, pos );
            goto Test_Error;
  
          case 4:                                 /* move to inside contour  */
          case 5:                                 /* move to outside contour */
            error = pfr_glyph_move_to( glyph, pos );
            goto Test_Error;
  
          default:                                       /* curve operations */
            error = pfr_glyph_curve_to( glyph, pos, pos + 1, pos + 2 );
  
          Test_Error:  /* test error condition */
            if ( error )
              goto Exit;
          }
        } /* for (;;) */
      }
  
    Exit:
      return error;
  
    Failure:
    Too_Short:
      error = FT_THROW( Invalid_Table );
      FT_ERROR(( "pfr_glyph_load_simple: invalid glyph data\n" ));
      goto Exit;
    }
  
  
    /* load a composite/compound glyph */
    static FT_Error
    pfr_glyph_load_compound( PFR_Glyph  glyph,
                             FT_Byte*   p,
                             FT_Byte*   limit )
    {
      FT_Error        error  = FT_Err_Ok;
      FT_GlyphLoader  loader = glyph->loader;
      FT_Memory       memory = loader->memory;
      PFR_SubGlyph    subglyph;
      FT_UInt         flags, i, count, org_count;
      FT_Int          x_pos, y_pos;
  
  
      PFR_CHECK( 1 );
      flags = PFR_NEXT_BYTE( p );
  
      /* test for composite glyphs */
      if ( !( flags & PFR_GLYPH_IS_COMPOUND ) )
        goto Failure;
  
      count = flags & 0x3F;
  
      /* ignore extra items when present */
      /*                                 */
      if ( flags & PFR_GLYPH_COMPOUND_EXTRA_ITEMS )
      {
        error = pfr_extra_items_skip( &p, limit );
        if ( error )
          goto Exit;
      }
  
      /* we can't rely on the FT_GlyphLoader to load sub-glyphs, because   */
      /* the PFR format is dumb, using direct file offsets to point to the */
      /* sub-glyphs (instead of glyph indices).  Sigh.                     */
      /*                                                                   */
      /* For now, we load the list of sub-glyphs into a different array    */
      /* but this will prevent us from using the auto-hinter at its best   */
      /* quality.                                                          */
      /*                                                                   */
      org_count = glyph->num_subs;
  
      if ( org_count + count > glyph->max_subs )
      {
        FT_UInt  new_max = ( org_count + count + 3 ) & (FT_UInt)-4;
  
  
        /* we arbitrarily limit the number of subglyphs */
        /* to avoid endless recursion                   */
        if ( new_max > 64 )
        {
          error = FT_THROW( Invalid_Table );
          FT_ERROR(( "pfr_glyph_load_compound:"
                     " too many compound glyphs components\n" ));
          goto Exit;
        }
  
        if ( FT_RENEW_ARRAY( glyph->subs, glyph->max_subs, new_max ) )
          goto Exit;
  
        glyph->max_subs = new_max;
      }
  
      subglyph = glyph->subs + org_count;
  
      for ( i = 0; i < count; i++, subglyph++ )
      {
        FT_UInt  format;
  
  
        x_pos = 0;
        y_pos = 0;
  
        PFR_CHECK( 1 );
        format = PFR_NEXT_BYTE( p );
  
        /* read scale when available */
        subglyph->x_scale = 0x10000L;
        if ( format & PFR_SUBGLYPH_XSCALE )
        {
          PFR_CHECK( 2 );
          subglyph->x_scale = PFR_NEXT_SHORT( p ) * 16;
        }
  
        subglyph->y_scale = 0x10000L;
        if ( format & PFR_SUBGLYPH_YSCALE )
        {
          PFR_CHECK( 2 );
          subglyph->y_scale = PFR_NEXT_SHORT( p ) * 16;
        }
  
        /* read offset */
        switch ( format & 3 )
        {
        case 1:
          PFR_CHECK( 2 );
          x_pos = PFR_NEXT_SHORT( p );
          break;
  
        case 2:
          PFR_CHECK( 1 );
          x_pos += PFR_NEXT_INT8( p );
          break;
  
        default:
          ;
        }
  
        switch ( ( format >> 2 ) & 3 )
        {
        case 1:
          PFR_CHECK( 2 );
          y_pos = PFR_NEXT_SHORT( p );
          break;
  
        case 2:
          PFR_CHECK( 1 );
          y_pos += PFR_NEXT_INT8( p );
          break;
  
        default:
          ;
        }
  
        subglyph->x_delta = x_pos;
        subglyph->y_delta = y_pos;
  
        /* read glyph position and size now */
        if ( format & PFR_SUBGLYPH_2BYTE_SIZE )
        {
          PFR_CHECK( 2 );
          subglyph->gps_size = PFR_NEXT_USHORT( p );
        }
        else
        {
          PFR_CHECK( 1 );
          subglyph->gps_size = PFR_NEXT_BYTE( p );
        }
  
        if ( format & PFR_SUBGLYPH_3BYTE_OFFSET )
        {
          PFR_CHECK( 3 );
          subglyph->gps_offset = PFR_NEXT_ULONG( p );
        }
        else
        {
          PFR_CHECK( 2 );
          subglyph->gps_offset = PFR_NEXT_USHORT( p );
        }
  
        glyph->num_subs++;
      }
  
    Exit:
      return error;
  
    Failure:
    Too_Short:
      error = FT_THROW( Invalid_Table );
      FT_ERROR(( "pfr_glyph_load_compound: invalid glyph data\n" ));
      goto Exit;
    }
  
  
    static FT_Error
    pfr_glyph_load_rec( PFR_Glyph  glyph,
                        FT_Stream  stream,
                        FT_ULong   gps_offset,
                        FT_ULong   offset,
                        FT_ULong   size )
    {
      FT_Error  error;
      FT_Byte*  p;
      FT_Byte*  limit;
  
  
      if ( FT_STREAM_SEEK( gps_offset + offset ) ||
           FT_FRAME_ENTER( size )                )
        goto Exit;
  
      p     = (FT_Byte*)stream->cursor;
      limit = p + size;
  
      if ( size > 0 && *p & PFR_GLYPH_IS_COMPOUND )
      {
        FT_UInt         n, old_count, count;
        FT_GlyphLoader  loader = glyph->loader;
        FT_Outline*     base   = &loader->base.outline;
  
  
        old_count = glyph->num_subs;
  
        /* this is a compound glyph - load it */
        error = pfr_glyph_load_compound( glyph, p, limit );
  
        FT_FRAME_EXIT();
  
        if ( error )
          goto Exit;
  
        count = glyph->num_subs - old_count;
  
        FT_TRACE4(( "compound glyph with %d element%s (offset %lu):\n",
                    count,
                    count == 1 ? "" : "s",
                    offset ));
  
        /* now, load each individual glyph */
        for ( n = 0; n < count; n++ )
        {
          FT_Int        i, old_points, num_points;
          PFR_SubGlyph  subglyph;
  
  
          FT_TRACE4(( "  subglyph %d:\n", n ));
  
          subglyph   = glyph->subs + old_count + n;
          old_points = base->n_points;
  
          error = pfr_glyph_load_rec( glyph, stream, gps_offset,
                                      subglyph->gps_offset,
                                      subglyph->gps_size );
          if ( error )
            break;
  
          /* note that `glyph->subs' might have been re-allocated */
          subglyph   = glyph->subs + old_count + n;
          num_points = base->n_points - old_points;
  
          /* translate and eventually scale the new glyph points */
          if ( subglyph->x_scale != 0x10000L || subglyph->y_scale != 0x10000L )
          {
            FT_Vector*  vec = base->points + old_points;
  
  
            for ( i = 0; i < num_points; i++, vec++ )
            {
              vec->x = FT_MulFix( vec->x, subglyph->x_scale ) +
                         subglyph->x_delta;
              vec->y = FT_MulFix( vec->y, subglyph->y_scale ) +
                         subglyph->y_delta;
            }
          }
          else
          {
            FT_Vector*  vec = loader->base.outline.points + old_points;
  
  
            for ( i = 0; i < num_points; i++, vec++ )
            {
              vec->x += subglyph->x_delta;
              vec->y += subglyph->y_delta;
            }
          }
  
          /* proceed to next sub-glyph */
        }
  
        FT_TRACE4(( "end compound glyph with %d element%s\n",
                    count,
                    count == 1 ? "" : "s" ));
      }
      else
      {
        FT_TRACE4(( "simple glyph (offset %lu)\n", offset ));
  
        /* load a simple glyph */
        error = pfr_glyph_load_simple( glyph, p, limit );
  
        FT_FRAME_EXIT();
      }
  
    Exit:
      return error;
    }
  
  
    FT_LOCAL_DEF( FT_Error )
    pfr_glyph_load( PFR_Glyph  glyph,
                    FT_Stream  stream,
                    FT_ULong   gps_offset,
                    FT_ULong   offset,
                    FT_ULong   size )
    {
      /* initialize glyph loader */
      FT_GlyphLoader_Rewind( glyph->loader );
  
      glyph->num_subs = 0;
  
      /* load the glyph, recursively when needed */
      return pfr_glyph_load_rec( glyph, stream, gps_offset, offset, size );
    }
  
  
  /* END */
  