tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

pfrgload.c (21886B)


      1 /****************************************************************************
      2 *
      3 * pfrgload.c
      4 *
      5 *   FreeType PFR glyph loader (body).
      6 *
      7 * Copyright (C) 2002-2025 by
      8 * David Turner, Robert Wilhelm, and Werner Lemberg.
      9 *
     10 * This file is part of the FreeType project, and may only be used,
     11 * modified, and distributed under the terms of the FreeType project
     12 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
     13 * this file you indicate that you have read the license and
     14 * understand and accept it fully.
     15 *
     16 */
     17 
     18 
     19 #include "pfrgload.h"
     20 #include "pfrsbit.h"
     21 #include "pfrload.h"            /* for macro definitions */
     22 #include <freetype/internal/ftdebug.h>
     23 
     24 #include "pfrerror.h"
     25 
     26 #undef  FT_COMPONENT
     27 #define FT_COMPONENT  pfr
     28 
     29 
     30  /*************************************************************************/
     31  /*************************************************************************/
     32  /*****                                                               *****/
     33  /*****                      PFR GLYPH BUILDER                        *****/
     34  /*****                                                               *****/
     35  /*************************************************************************/
     36  /*************************************************************************/
     37 
     38 
     39  FT_LOCAL_DEF( void )
     40  pfr_glyph_init( PFR_Glyph       glyph,
     41                  FT_GlyphLoader  loader )
     42  {
     43    FT_ZERO( glyph );
     44 
     45    glyph->loader = loader;
     46 
     47    FT_GlyphLoader_Rewind( loader );
     48  }
     49 
     50 
     51  FT_LOCAL_DEF( void )
     52  pfr_glyph_done( PFR_Glyph  glyph )
     53  {
     54    FT_Memory  memory = glyph->loader->memory;
     55 
     56 
     57    FT_FREE( glyph->x_control );
     58    glyph->y_control = NULL;
     59 
     60    glyph->max_xy_control = 0;
     61 #if 0
     62    glyph->num_x_control  = 0;
     63    glyph->num_y_control  = 0;
     64 #endif
     65 
     66    FT_FREE( glyph->subs );
     67 
     68    glyph->max_subs = 0;
     69    glyph->num_subs = 0;
     70 
     71    glyph->loader     = NULL;
     72    glyph->path_begun = 0;
     73  }
     74 
     75 
     76  /* close current contour, if any */
     77  static void
     78  pfr_glyph_close_contour( PFR_Glyph  glyph )
     79  {
     80    FT_GlyphLoader  loader  = glyph->loader;
     81    FT_Outline*     outline = &loader->current.outline;
     82    FT_Int          last, first;
     83 
     84 
     85    if ( !glyph->path_begun )
     86      return;
     87 
     88    /* compute first and last point indices in current glyph outline */
     89    last  = outline->n_points - 1;
     90    first = 0;
     91    if ( outline->n_contours > 0 )
     92      first = outline->contours[outline->n_contours - 1];
     93 
     94    /* if the last point falls on the same location as the first one */
     95    /* we need to delete it                                          */
     96    if ( last > first )
     97    {
     98      FT_Vector*  p1 = outline->points + first;
     99      FT_Vector*  p2 = outline->points + last;
    100 
    101 
    102      if ( p1->x == p2->x && p1->y == p2->y )
    103      {
    104        outline->n_points--;
    105        last--;
    106      }
    107    }
    108 
    109    /* don't add empty contours */
    110    if ( last >= first )
    111      outline->contours[outline->n_contours++] = (FT_UShort)last;
    112 
    113    glyph->path_begun = 0;
    114  }
    115 
    116 
    117  /* reset glyph to start the loading of a new glyph */
    118  static void
    119  pfr_glyph_start( PFR_Glyph  glyph )
    120  {
    121    glyph->path_begun = 0;
    122  }
    123 
    124 
    125  static FT_Error
    126  pfr_glyph_line_to( PFR_Glyph   glyph,
    127                     FT_Vector*  to )
    128  {
    129    FT_GlyphLoader  loader  = glyph->loader;
    130    FT_Outline*     outline = &loader->current.outline;
    131    FT_Error        error;
    132 
    133 
    134    /* check that we have begun a new path */
    135    if ( !glyph->path_begun )
    136    {
    137      error = FT_THROW( Invalid_Table );
    138      FT_ERROR(( "pfr_glyph_line_to: invalid glyph data\n" ));
    139      goto Exit;
    140    }
    141 
    142    error = FT_GLYPHLOADER_CHECK_POINTS( loader, 1, 0 );
    143    if ( !error )
    144    {
    145      FT_Int  n = outline->n_points;
    146 
    147 
    148      outline->points[n] = *to;
    149      outline->tags  [n] = FT_CURVE_TAG_ON;
    150 
    151      outline->n_points++;
    152    }
    153 
    154  Exit:
    155    return error;
    156  }
    157 
    158 
    159  static FT_Error
    160  pfr_glyph_curve_to( PFR_Glyph   glyph,
    161                      FT_Vector*  control1,
    162                      FT_Vector*  control2,
    163                      FT_Vector*  to )
    164  {
    165    FT_GlyphLoader  loader  = glyph->loader;
    166    FT_Outline*     outline = &loader->current.outline;
    167    FT_Error        error;
    168 
    169 
    170    /* check that we have begun a new path */
    171    if ( !glyph->path_begun )
    172    {
    173      error = FT_THROW( Invalid_Table );
    174      FT_ERROR(( "pfr_glyph_line_to: invalid glyph data\n" ));
    175      goto Exit;
    176    }
    177 
    178    error = FT_GLYPHLOADER_CHECK_POINTS( loader, 3, 0 );
    179    if ( !error )
    180    {
    181      FT_Vector*  vec = outline->points + outline->n_points;
    182      FT_Byte*    tag = outline->tags   + outline->n_points;
    183 
    184 
    185      vec[0] = *control1;
    186      vec[1] = *control2;
    187      vec[2] = *to;
    188      tag[0] = FT_CURVE_TAG_CUBIC;
    189      tag[1] = FT_CURVE_TAG_CUBIC;
    190      tag[2] = FT_CURVE_TAG_ON;
    191 
    192      outline->n_points += 3;
    193    }
    194 
    195  Exit:
    196    return error;
    197  }
    198 
    199 
    200  static FT_Error
    201  pfr_glyph_move_to( PFR_Glyph   glyph,
    202                     FT_Vector*  to )
    203  {
    204    FT_GlyphLoader  loader  = glyph->loader;
    205    FT_Error        error;
    206 
    207 
    208    /* close current contour if any */
    209    pfr_glyph_close_contour( glyph );
    210 
    211    /* indicate that a new contour has started */
    212    glyph->path_begun = 1;
    213 
    214    /* check that there is space for a new contour and a new point */
    215    error = FT_GLYPHLOADER_CHECK_POINTS( loader, 1, 1 );
    216    if ( !error )
    217    {
    218      /* add new start point */
    219      error = pfr_glyph_line_to( glyph, to );
    220    }
    221 
    222    return error;
    223  }
    224 
    225 
    226  static void
    227  pfr_glyph_end( PFR_Glyph  glyph )
    228  {
    229    /* close current contour if any */
    230    pfr_glyph_close_contour( glyph );
    231 
    232    /* merge the current glyph into the stack */
    233    FT_GlyphLoader_Add( glyph->loader );
    234  }
    235 
    236 
    237  /*************************************************************************/
    238  /*************************************************************************/
    239  /*****                                                               *****/
    240  /*****                      PFR GLYPH LOADER                         *****/
    241  /*****                                                               *****/
    242  /*************************************************************************/
    243  /*************************************************************************/
    244 
    245 
    246  /* load a simple glyph */
    247  static FT_Error
    248  pfr_glyph_load_simple( PFR_Glyph  glyph,
    249                         FT_Byte*   p,
    250                         FT_Byte*   limit )
    251  {
    252    FT_Error   error  = FT_Err_Ok;
    253    FT_Memory  memory = glyph->loader->memory;
    254    FT_UInt    flags, x_count, y_count, i, count, mask;
    255    FT_Int     x;
    256 
    257 
    258    PFR_CHECK( 1 );
    259    flags = PFR_NEXT_BYTE( p );
    260 
    261    /* test for composite glyphs */
    262    if ( flags & PFR_GLYPH_IS_COMPOUND )
    263      goto Failure;
    264 
    265    x_count = 0;
    266    y_count = 0;
    267 
    268    if ( flags & PFR_GLYPH_1BYTE_XYCOUNT )
    269    {
    270      PFR_CHECK( 1 );
    271      count   = PFR_NEXT_BYTE( p );
    272      x_count = count & 15;
    273      y_count = count >> 4;
    274    }
    275    else
    276    {
    277      if ( flags & PFR_GLYPH_XCOUNT )
    278      {
    279        PFR_CHECK( 1 );
    280        x_count = PFR_NEXT_BYTE( p );
    281      }
    282 
    283      if ( flags & PFR_GLYPH_YCOUNT )
    284      {
    285        PFR_CHECK( 1 );
    286        y_count = PFR_NEXT_BYTE( p );
    287      }
    288    }
    289 
    290    count = x_count + y_count;
    291 
    292    /* re-allocate array when necessary */
    293    if ( count > glyph->max_xy_control )
    294    {
    295      FT_UInt  new_max = FT_PAD_CEIL( count, 8 );
    296 
    297 
    298      if ( FT_RENEW_ARRAY( glyph->x_control,
    299                           glyph->max_xy_control,
    300                           new_max ) )
    301        goto Exit;
    302 
    303      glyph->max_xy_control = new_max;
    304    }
    305 
    306    glyph->y_control = glyph->x_control + x_count;
    307 
    308    mask = 0;
    309    x    = 0;
    310 
    311    for ( i = 0; i < count; i++ )
    312    {
    313      if ( ( i & 7 ) == 0 )
    314      {
    315        PFR_CHECK( 1 );
    316        mask = PFR_NEXT_BYTE( p );
    317      }
    318 
    319      if ( mask & 1 )
    320      {
    321        PFR_CHECK( 2 );
    322        x = PFR_NEXT_SHORT( p );
    323      }
    324      else
    325      {
    326        PFR_CHECK( 1 );
    327        x += PFR_NEXT_BYTE( p );
    328      }
    329 
    330      glyph->x_control[i] = x;
    331 
    332      mask >>= 1;
    333    }
    334 
    335    /* XXX: we ignore the secondary stroke and edge definitions */
    336    /*      since we don't support native PFR hinting           */
    337    /*                                                          */
    338    if ( flags & PFR_GLYPH_SINGLE_EXTRA_ITEMS )
    339    {
    340      error = pfr_extra_items_skip( &p, limit );
    341      if ( error )
    342        goto Exit;
    343    }
    344 
    345    pfr_glyph_start( glyph );
    346 
    347    /* now load a simple glyph */
    348    {
    349      FT_Vector   pos[4];
    350      FT_Vector*  cur;
    351 
    352 
    353      pos[0].x = pos[0].y = 0;
    354      pos[3]   = pos[0];
    355 
    356      for (;;)
    357      {
    358        FT_UInt  format, format_low, args_format = 0, args_count, n;
    359 
    360 
    361        /****************************************************************
    362         * read instruction
    363         */
    364        PFR_CHECK( 1 );
    365        format     = PFR_NEXT_BYTE( p );
    366        format_low = format & 15;
    367 
    368        switch ( format >> 4 )
    369        {
    370        case 0:                                               /* end glyph */
    371          FT_TRACE6(( "- end glyph" ));
    372          args_count = 0;
    373          break;
    374 
    375        case 1:                                  /* general line operation */
    376          FT_TRACE6(( "- general line" ));
    377          goto Line1;
    378 
    379        case 4:                                 /* move to inside contour  */
    380          FT_TRACE6(( "- move to inside" ));
    381          goto Line1;
    382 
    383        case 5:                                 /* move to outside contour */
    384          FT_TRACE6(( "- move to outside" ));
    385        Line1:
    386          args_format = format_low;
    387          args_count  = 1;
    388          break;
    389 
    390        case 2:                                      /* horizontal line to */
    391          FT_TRACE6(( "- horizontal line to cx.%u", format_low ));
    392          if ( format_low >= x_count )
    393            goto Failure;
    394          pos[0].x   = glyph->x_control[format_low];
    395          pos[0].y   = pos[3].y;
    396          pos[3]     = pos[0];
    397          args_count = 0;
    398          break;
    399 
    400        case 3:                                        /* vertical line to */
    401          FT_TRACE6(( "- vertical line to cy.%u", format_low ));
    402          if ( format_low >= y_count )
    403            goto Failure;
    404          pos[0].x   = pos[3].x;
    405          pos[0].y   = glyph->y_control[format_low];
    406          pos[3]     = pos[0];
    407          args_count = 0;
    408          break;
    409 
    410        case 6:                            /* horizontal to vertical curve */
    411          FT_TRACE6(( "- hv curve" ));
    412          args_format = 0xB8E;
    413          args_count  = 3;
    414          break;
    415 
    416        case 7:                            /* vertical to horizontal curve */
    417          FT_TRACE6(( "- vh curve" ));
    418          args_format = 0xE2B;
    419          args_count  = 3;
    420          break;
    421 
    422        default:                                       /* general curve to */
    423          FT_TRACE6(( "- general curve" ));
    424          args_count  = 4;
    425          args_format = format_low;
    426        }
    427 
    428        /************************************************************
    429         * now read arguments
    430         */
    431        cur = pos;
    432        for ( n = 0; n < args_count; n++ )
    433        {
    434          FT_UInt  idx;
    435          FT_Int   delta;
    436 
    437 
    438          /* read the X argument */
    439          switch ( args_format & 3 )
    440          {
    441          case 0:                           /* 8-bit index */
    442            PFR_CHECK( 1 );
    443            idx = PFR_NEXT_BYTE( p );
    444            if ( idx >= x_count )
    445              goto Failure;
    446            cur->x = glyph->x_control[idx];
    447            FT_TRACE7(( " cx#%u", idx ));
    448            break;
    449 
    450          case 1:                           /* 16-bit absolute value */
    451            PFR_CHECK( 2 );
    452            cur->x = PFR_NEXT_SHORT( p );
    453            FT_TRACE7(( " x.%ld", cur->x ));
    454            break;
    455 
    456          case 2:                           /* 8-bit delta */
    457            PFR_CHECK( 1 );
    458            delta  = PFR_NEXT_INT8( p );
    459            cur->x = pos[3].x + delta;
    460            FT_TRACE7(( " dx.%d", delta ));
    461            break;
    462 
    463          default:
    464            FT_TRACE7(( " |" ));
    465            cur->x = pos[3].x;
    466          }
    467 
    468          /* read the Y argument */
    469          switch ( ( args_format >> 2 ) & 3 )
    470          {
    471          case 0:                           /* 8-bit index */
    472            PFR_CHECK( 1 );
    473            idx  = PFR_NEXT_BYTE( p );
    474            if ( idx >= y_count )
    475              goto Failure;
    476            cur->y = glyph->y_control[idx];
    477            FT_TRACE7(( " cy#%u", idx ));
    478            break;
    479 
    480          case 1:                           /* 16-bit absolute value */
    481            PFR_CHECK( 2 );
    482            cur->y = PFR_NEXT_SHORT( p );
    483            FT_TRACE7(( " y.%ld", cur->y ));
    484            break;
    485 
    486          case 2:                           /* 8-bit delta */
    487            PFR_CHECK( 1 );
    488            delta  = PFR_NEXT_INT8( p );
    489            cur->y = pos[3].y + delta;
    490            FT_TRACE7(( " dy.%d", delta ));
    491            break;
    492 
    493          default:
    494            FT_TRACE7(( " -" ));
    495            cur->y = pos[3].y;
    496          }
    497 
    498          /* read the additional format flag for the general curve */
    499          if ( n == 0 && args_count == 4 )
    500          {
    501            PFR_CHECK( 1 );
    502            args_format = PFR_NEXT_BYTE( p );
    503            args_count--;
    504          }
    505          else
    506            args_format >>= 4;
    507 
    508          /* save the previous point */
    509          pos[3] = cur[0];
    510          cur++;
    511        }
    512 
    513        FT_TRACE7(( "\n" ));
    514 
    515        /************************************************************
    516         * finally, execute instruction
    517         */
    518        switch ( format >> 4 )
    519        {
    520        case 0:                                       /* end glyph => EXIT */
    521          pfr_glyph_end( glyph );
    522          goto Exit;
    523 
    524        case 1:                                         /* line operations */
    525        case 2:
    526        case 3:
    527          error = pfr_glyph_line_to( glyph, pos );
    528          goto Test_Error;
    529 
    530        case 4:                                 /* move to inside contour  */
    531        case 5:                                 /* move to outside contour */
    532          error = pfr_glyph_move_to( glyph, pos );
    533          goto Test_Error;
    534 
    535        default:                                       /* curve operations */
    536          error = pfr_glyph_curve_to( glyph, pos, pos + 1, pos + 2 );
    537 
    538        Test_Error:  /* test error condition */
    539          if ( error )
    540            goto Exit;
    541        }
    542      } /* for (;;) */
    543    }
    544 
    545  Exit:
    546    return error;
    547 
    548  Failure:
    549  Too_Short:
    550    error = FT_THROW( Invalid_Table );
    551    FT_ERROR(( "pfr_glyph_load_simple: invalid glyph data\n" ));
    552    goto Exit;
    553  }
    554 
    555 
    556  /* load a composite/compound glyph */
    557  static FT_Error
    558  pfr_glyph_load_compound( PFR_Glyph  glyph,
    559                           FT_Byte*   p,
    560                           FT_Byte*   limit )
    561  {
    562    FT_Error        error  = FT_Err_Ok;
    563    FT_Memory       memory = glyph->loader->memory;
    564    PFR_SubGlyph    subglyph;
    565    FT_UInt         flags, i, count, org_count;
    566    FT_Int          x_pos, y_pos;
    567 
    568 
    569    PFR_CHECK( 1 );
    570    flags = PFR_NEXT_BYTE( p );
    571 
    572    /* test for composite glyphs */
    573    if ( !( flags & PFR_GLYPH_IS_COMPOUND ) )
    574      goto Failure;
    575 
    576    count = flags & 0x3F;
    577 
    578    /* ignore extra items when present */
    579    /*                                 */
    580    if ( flags & PFR_GLYPH_COMPOUND_EXTRA_ITEMS )
    581    {
    582      error = pfr_extra_items_skip( &p, limit );
    583      if ( error )
    584        goto Exit;
    585    }
    586 
    587    /* we can't rely on the FT_GlyphLoader to load sub-glyphs, because   */
    588    /* the PFR format is dumb, using direct file offsets to point to the */
    589    /* sub-glyphs (instead of glyph indices).  Sigh.                     */
    590    /*                                                                   */
    591    /* For now, we load the list of sub-glyphs into a different array    */
    592    /* but this will prevent us from using the auto-hinter at its best   */
    593    /* quality.                                                          */
    594    /*                                                                   */
    595    org_count = glyph->num_subs;
    596 
    597    if ( org_count + count > glyph->max_subs )
    598    {
    599      FT_UInt  new_max = ( org_count + count + 3 ) & (FT_UInt)-4;
    600 
    601 
    602      /* we arbitrarily limit the number of subglyphs */
    603      /* to avoid endless recursion                   */
    604      if ( new_max > 64 )
    605      {
    606        error = FT_THROW( Invalid_Table );
    607        FT_ERROR(( "pfr_glyph_load_compound:"
    608                   " too many compound glyphs components\n" ));
    609        goto Exit;
    610      }
    611 
    612      if ( FT_RENEW_ARRAY( glyph->subs, glyph->max_subs, new_max ) )
    613        goto Exit;
    614 
    615      glyph->max_subs = new_max;
    616    }
    617 
    618    subglyph = glyph->subs + org_count;
    619 
    620    for ( i = 0; i < count; i++, subglyph++ )
    621    {
    622      FT_UInt  format;
    623 
    624 
    625      x_pos = 0;
    626      y_pos = 0;
    627 
    628      PFR_CHECK( 1 );
    629      format = PFR_NEXT_BYTE( p );
    630 
    631      /* read scale when available */
    632      subglyph->x_scale = 0x10000L;
    633      if ( format & PFR_SUBGLYPH_XSCALE )
    634      {
    635        PFR_CHECK( 2 );
    636        subglyph->x_scale = PFR_NEXT_SHORT( p ) * 16;
    637      }
    638 
    639      subglyph->y_scale = 0x10000L;
    640      if ( format & PFR_SUBGLYPH_YSCALE )
    641      {
    642        PFR_CHECK( 2 );
    643        subglyph->y_scale = PFR_NEXT_SHORT( p ) * 16;
    644      }
    645 
    646      /* read offset */
    647      switch ( format & 3 )
    648      {
    649      case 1:
    650        PFR_CHECK( 2 );
    651        x_pos = PFR_NEXT_SHORT( p );
    652        break;
    653 
    654      case 2:
    655        PFR_CHECK( 1 );
    656        x_pos += PFR_NEXT_INT8( p );
    657        break;
    658 
    659      default:
    660        ;
    661      }
    662 
    663      switch ( ( format >> 2 ) & 3 )
    664      {
    665      case 1:
    666        PFR_CHECK( 2 );
    667        y_pos = PFR_NEXT_SHORT( p );
    668        break;
    669 
    670      case 2:
    671        PFR_CHECK( 1 );
    672        y_pos += PFR_NEXT_INT8( p );
    673        break;
    674 
    675      default:
    676        ;
    677      }
    678 
    679      subglyph->x_delta = x_pos;
    680      subglyph->y_delta = y_pos;
    681 
    682      /* read glyph position and size now */
    683      if ( format & PFR_SUBGLYPH_2BYTE_SIZE )
    684      {
    685        PFR_CHECK( 2 );
    686        subglyph->gps_size = PFR_NEXT_USHORT( p );
    687      }
    688      else
    689      {
    690        PFR_CHECK( 1 );
    691        subglyph->gps_size = PFR_NEXT_BYTE( p );
    692      }
    693 
    694      if ( format & PFR_SUBGLYPH_3BYTE_OFFSET )
    695      {
    696        PFR_CHECK( 3 );
    697        subglyph->gps_offset = PFR_NEXT_ULONG( p );
    698      }
    699      else
    700      {
    701        PFR_CHECK( 2 );
    702        subglyph->gps_offset = PFR_NEXT_USHORT( p );
    703      }
    704 
    705      glyph->num_subs++;
    706    }
    707 
    708  Exit:
    709    return error;
    710 
    711  Failure:
    712  Too_Short:
    713    error = FT_THROW( Invalid_Table );
    714    FT_ERROR(( "pfr_glyph_load_compound: invalid glyph data\n" ));
    715    goto Exit;
    716  }
    717 
    718 
    719  static FT_Error
    720  pfr_glyph_load_rec( PFR_Glyph  glyph,
    721                      FT_Stream  stream,
    722                      FT_ULong   gps_offset,
    723                      FT_ULong   offset,
    724                      FT_ULong   size )
    725  {
    726    FT_Error  error;
    727    FT_Byte*  p;
    728    FT_Byte*  limit;
    729 
    730 
    731    if ( FT_STREAM_SEEK( gps_offset + offset ) ||
    732         FT_FRAME_ENTER( size )                )
    733      goto Exit;
    734 
    735    p     = (FT_Byte*)stream->cursor;
    736    limit = p + size;
    737 
    738    if ( size > 0 && *p & PFR_GLYPH_IS_COMPOUND )
    739    {
    740      FT_UInt         n, old_count, count;
    741      FT_GlyphLoader  loader = glyph->loader;
    742      FT_Outline*     base   = &loader->base.outline;
    743 
    744 
    745      old_count = glyph->num_subs;
    746 
    747      /* this is a compound glyph - load it */
    748      error = pfr_glyph_load_compound( glyph, p, limit );
    749 
    750      FT_FRAME_EXIT();
    751 
    752      if ( error )
    753        goto Exit;
    754 
    755      count = glyph->num_subs - old_count;
    756 
    757      FT_TRACE4(( "compound glyph with %u element%s (offset %lu):\n",
    758                  count,
    759                  count == 1 ? "" : "s",
    760                  offset ));
    761 
    762      /* now, load each individual glyph */
    763      for ( n = 0; n < count; n++ )
    764      {
    765        FT_Int        i, old_points, num_points;
    766        PFR_SubGlyph  subglyph;
    767 
    768 
    769        FT_TRACE4(( "  subglyph %u:\n", n ));
    770 
    771        subglyph   = glyph->subs + old_count + n;
    772        old_points = base->n_points;
    773 
    774        error = pfr_glyph_load_rec( glyph, stream, gps_offset,
    775                                    subglyph->gps_offset,
    776                                    subglyph->gps_size );
    777        if ( error )
    778          break;
    779 
    780        /* note that `glyph->subs' might have been re-allocated */
    781        subglyph   = glyph->subs + old_count + n;
    782        num_points = base->n_points - old_points;
    783 
    784        /* translate and eventually scale the new glyph points */
    785        if ( subglyph->x_scale != 0x10000L || subglyph->y_scale != 0x10000L )
    786        {
    787          FT_Vector*  vec = base->points + old_points;
    788 
    789 
    790          for ( i = 0; i < num_points; i++, vec++ )
    791          {
    792            vec->x = FT_MulFix( vec->x, subglyph->x_scale ) +
    793                       subglyph->x_delta;
    794            vec->y = FT_MulFix( vec->y, subglyph->y_scale ) +
    795                       subglyph->y_delta;
    796          }
    797        }
    798        else
    799        {
    800          FT_Vector*  vec = loader->base.outline.points + old_points;
    801 
    802 
    803          for ( i = 0; i < num_points; i++, vec++ )
    804          {
    805            vec->x += subglyph->x_delta;
    806            vec->y += subglyph->y_delta;
    807          }
    808        }
    809 
    810        /* proceed to next sub-glyph */
    811      }
    812 
    813      FT_TRACE4(( "end compound glyph with %u element%s\n",
    814                  count,
    815                  count == 1 ? "" : "s" ));
    816    }
    817    else
    818    {
    819      FT_TRACE4(( "simple glyph (offset %lu)\n", offset ));
    820 
    821      /* load a simple glyph */
    822      error = pfr_glyph_load_simple( glyph, p, limit );
    823 
    824      FT_FRAME_EXIT();
    825    }
    826 
    827  Exit:
    828    return error;
    829  }
    830 
    831 
    832  FT_LOCAL_DEF( FT_Error )
    833  pfr_glyph_load( PFR_Glyph  glyph,
    834                  FT_Stream  stream,
    835                  FT_ULong   gps_offset,
    836                  FT_ULong   offset,
    837                  FT_ULong   size )
    838  {
    839    /* initialize glyph loader */
    840    FT_GlyphLoader_Rewind( glyph->loader );
    841 
    842    glyph->num_subs = 0;
    843 
    844    /* load the glyph, recursively when needed */
    845    return pfr_glyph_load_rec( glyph, stream, gps_offset, offset, size );
    846  }
    847 
    848 
    849 /* END */