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