• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
cf2_checkTransform(const CF2_Matrix * transform,CF2_Int unitsPerEm)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
cf2_setGlyphWidth(CF2_Outline outline,CF2_Fixed width)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
cf2_free_instance(void * ptr)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
cf2_builder_moveTo(CF2_OutlineCallbacks callbacks,const CF2_CallbackParams params)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
cf2_builder_lineTo(CF2_OutlineCallbacks callbacks,const CF2_CallbackParams params)142   cf2_builder_lineTo( CF2_OutlineCallbacks      callbacks,
143                       const CF2_CallbackParams  params )
144   {
145     /* downcast the object pointer */
146     CF2_Outline   outline = (CF2_Outline)callbacks;
147     CFF_Builder*  builder;
148 
149 
150     FT_ASSERT( outline && outline->decoder );
151     FT_ASSERT( params->op == CF2_PathOpLineTo );
152 
153     builder = &outline->decoder->builder;
154 
155     if ( !builder->path_begun )
156     {
157       /* record the move before the line; also check points and set */
158       /* `path_begun'                                               */
159       cff_builder_start_point( builder,
160                                params->pt0.x,
161                                params->pt0.y );
162     }
163 
164     /* `cff_builder_add_point1' includes a check_points call for one point */
165     cff_builder_add_point1( builder,
166                             params->pt1.x,
167                             params->pt1.y );
168   }
169 
170 
171   static void
cf2_builder_cubeTo(CF2_OutlineCallbacks callbacks,const CF2_CallbackParams params)172   cf2_builder_cubeTo( CF2_OutlineCallbacks      callbacks,
173                       const CF2_CallbackParams  params )
174   {
175     /* downcast the object pointer */
176     CF2_Outline   outline = (CF2_Outline)callbacks;
177     CFF_Builder*  builder;
178 
179 
180     FT_ASSERT( outline && outline->decoder );
181     FT_ASSERT( params->op == CF2_PathOpCubeTo );
182 
183     builder = &outline->decoder->builder;
184 
185     if ( !builder->path_begun )
186     {
187       /* record the move before the line; also check points and set */
188       /* `path_begun'                                               */
189       cff_builder_start_point( builder,
190                                params->pt0.x,
191                                params->pt0.y );
192     }
193 
194     /* prepare room for 3 points: 2 off-curve, 1 on-curve */
195     cff_check_points( builder, 3 );
196 
197     cff_builder_add_point( builder,
198                            params->pt1.x,
199                            params->pt1.y, 0 );
200     cff_builder_add_point( builder,
201                            params->pt2.x,
202                            params->pt2.y, 0 );
203     cff_builder_add_point( builder,
204                            params->pt3.x,
205                            params->pt3.y, 1 );
206   }
207 
208 
209   static void
cf2_outline_init(CF2_Outline outline,FT_Memory memory,FT_Error * error)210   cf2_outline_init( CF2_Outline  outline,
211                     FT_Memory    memory,
212                     FT_Error*    error )
213   {
214     FT_MEM_ZERO( outline, sizeof ( CF2_OutlineRec ) );
215 
216     outline->root.memory = memory;
217     outline->root.error  = error;
218 
219     outline->root.moveTo = cf2_builder_moveTo;
220     outline->root.lineTo = cf2_builder_lineTo;
221     outline->root.cubeTo = cf2_builder_cubeTo;
222   }
223 
224 
225   /* get scaling and hint flag from GlyphSlot */
226   static void
cf2_getScaleAndHintFlag(CFF_Decoder * decoder,CF2_Fixed * x_scale,CF2_Fixed * y_scale,FT_Bool * hinted,FT_Bool * scaled)227   cf2_getScaleAndHintFlag( CFF_Decoder*  decoder,
228                            CF2_Fixed*    x_scale,
229                            CF2_Fixed*    y_scale,
230                            FT_Bool*      hinted,
231                            FT_Bool*      scaled )
232   {
233     FT_ASSERT( decoder && decoder->builder.glyph );
234 
235     /* note: FreeType scale includes a factor of 64 */
236     *hinted = decoder->builder.glyph->hint;
237     *scaled = decoder->builder.glyph->scaled;
238 
239     if ( *hinted )
240     {
241       *x_scale = ( decoder->builder.glyph->x_scale + 32 ) / 64;
242       *y_scale = ( decoder->builder.glyph->y_scale + 32 ) / 64;
243     }
244     else
245     {
246       /* for unhinted outlines, `cff_slot_load' does the scaling, */
247       /* thus render at `unity' scale                             */
248 
249       *x_scale = 0x0400;   /* 1/64 as 16.16 */
250       *y_scale = 0x0400;
251     }
252   }
253 
254 
255   /* get units per em from `FT_Face' */
256   /* TODO: should handle font matrix concatenation? */
257   static FT_UShort
cf2_getUnitsPerEm(CFF_Decoder * decoder)258   cf2_getUnitsPerEm( CFF_Decoder*  decoder )
259   {
260     FT_ASSERT( decoder && decoder->builder.face );
261     FT_ASSERT( decoder->builder.face->root.units_per_EM );
262 
263     return decoder->builder.face->root.units_per_EM;
264   }
265 
266 
267   /* Main entry point: Render one glyph. */
268   FT_LOCAL_DEF( FT_Error )
cf2_decoder_parse_charstrings(CFF_Decoder * decoder,FT_Byte * charstring_base,FT_ULong charstring_len)269   cf2_decoder_parse_charstrings( CFF_Decoder*  decoder,
270                                  FT_Byte*      charstring_base,
271                                  FT_ULong      charstring_len )
272   {
273     FT_Memory  memory;
274     FT_Error   error = FT_Err_Ok;
275     CF2_Font   font;
276 
277 
278     FT_ASSERT( decoder && decoder->cff );
279 
280     memory = decoder->builder.memory;
281 
282     /* CF2 data is saved here across glyphs */
283     font = (CF2_Font)decoder->cff->cf2_instance.data;
284 
285     /* on first glyph, allocate instance structure */
286     if ( decoder->cff->cf2_instance.data == NULL )
287     {
288       decoder->cff->cf2_instance.finalizer =
289         (FT_Generic_Finalizer)cf2_free_instance;
290 
291       if ( FT_ALLOC( decoder->cff->cf2_instance.data,
292                      sizeof ( CF2_FontRec ) ) )
293         return FT_THROW( Out_Of_Memory );
294 
295       font = (CF2_Font)decoder->cff->cf2_instance.data;
296 
297       font->memory = memory;
298 
299       /* initialize a client outline, to be shared by each glyph rendered */
300       cf2_outline_init( &font->outline, font->memory, &font->error );
301     }
302 
303     /* save decoder; it is a stack variable and will be different on each */
304     /* call                                                               */
305     font->decoder         = decoder;
306     font->outline.decoder = decoder;
307 
308     {
309       /* build parameters for Adobe engine */
310 
311       CFF_Builder*  builder = &decoder->builder;
312       CFF_Driver    driver  = (CFF_Driver)FT_FACE_DRIVER( builder->face );
313 
314       /* local error */
315       FT_Error       error2 = FT_Err_Ok;
316       CF2_BufferRec  buf;
317       CF2_Matrix     transform;
318       CF2_F16Dot16   glyphWidth;
319 
320       FT_Bool  hinted;
321       FT_Bool  scaled;
322 
323 
324       /* FreeType has already looked up the GID; convert to         */
325       /* `RegionBuffer', assuming that the input has been validated */
326       FT_ASSERT( charstring_base + charstring_len >= charstring_base );
327 
328       FT_ZERO( &buf );
329       buf.start =
330       buf.ptr   = charstring_base;
331       buf.end   = charstring_base + charstring_len;
332 
333       FT_ZERO( &transform );
334 
335       cf2_getScaleAndHintFlag( decoder,
336                                &transform.a,
337                                &transform.d,
338                                &hinted,
339                                &scaled );
340 
341       font->renderingFlags = 0;
342       if ( hinted )
343         font->renderingFlags |= CF2_FlagsHinted;
344       if ( scaled && !driver->no_stem_darkening )
345         font->renderingFlags |= CF2_FlagsDarkened;
346 
347       font->darkenParams[0] = driver->darken_params[0];
348       font->darkenParams[1] = driver->darken_params[1];
349       font->darkenParams[2] = driver->darken_params[2];
350       font->darkenParams[3] = driver->darken_params[3];
351       font->darkenParams[4] = driver->darken_params[4];
352       font->darkenParams[5] = driver->darken_params[5];
353       font->darkenParams[6] = driver->darken_params[6];
354       font->darkenParams[7] = driver->darken_params[7];
355 
356       /* now get an outline for this glyph;      */
357       /* also get units per em to validate scale */
358       font->unitsPerEm = (CF2_Int)cf2_getUnitsPerEm( decoder );
359 
360       if ( scaled )
361       {
362         error2 = cf2_checkTransform( &transform, font->unitsPerEm );
363         if ( error2 )
364           return error2;
365       }
366 
367       error2 = cf2_getGlyphOutline( font, &buf, &transform, &glyphWidth );
368       if ( error2 )
369         return FT_ERR( Invalid_File_Format );
370 
371       cf2_setGlyphWidth( &font->outline, glyphWidth );
372 
373       return FT_Err_Ok;
374     }
375   }
376 
377 
378   /* get pointer to current FreeType subfont (based on current glyphID) */
379   FT_LOCAL_DEF( CFF_SubFont )
cf2_getSubfont(CFF_Decoder * decoder)380   cf2_getSubfont( CFF_Decoder*  decoder )
381   {
382     FT_ASSERT( decoder && decoder->current_subfont );
383 
384     return decoder->current_subfont;
385   }
386 
387 
388   /* get `y_ppem' from `CFF_Size' */
389   FT_LOCAL_DEF( CF2_Fixed )
cf2_getPpemY(CFF_Decoder * decoder)390   cf2_getPpemY( CFF_Decoder*  decoder )
391   {
392     FT_ASSERT( decoder                          &&
393                decoder->builder.face            &&
394                decoder->builder.face->root.size );
395 
396     /*
397      * Note that `y_ppem' can be zero if there wasn't a call to
398      * `FT_Set_Char_Size' or something similar.  However, this isn't a
399      * problem since we come to this place in the code only if
400      * FT_LOAD_NO_SCALE is set (the other case gets caught by
401      * `cf2_checkTransform').  The ppem value is needed to compute the stem
402      * darkening, which is disabled for getting the unscaled outline.
403      *
404      */
405     return cf2_intToFixed(
406              decoder->builder.face->root.size->metrics.y_ppem );
407   }
408 
409 
410   /* get standard stem widths for the current subfont; */
411   /* FreeType stores these as integer font units       */
412   /* (note: variable names seem swapped)               */
413   FT_LOCAL_DEF( CF2_Fixed )
cf2_getStdVW(CFF_Decoder * decoder)414   cf2_getStdVW( CFF_Decoder*  decoder )
415   {
416     FT_ASSERT( decoder && decoder->current_subfont );
417 
418     return cf2_intToFixed(
419              decoder->current_subfont->private_dict.standard_height );
420   }
421 
422 
423   FT_LOCAL_DEF( CF2_Fixed )
cf2_getStdHW(CFF_Decoder * decoder)424   cf2_getStdHW( CFF_Decoder*  decoder )
425   {
426     FT_ASSERT( decoder && decoder->current_subfont );
427 
428     return cf2_intToFixed(
429              decoder->current_subfont->private_dict.standard_width );
430   }
431 
432 
433   /* note: FreeType stores 1000 times the actual value for `BlueScale' */
434   FT_LOCAL_DEF( void )
cf2_getBlueMetrics(CFF_Decoder * decoder,CF2_Fixed * blueScale,CF2_Fixed * blueShift,CF2_Fixed * blueFuzz)435   cf2_getBlueMetrics( CFF_Decoder*  decoder,
436                       CF2_Fixed*    blueScale,
437                       CF2_Fixed*    blueShift,
438                       CF2_Fixed*    blueFuzz )
439   {
440     FT_ASSERT( decoder && decoder->current_subfont );
441 
442     *blueScale = FT_DivFix(
443                    decoder->current_subfont->private_dict.blue_scale,
444                    cf2_intToFixed( 1000 ) );
445     *blueShift = cf2_intToFixed(
446                    decoder->current_subfont->private_dict.blue_shift );
447     *blueFuzz  = cf2_intToFixed(
448                    decoder->current_subfont->private_dict.blue_fuzz );
449   }
450 
451 
452   /* get blue values counts and arrays; the FreeType parser has validated */
453   /* the counts and verified that each is an even number                  */
454   FT_LOCAL_DEF( void )
cf2_getBlueValues(CFF_Decoder * decoder,size_t * count,FT_Pos ** data)455   cf2_getBlueValues( CFF_Decoder*  decoder,
456                      size_t*       count,
457                      FT_Pos*      *data )
458   {
459     FT_ASSERT( decoder && decoder->current_subfont );
460 
461     *count = decoder->current_subfont->private_dict.num_blue_values;
462     *data  = (FT_Pos*)
463                &decoder->current_subfont->private_dict.blue_values;
464   }
465 
466 
467   FT_LOCAL_DEF( void )
cf2_getOtherBlues(CFF_Decoder * decoder,size_t * count,FT_Pos ** data)468   cf2_getOtherBlues( CFF_Decoder*  decoder,
469                      size_t*       count,
470                      FT_Pos*      *data )
471   {
472     FT_ASSERT( decoder && decoder->current_subfont );
473 
474     *count = decoder->current_subfont->private_dict.num_other_blues;
475     *data  = (FT_Pos*)
476                &decoder->current_subfont->private_dict.other_blues;
477   }
478 
479 
480   FT_LOCAL_DEF( void )
cf2_getFamilyBlues(CFF_Decoder * decoder,size_t * count,FT_Pos ** data)481   cf2_getFamilyBlues( CFF_Decoder*  decoder,
482                       size_t*       count,
483                       FT_Pos*      *data )
484   {
485     FT_ASSERT( decoder && decoder->current_subfont );
486 
487     *count = decoder->current_subfont->private_dict.num_family_blues;
488     *data  = (FT_Pos*)
489                &decoder->current_subfont->private_dict.family_blues;
490   }
491 
492 
493   FT_LOCAL_DEF( void )
cf2_getFamilyOtherBlues(CFF_Decoder * decoder,size_t * count,FT_Pos ** data)494   cf2_getFamilyOtherBlues( CFF_Decoder*  decoder,
495                            size_t*       count,
496                            FT_Pos*      *data )
497   {
498     FT_ASSERT( decoder && decoder->current_subfont );
499 
500     *count = decoder->current_subfont->private_dict.num_family_other_blues;
501     *data  = (FT_Pos*)
502                &decoder->current_subfont->private_dict.family_other_blues;
503   }
504 
505 
506   FT_LOCAL_DEF( CF2_Int )
cf2_getLanguageGroup(CFF_Decoder * decoder)507   cf2_getLanguageGroup( CFF_Decoder*  decoder )
508   {
509     FT_ASSERT( decoder && decoder->current_subfont );
510 
511     return decoder->current_subfont->private_dict.language_group;
512   }
513 
514 
515   /* convert unbiased subroutine index to `CF2_Buffer' and */
516   /* return 0 on success                                   */
517   FT_LOCAL_DEF( CF2_Int )
cf2_initGlobalRegionBuffer(CFF_Decoder * decoder,CF2_UInt idx,CF2_Buffer buf)518   cf2_initGlobalRegionBuffer( CFF_Decoder*  decoder,
519                               CF2_UInt      idx,
520                               CF2_Buffer    buf )
521   {
522     FT_ASSERT( decoder );
523 
524     FT_ZERO( buf );
525 
526     idx += decoder->globals_bias;
527     if ( idx >= decoder->num_globals )
528       return TRUE;     /* error */
529 
530     FT_ASSERT( decoder->globals );
531 
532     buf->start =
533     buf->ptr   = decoder->globals[idx];
534     buf->end   = decoder->globals[idx + 1];
535 
536     return FALSE;      /* success */
537   }
538 
539 
540   /* convert AdobeStandardEncoding code to CF2_Buffer; */
541   /* used for seac component                           */
542   FT_LOCAL_DEF( FT_Error )
cf2_getSeacComponent(CFF_Decoder * decoder,CF2_UInt code,CF2_Buffer buf)543   cf2_getSeacComponent( CFF_Decoder*  decoder,
544                         CF2_UInt      code,
545                         CF2_Buffer    buf )
546   {
547     CF2_Int   gid;
548     FT_Byte*  charstring;
549     FT_ULong  len;
550     FT_Error  error;
551 
552 
553     FT_ASSERT( decoder );
554 
555     FT_ZERO( buf );
556 
557     gid = cff_lookup_glyph_by_stdcharcode( decoder->cff, code );
558     if ( gid < 0 )
559       return FT_THROW( Invalid_Glyph_Format );
560 
561     error = cff_get_glyph_data( decoder->builder.face,
562                                 gid,
563                                 &charstring,
564                                 &len );
565     /* TODO: for now, just pass the FreeType error through */
566     if ( error )
567       return error;
568 
569     /* assume input has been validated */
570     FT_ASSERT( charstring + len >= charstring );
571 
572     buf->start = charstring;
573     buf->end   = charstring + len;
574     buf->ptr   = buf->start;
575 
576     return FT_Err_Ok;
577   }
578 
579 
580   FT_LOCAL_DEF( void )
cf2_freeSeacComponent(CFF_Decoder * decoder,CF2_Buffer buf)581   cf2_freeSeacComponent( CFF_Decoder*  decoder,
582                          CF2_Buffer    buf )
583   {
584     FT_ASSERT( decoder );
585 
586     cff_free_glyph_data( decoder->builder.face,
587                          (FT_Byte**)&buf->start,
588                          (FT_ULong)( buf->end - buf->start ) );
589   }
590 
591 
592   FT_LOCAL_DEF( CF2_Int )
cf2_initLocalRegionBuffer(CFF_Decoder * decoder,CF2_UInt idx,CF2_Buffer buf)593   cf2_initLocalRegionBuffer( CFF_Decoder*  decoder,
594                              CF2_UInt      idx,
595                              CF2_Buffer    buf )
596   {
597     FT_ASSERT( decoder );
598 
599     FT_ZERO( buf );
600 
601     idx += decoder->locals_bias;
602     if ( idx >= decoder->num_locals )
603       return TRUE;     /* error */
604 
605     FT_ASSERT( decoder->locals );
606 
607     buf->start =
608     buf->ptr   = decoder->locals[idx];
609     buf->end   = decoder->locals[idx + 1];
610 
611     return FALSE;      /* success */
612   }
613 
614 
615   FT_LOCAL_DEF( CF2_Fixed )
cf2_getDefaultWidthX(CFF_Decoder * decoder)616   cf2_getDefaultWidthX( CFF_Decoder*  decoder )
617   {
618     FT_ASSERT( decoder && decoder->current_subfont );
619 
620     return cf2_intToFixed(
621              decoder->current_subfont->private_dict.default_width );
622   }
623 
624 
625   FT_LOCAL_DEF( CF2_Fixed )
cf2_getNominalWidthX(CFF_Decoder * decoder)626   cf2_getNominalWidthX( CFF_Decoder*  decoder )
627   {
628     FT_ASSERT( decoder && decoder->current_subfont );
629 
630     return cf2_intToFixed(
631              decoder->current_subfont->private_dict.nominal_width );
632   }
633 
634 
635   FT_LOCAL_DEF( void )
cf2_outline_reset(CF2_Outline outline)636   cf2_outline_reset( CF2_Outline  outline )
637   {
638     CFF_Decoder*  decoder = outline->decoder;
639 
640 
641     FT_ASSERT( decoder );
642 
643     outline->root.windingMomentum = 0;
644 
645     FT_GlyphLoader_Rewind( decoder->builder.loader );
646   }
647 
648 
649   FT_LOCAL_DEF( void )
cf2_outline_close(CF2_Outline outline)650   cf2_outline_close( CF2_Outline  outline )
651   {
652     CFF_Decoder*  decoder = outline->decoder;
653 
654 
655     FT_ASSERT( decoder );
656 
657     cff_builder_close_contour( &decoder->builder );
658 
659     FT_GlyphLoader_Add( decoder->builder.loader );
660   }
661 
662 
663 /* END */
664