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