Home | History | Annotate | Download | only in cff
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  cf2ft.c                                                                */
      4 /*                                                                         */
      5 /*    FreeType Glue Component to Adobe's Interpreter (body).               */
      6 /*                                                                         */
      7 /*  Copyright 2013-2014 Adobe Systems Incorporated.                        */
      8 /*                                                                         */
      9 /*  This software, and all works of authorship, whether in source or       */
     10 /*  object code form as indicated by the copyright notice(s) included      */
     11 /*  herein (collectively, the "Work") is made available, and may only be   */
     12 /*  used, modified, and distributed under the FreeType Project License,    */
     13 /*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
     14 /*  FreeType Project License, each contributor to the Work hereby grants   */
     15 /*  to any individual or legal entity exercising permissions granted by    */
     16 /*  the FreeType Project License and this section (hereafter, "You" or     */
     17 /*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
     18 /*  royalty-free, irrevocable (except as stated in this section) patent    */
     19 /*  license to make, have made, use, offer to sell, sell, import, and      */
     20 /*  otherwise transfer the Work, where such license applies only to those  */
     21 /*  patent claims licensable by such contributor that are necessarily      */
     22 /*  infringed by their contribution(s) alone or by combination of their    */
     23 /*  contribution(s) with the Work to which such contribution(s) was        */
     24 /*  submitted.  If You institute patent litigation against any entity      */
     25 /*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
     26 /*  the Work or a contribution incorporated within the Work constitutes    */
     27 /*  direct or contributory patent infringement, then any patent licenses   */
     28 /*  granted to You under this License for that Work shall terminate as of  */
     29 /*  the date such litigation is filed.                                     */
     30 /*                                                                         */
     31 /*  By using, modifying, or distributing the Work you indicate that you    */
     32 /*  have read and understood the terms and conditions of the               */
     33 /*  FreeType Project License as well as those provided in this section,    */
     34 /*  and you accept them fully.                                             */
     35 /*                                                                         */
     36 /***************************************************************************/
     37 
     38 
     39 #include "cf2ft.h"
     40 #include FT_INTERNAL_DEBUG_H
     41 
     42 #include "cf2font.h"
     43 #include "cf2error.h"
     44 
     45 
     46 #define CF2_MAX_SIZE  cf2_intToFixed( 2000 )    /* max ppem */
     47 
     48 
     49   /*
     50    * This check should avoid most internal overflow cases.  Clients should
     51    * generally respond to `Glyph_Too_Big' by getting a glyph outline
     52    * at EM size, scaling it and filling it as a graphics operation.
     53    *
     54    */
     55   static FT_Error
     56   cf2_checkTransform( const CF2_Matrix*  transform,
     57                       CF2_Int            unitsPerEm )
     58   {
     59     CF2_Fixed  maxScale;
     60 
     61 
     62     FT_ASSERT( unitsPerEm > 0 );
     63 
     64     if ( transform->a <= 0 || transform->d <= 0 )
     65       return FT_THROW( Invalid_Size_Handle );
     66 
     67     FT_ASSERT( transform->b == 0 && transform->c == 0 );
     68     FT_ASSERT( transform->tx == 0 && transform->ty == 0 );
     69 
     70     if ( unitsPerEm > 0x7FFF )
     71       return FT_THROW( Glyph_Too_Big );
     72 
     73     maxScale = FT_DivFix( CF2_MAX_SIZE, cf2_intToFixed( unitsPerEm ) );
     74 
     75     if ( transform->a > maxScale || transform->d > maxScale )
     76       return FT_THROW( Glyph_Too_Big );
     77 
     78     return FT_Err_Ok;
     79   }
     80 
     81 
     82   static void
     83   cf2_setGlyphWidth( CF2_Outline  outline,
     84                      CF2_Fixed    width )
     85   {
     86     CFF_Decoder*  decoder = outline->decoder;
     87 
     88 
     89     FT_ASSERT( decoder );
     90 
     91     decoder->glyph_width = cf2_fixedToInt( width );
     92   }
     93 
     94 
     95   /* Clean up font instance. */
     96   static void
     97   cf2_free_instance( void*  ptr )
     98   {
     99     CF2_Font  font = (CF2_Font)ptr;
    100 
    101 
    102     if ( font )
    103     {
    104       FT_Memory  memory = font->memory;
    105 
    106 
    107       (void)memory;
    108     }
    109   }
    110 
    111 
    112   /********************************************/
    113   /*                                          */
    114   /* functions for handling client outline;   */
    115   /* FreeType uses coordinates in 26.6 format */
    116   /*                                          */
    117   /********************************************/
    118 
    119   static void
    120   cf2_builder_moveTo( CF2_OutlineCallbacks      callbacks,
    121                       const CF2_CallbackParams  params )
    122   {
    123     /* downcast the object pointer */
    124     CF2_Outline   outline = (CF2_Outline)callbacks;
    125     CFF_Builder*  builder;
    126 
    127     (void)params;        /* only used in debug mode */
    128 
    129 
    130     FT_ASSERT( outline && outline->decoder );
    131     FT_ASSERT( params->op == CF2_PathOpMoveTo );
    132 
    133     builder = &outline->decoder->builder;
    134 
    135     /* note: two successive moves simply close the contour twice */
    136     cff_builder_close_contour( builder );
    137     builder->path_begun = 0;
    138   }
    139 
    140 
    141   static void
    142   cf2_builder_lineTo( CF2_OutlineCallbacks      callbacks,
    143                       const CF2_CallbackParams  params )
    144   {
    145     FT_Error  error;
    146 
    147     /* downcast the object pointer */
    148     CF2_Outline   outline = (CF2_Outline)callbacks;
    149     CFF_Builder*  builder;
    150 
    151 
    152     FT_ASSERT( outline && outline->decoder );
    153     FT_ASSERT( params->op == CF2_PathOpLineTo );
    154 
    155     builder = &outline->decoder->builder;
    156 
    157     if ( !builder->path_begun )
    158     {
    159       /* record the move before the line; also check points and set */
    160       /* `path_begun'                                               */
    161       error = cff_builder_start_point( builder,
    162                                        params->pt0.x,
    163                                        params->pt0.y );
    164       if ( error )
    165       {
    166         if ( !*callbacks->error )
    167           *callbacks->error =  error;
    168         return;
    169       }
    170     }
    171 
    172     /* `cff_builder_add_point1' includes a check_points call for one point */
    173     error = cff_builder_add_point1( builder,
    174                                     params->pt1.x,
    175                                     params->pt1.y );
    176     if ( error )
    177     {
    178       if ( !*callbacks->error )
    179         *callbacks->error =  error;
    180       return;
    181     }
    182   }
    183 
    184 
    185   static void
    186   cf2_builder_cubeTo( CF2_OutlineCallbacks      callbacks,
    187                       const CF2_CallbackParams  params )
    188   {
    189     FT_Error  error;
    190 
    191     /* downcast the object pointer */
    192     CF2_Outline   outline = (CF2_Outline)callbacks;
    193     CFF_Builder*  builder;
    194 
    195 
    196     FT_ASSERT( outline && outline->decoder );
    197     FT_ASSERT( params->op == CF2_PathOpCubeTo );
    198 
    199     builder = &outline->decoder->builder;
    200 
    201     if ( !builder->path_begun )
    202     {
    203       /* record the move before the line; also check points and set */
    204       /* `path_begun'                                               */
    205       error = cff_builder_start_point( builder,
    206                                        params->pt0.x,
    207                                        params->pt0.y );
    208       if ( error )
    209       {
    210         if ( !*callbacks->error )
    211           *callbacks->error =  error;
    212         return;
    213       }
    214     }
    215 
    216     /* prepare room for 3 points: 2 off-curve, 1 on-curve */
    217     error = cff_check_points( builder, 3 );
    218     if ( error )
    219     {
    220       if ( !*callbacks->error )
    221         *callbacks->error =  error;
    222       return;
    223     }
    224 
    225     cff_builder_add_point( builder,
    226                            params->pt1.x,
    227                            params->pt1.y, 0 );
    228     cff_builder_add_point( builder,
    229                            params->pt2.x,
    230                            params->pt2.y, 0 );
    231     cff_builder_add_point( builder,
    232                            params->pt3.x,
    233                            params->pt3.y, 1 );
    234   }
    235 
    236 
    237   static void
    238   cf2_outline_init( CF2_Outline  outline,
    239                     FT_Memory    memory,
    240                     FT_Error*    error )
    241   {
    242     FT_MEM_ZERO( outline, sizeof ( CF2_OutlineRec ) );
    243 
    244     outline->root.memory = memory;
    245     outline->root.error  = error;
    246 
    247     outline->root.moveTo = cf2_builder_moveTo;
    248     outline->root.lineTo = cf2_builder_lineTo;
    249     outline->root.cubeTo = cf2_builder_cubeTo;
    250   }
    251 
    252 
    253   /* get scaling and hint flag from GlyphSlot */
    254   static void
    255   cf2_getScaleAndHintFlag( CFF_Decoder*  decoder,
    256                            CF2_Fixed*    x_scale,
    257                            CF2_Fixed*    y_scale,
    258                            FT_Bool*      hinted,
    259                            FT_Bool*      scaled )
    260   {
    261     FT_ASSERT( decoder && decoder->builder.glyph );
    262 
    263     /* note: FreeType scale includes a factor of 64 */
    264     *hinted = decoder->builder.glyph->hint;
    265     *scaled = decoder->builder.glyph->scaled;
    266 
    267     if ( *hinted )
    268     {
    269       *x_scale = ( decoder->builder.glyph->x_scale + 32 ) / 64;
    270       *y_scale = ( decoder->builder.glyph->y_scale + 32 ) / 64;
    271     }
    272     else
    273     {
    274       /* for unhinted outlines, `cff_slot_load' does the scaling, */
    275       /* thus render at `unity' scale                             */
    276 
    277       *x_scale = 0x0400;   /* 1/64 as 16.16 */
    278       *y_scale = 0x0400;
    279     }
    280   }
    281 
    282 
    283   /* get units per em from `FT_Face' */
    284   /* TODO: should handle font matrix concatenation? */
    285   static FT_UShort
    286   cf2_getUnitsPerEm( CFF_Decoder*  decoder )
    287   {
    288     FT_ASSERT( decoder && decoder->builder.face );
    289     FT_ASSERT( decoder->builder.face->root.units_per_EM );
    290 
    291     return decoder->builder.face->root.units_per_EM;
    292   }
    293 
    294 
    295   /* Main entry point: Render one glyph. */
    296   FT_LOCAL_DEF( FT_Error )
    297   cf2_decoder_parse_charstrings( CFF_Decoder*  decoder,
    298                                  FT_Byte*      charstring_base,
    299                                  FT_ULong      charstring_len )
    300   {
    301     FT_Memory  memory;
    302     FT_Error   error = FT_Err_Ok;
    303     CF2_Font   font;
    304 
    305 
    306     FT_ASSERT( decoder && decoder->cff );
    307 
    308     memory = decoder->builder.memory;
    309 
    310     /* CF2 data is saved here across glyphs */
    311     font = (CF2_Font)decoder->cff->cf2_instance.data;
    312 
    313     /* on first glyph, allocate instance structure */
    314     if ( decoder->cff->cf2_instance.data == NULL )
    315     {
    316       decoder->cff->cf2_instance.finalizer =
    317         (FT_Generic_Finalizer)cf2_free_instance;
    318 
    319       if ( FT_ALLOC( decoder->cff->cf2_instance.data,
    320                      sizeof ( CF2_FontRec ) ) )
    321         return FT_THROW( Out_Of_Memory );
    322 
    323       font = (CF2_Font)decoder->cff->cf2_instance.data;
    324 
    325       font->memory = memory;
    326 
    327       /* initialize a client outline, to be shared by each glyph rendered */
    328       cf2_outline_init( &font->outline, font->memory, &font->error );
    329     }
    330 
    331     /* save decoder; it is a stack variable and will be different on each */
    332     /* call                                                               */
    333     font->decoder         = decoder;
    334     font->outline.decoder = decoder;
    335 
    336     {
    337       /* build parameters for Adobe engine */
    338 
    339       CFF_Builder*  builder = &decoder->builder;
    340       CFF_Driver    driver  = (CFF_Driver)FT_FACE_DRIVER( builder->face );
    341 
    342       /* local error */
    343       FT_Error       error2 = FT_Err_Ok;
    344       CF2_BufferRec  buf;
    345       CF2_Matrix     transform;
    346       CF2_F16Dot16   glyphWidth;
    347 
    348       FT_Bool  hinted;
    349       FT_Bool  scaled;
    350 
    351 
    352       /* FreeType has already looked up the GID; convert to         */
    353       /* `RegionBuffer', assuming that the input has been validated */
    354       FT_ASSERT( charstring_base + charstring_len >= charstring_base );
    355 
    356       FT_ZERO( &buf );
    357       buf.start =
    358       buf.ptr   = charstring_base;
    359       buf.end   = charstring_base + charstring_len;
    360 
    361       FT_ZERO( &transform );
    362 
    363       cf2_getScaleAndHintFlag( decoder,
    364                                &transform.a,
    365                                &transform.d,
    366                                &hinted,
    367                                &scaled );
    368 
    369       font->renderingFlags = 0;
    370       if ( hinted )
    371         font->renderingFlags |= CF2_FlagsHinted;
    372       if ( scaled && !driver->no_stem_darkening )
    373         font->renderingFlags |= CF2_FlagsDarkened;
    374 
    375       font->darkenParams[0] = driver->darken_params[0];
    376       font->darkenParams[1] = driver->darken_params[1];
    377       font->darkenParams[2] = driver->darken_params[2];
    378       font->darkenParams[3] = driver->darken_params[3];
    379       font->darkenParams[4] = driver->darken_params[4];
    380       font->darkenParams[5] = driver->darken_params[5];
    381       font->darkenParams[6] = driver->darken_params[6];
    382       font->darkenParams[7] = driver->darken_params[7];
    383 
    384       /* now get an outline for this glyph;      */
    385       /* also get units per em to validate scale */
    386       font->unitsPerEm = (CF2_Int)cf2_getUnitsPerEm( decoder );
    387 
    388       if ( scaled )
    389       {
    390         error2 = cf2_checkTransform( &transform, font->unitsPerEm );
    391         if ( error2 )
    392           return error2;
    393       }
    394 
    395       error2 = cf2_getGlyphOutline( font, &buf, &transform, &glyphWidth );
    396       if ( error2 )
    397         return FT_ERR( Invalid_File_Format );
    398 
    399       cf2_setGlyphWidth( &font->outline, glyphWidth );
    400 
    401       return FT_Err_Ok;
    402     }
    403   }
    404 
    405 
    406   /* get pointer to current FreeType subfont (based on current glyphID) */
    407   FT_LOCAL_DEF( CFF_SubFont )
    408   cf2_getSubfont( CFF_Decoder*  decoder )
    409   {
    410     FT_ASSERT( decoder && decoder->current_subfont );
    411 
    412     return decoder->current_subfont;
    413   }
    414 
    415 
    416   /* get `y_ppem' from `CFF_Size' */
    417   FT_LOCAL_DEF( CF2_Fixed )
    418   cf2_getPpemY( CFF_Decoder*  decoder )
    419   {
    420     FT_ASSERT( decoder                          &&
    421                decoder->builder.face            &&
    422                decoder->builder.face->root.size );
    423 
    424     /*
    425      * Note that `y_ppem' can be zero if there wasn't a call to
    426      * `FT_Set_Char_Size' or something similar.  However, this isn't a
    427      * problem since we come to this place in the code only if
    428      * FT_LOAD_NO_SCALE is set (the other case gets caught by
    429      * `cf2_checkTransform').  The ppem value is needed to compute the stem
    430      * darkening, which is disabled for getting the unscaled outline.
    431      *
    432      */
    433     return cf2_intToFixed(
    434              decoder->builder.face->root.size->metrics.y_ppem );
    435   }
    436 
    437 
    438   /* get standard stem widths for the current subfont; */
    439   /* FreeType stores these as integer font units       */
    440   /* (note: variable names seem swapped)               */
    441   FT_LOCAL_DEF( CF2_Fixed )
    442   cf2_getStdVW( CFF_Decoder*  decoder )
    443   {
    444     FT_ASSERT( decoder && decoder->current_subfont );
    445 
    446     return cf2_intToFixed(
    447              decoder->current_subfont->private_dict.standard_height );
    448   }
    449 
    450 
    451   FT_LOCAL_DEF( CF2_Fixed )
    452   cf2_getStdHW( CFF_Decoder*  decoder )
    453   {
    454     FT_ASSERT( decoder && decoder->current_subfont );
    455 
    456     return cf2_intToFixed(
    457              decoder->current_subfont->private_dict.standard_width );
    458   }
    459 
    460 
    461   /* note: FreeType stores 1000 times the actual value for `BlueScale' */
    462   FT_LOCAL_DEF( void )
    463   cf2_getBlueMetrics( CFF_Decoder*  decoder,
    464                       CF2_Fixed*    blueScale,
    465                       CF2_Fixed*    blueShift,
    466                       CF2_Fixed*    blueFuzz )
    467   {
    468     FT_ASSERT( decoder && decoder->current_subfont );
    469 
    470     *blueScale = FT_DivFix(
    471                    decoder->current_subfont->private_dict.blue_scale,
    472                    cf2_intToFixed( 1000 ) );
    473     *blueShift = cf2_intToFixed(
    474                    decoder->current_subfont->private_dict.blue_shift );
    475     *blueFuzz  = cf2_intToFixed(
    476                    decoder->current_subfont->private_dict.blue_fuzz );
    477   }
    478 
    479 
    480   /* get blue values counts and arrays; the FreeType parser has validated */
    481   /* the counts and verified that each is an even number                  */
    482   FT_LOCAL_DEF( void )
    483   cf2_getBlueValues( CFF_Decoder*  decoder,
    484                      size_t*       count,
    485                      FT_Pos*      *data )
    486   {
    487     FT_ASSERT( decoder && decoder->current_subfont );
    488 
    489     *count = decoder->current_subfont->private_dict.num_blue_values;
    490     *data  = (FT_Pos*)
    491                &decoder->current_subfont->private_dict.blue_values;
    492   }
    493 
    494 
    495   FT_LOCAL_DEF( void )
    496   cf2_getOtherBlues( CFF_Decoder*  decoder,
    497                      size_t*       count,
    498                      FT_Pos*      *data )
    499   {
    500     FT_ASSERT( decoder && decoder->current_subfont );
    501 
    502     *count = decoder->current_subfont->private_dict.num_other_blues;
    503     *data  = (FT_Pos*)
    504                &decoder->current_subfont->private_dict.other_blues;
    505   }
    506 
    507 
    508   FT_LOCAL_DEF( void )
    509   cf2_getFamilyBlues( CFF_Decoder*  decoder,
    510                       size_t*       count,
    511                       FT_Pos*      *data )
    512   {
    513     FT_ASSERT( decoder && decoder->current_subfont );
    514 
    515     *count = decoder->current_subfont->private_dict.num_family_blues;
    516     *data  = (FT_Pos*)
    517                &decoder->current_subfont->private_dict.family_blues;
    518   }
    519 
    520 
    521   FT_LOCAL_DEF( void )
    522   cf2_getFamilyOtherBlues( CFF_Decoder*  decoder,
    523                            size_t*       count,
    524                            FT_Pos*      *data )
    525   {
    526     FT_ASSERT( decoder && decoder->current_subfont );
    527 
    528     *count = decoder->current_subfont->private_dict.num_family_other_blues;
    529     *data  = (FT_Pos*)
    530                &decoder->current_subfont->private_dict.family_other_blues;
    531   }
    532 
    533 
    534   FT_LOCAL_DEF( CF2_Int )
    535   cf2_getLanguageGroup( CFF_Decoder*  decoder )
    536   {
    537     FT_ASSERT( decoder && decoder->current_subfont );
    538 
    539     return decoder->current_subfont->private_dict.language_group;
    540   }
    541 
    542 
    543   /* convert unbiased subroutine index to `CF2_Buffer' and */
    544   /* return 0 on success                                   */
    545   FT_LOCAL_DEF( CF2_Int )
    546   cf2_initGlobalRegionBuffer( CFF_Decoder*  decoder,
    547                               CF2_UInt      idx,
    548                               CF2_Buffer    buf )
    549   {
    550     FT_ASSERT( decoder );
    551 
    552     FT_ZERO( buf );
    553 
    554     idx += (CF2_UInt)decoder->globals_bias;
    555     if ( idx >= decoder->num_globals )
    556       return TRUE;     /* error */
    557 
    558     FT_ASSERT( decoder->globals );
    559 
    560     buf->start =
    561     buf->ptr   = decoder->globals[idx];
    562     buf->end   = decoder->globals[idx + 1];
    563 
    564     return FALSE;      /* success */
    565   }
    566 
    567 
    568   /* convert AdobeStandardEncoding code to CF2_Buffer; */
    569   /* used for seac component                           */
    570   FT_LOCAL_DEF( FT_Error )
    571   cf2_getSeacComponent( CFF_Decoder*  decoder,
    572                         CF2_Int       code,
    573                         CF2_Buffer    buf )
    574   {
    575     CF2_Int   gid;
    576     FT_Byte*  charstring;
    577     FT_ULong  len;
    578     FT_Error  error;
    579 
    580 
    581     FT_ASSERT( decoder );
    582 
    583     FT_ZERO( buf );
    584 
    585 #ifdef FT_CONFIG_OPTION_INCREMENTAL
    586     /* Incremental fonts don't necessarily have valid charsets.        */
    587     /* They use the character code, not the glyph index, in this case. */
    588     if ( decoder->builder.face->root.internal->incremental_interface )
    589       gid = code;
    590     else
    591 #endif /* FT_CONFIG_OPTION_INCREMENTAL */
    592     {
    593       gid = cff_lookup_glyph_by_stdcharcode( decoder->cff, code );
    594       if ( gid < 0 )
    595         return FT_THROW( Invalid_Glyph_Format );
    596     }
    597 
    598     error = cff_get_glyph_data( decoder->builder.face,
    599                                 (CF2_UInt)gid,
    600                                 &charstring,
    601                                 &len );
    602     /* TODO: for now, just pass the FreeType error through */
    603     if ( error )
    604       return error;
    605 
    606     /* assume input has been validated */
    607     FT_ASSERT( charstring + len >= charstring );
    608 
    609     buf->start = charstring;
    610     buf->end   = charstring + len;
    611     buf->ptr   = buf->start;
    612 
    613     return FT_Err_Ok;
    614   }
    615 
    616 
    617   FT_LOCAL_DEF( void )
    618   cf2_freeSeacComponent( CFF_Decoder*  decoder,
    619                          CF2_Buffer    buf )
    620   {
    621     FT_ASSERT( decoder );
    622 
    623     cff_free_glyph_data( decoder->builder.face,
    624                          (FT_Byte**)&buf->start,
    625                          (FT_ULong)( buf->end - buf->start ) );
    626   }
    627 
    628 
    629   FT_LOCAL_DEF( CF2_Int )
    630   cf2_initLocalRegionBuffer( CFF_Decoder*  decoder,
    631                              CF2_UInt      idx,
    632                              CF2_Buffer    buf )
    633   {
    634     FT_ASSERT( decoder );
    635 
    636     FT_ZERO( buf );
    637 
    638     idx += (CF2_UInt)decoder->locals_bias;
    639     if ( idx >= decoder->num_locals )
    640       return TRUE;     /* error */
    641 
    642     FT_ASSERT( decoder->locals );
    643 
    644     buf->start =
    645     buf->ptr   = decoder->locals[idx];
    646     buf->end   = decoder->locals[idx + 1];
    647 
    648     return FALSE;      /* success */
    649   }
    650 
    651 
    652   FT_LOCAL_DEF( CF2_Fixed )
    653   cf2_getDefaultWidthX( CFF_Decoder*  decoder )
    654   {
    655     FT_ASSERT( decoder && decoder->current_subfont );
    656 
    657     return cf2_intToFixed(
    658              decoder->current_subfont->private_dict.default_width );
    659   }
    660 
    661 
    662   FT_LOCAL_DEF( CF2_Fixed )
    663   cf2_getNominalWidthX( CFF_Decoder*  decoder )
    664   {
    665     FT_ASSERT( decoder && decoder->current_subfont );
    666 
    667     return cf2_intToFixed(
    668              decoder->current_subfont->private_dict.nominal_width );
    669   }
    670 
    671 
    672   FT_LOCAL_DEF( void )
    673   cf2_outline_reset( CF2_Outline  outline )
    674   {
    675     CFF_Decoder*  decoder = outline->decoder;
    676 
    677 
    678     FT_ASSERT( decoder );
    679 
    680     outline->root.windingMomentum = 0;
    681 
    682     FT_GlyphLoader_Rewind( decoder->builder.loader );
    683   }
    684 
    685 
    686   FT_LOCAL_DEF( void )
    687   cf2_outline_close( CF2_Outline  outline )
    688   {
    689     CFF_Decoder*  decoder = outline->decoder;
    690 
    691 
    692     FT_ASSERT( decoder );
    693 
    694     cff_builder_close_contour( &decoder->builder );
    695 
    696     FT_GlyphLoader_Add( decoder->builder.loader );
    697   }
    698 
    699 
    700 /* END */
    701