shithub: freetype+ttf2subf

ref: 609e28c3be484792936271dacefe9b48c378e7eb
dir: /src/pfr/pfrgload.c/

View raw version
#include "pfrgload.h"
#include "pfrload.h"  /* for macro definitions */
#include FT_INTERNAL_DEBUG_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;
    glyph->num_x_control  = 0;
    glyph->num_y_control  = 0;

    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 than 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've begun a new path */
    FT_ASSERT( glyph->path_begun != 0 );

    error = FT_GlyphLoader_CheckPoints( loader, 1, 0 );
    if ( !error )
    {
      FT_UInt  n = outline->n_points;

      outline->points[n] = *to;
      outline->tags  [n] = FT_Curve_Tag_On;

      outline->n_points++;
    }

    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've begun a new path */
    FT_ASSERT( glyph->path_begun != 0 );

    error = FT_GlyphLoader_CheckPoints( 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 );
    }

    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 room for a new contour and a new point */
    error = FT_GlyphLoader_CheckPoints( 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 = 0;
    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 */
    FT_ASSERT( (flags & PFR_GLYPH_IS_COMPOUND) == 0 );

    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 = (count+7) & -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;
    }

    /* XXXX: for now we ignore the secondary stroke and edge definitions */
    /*       since we don't want to support native PFR hinting..         */
    /*                                                                   */
    if ( flags & PFR_GLYPH_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_Int  format, args_format = 0, args_count, n;

        /***************************************************************/
        /*  read instruction                                           */
        /*                                                             */
        PFR_CHECK(1);
        format = PFR_NEXT_BYTE(p);
        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 & 15;
            args_count  = 1;
            break;

          case 2:    /* horizontal line to */
            FT_TRACE6(( "- horizontal line to cx.%d", format & 15 ));
            pos[0].y   = pos[3].y;
            pos[0].x   = glyph->x_control[ format & 15 ];
            pos[3]     = pos[0];
            args_count = 0;
            break;

          case 3:    /* vertical line to */
            FT_TRACE6(( "- vertical line to cy.%d", format & 15 ));
            pos[0].x   = pos[3].x;
            pos[0].y   = glyph->y_control[ format & 15 ];
            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 & 15);
        }

        /***********************************************************/
        /*  now read arguments                                     */
        /*                                                         */
        cur = pos;
        for ( n = 0; n < args_count; n++ )
        {
          FT_Int  index, delta;

          /* read the X argument */
          switch ( args_format & 3 )
          {
            case 0:  /* 8-bit index */
              PFR_CHECK(1);
              index  = PFR_NEXT_BYTE(p);
              cur->x = glyph->x_control[index];
              FT_TRACE7(( " cx#%d", index ));
              break;

            case 1:  /* 16-bit 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);
              index  = PFR_NEXT_BYTE(p);
              cur->y = glyph->y_control[index];
              FT_TRACE7(( " cy#%d", index ));
              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;

  Too_Short:
    error = FT_Err_Invalid_Table;
    FT_ERROR(( "pfr_glyph_load: 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  = 0;
    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 */
    FT_ASSERT( (flags & PFR_GLYPH_IS_COMPOUND) != 0 );

    count = flags & 0x3F;

    /* ignore extra items when present */
    /*                                 */
    if ( flags & PFR_GLYPH_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 so damn stupid that it uses direct file offsets */
    /* to point to the sub-glyphs (instead of glyph indices)..           */
    /*                                                                   */
    /* for now, we'll load the list of sub-glyphs in 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 ) & -4;

      if ( FT_RENEW_ARRAY( glyph->subs, glyph->max_subs, new_max ) )
        goto Exit;

      glyph->max_subs = new_max;
    }

    subglyph = glyph->subs + org_count;
    x_pos    = 0;
    y_pos    = 0;

    for ( i = 0; i < count; i++, subglyph++ )
    {
      FT_UInt  format;

      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) << 4;
      }

      subglyph->y_scale = 0x10000L;
      if ( format & PFR_SUBGLYPH_YSCALE )
      {
        PFR_CHECK(2);
        subglyph->y_scale = PFR_NEXT_SHORT(p) << 4;
      }

      /* 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_LONG(p);
      }
      else
      {
        PFR_CHECK(2);
        subglyph->gps_offset = PFR_NEXT_USHORT(p);
      }

      glyph->num_subs++;
    }


  Exit:
    return error;

  Too_Short:
    error = FT_Err_Invalid_Table;
    FT_ERROR(( "pfr_glyph_load: 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_Int          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;

      /* now, load each individual glyph */
      for ( n = 0; n < count; n++ )
      {
        FT_Int        i, old_points, num_points;
        PFR_SubGlyph  subglyph;


        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 )
          goto Exit;

        /* note that the '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 */
      }
    }
    else
    {
      /* 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 );

    /* load the glyph, recursively when needed */
    return pfr_glyph_load_rec( glyph, stream, gps_offset, offset, size );
  }