• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  *
3  * pfrgload.c
4  *
5  *   FreeType PFR glyph loader (body).
6  *
7  * Copyright 2002-2018 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 )
pfr_glyph_init(PFR_Glyph glyph,FT_GlyphLoader loader)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 )
pfr_glyph_done(PFR_Glyph glyph)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
pfr_glyph_close_contour(PFR_Glyph glyph)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
pfr_glyph_start(PFR_Glyph glyph)120   pfr_glyph_start( PFR_Glyph  glyph )
121   {
122     glyph->path_begun = 0;
123   }
124 
125 
126   static FT_Error
pfr_glyph_line_to(PFR_Glyph glyph,FT_Vector * to)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
pfr_glyph_curve_to(PFR_Glyph glyph,FT_Vector * control1,FT_Vector * control2,FT_Vector * to)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
pfr_glyph_move_to(PFR_Glyph glyph,FT_Vector * to)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
pfr_glyph_end(PFR_Glyph glyph)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
pfr_glyph_load_simple(PFR_Glyph glyph,FT_Byte * p,FT_Byte * limit)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_SINGLE_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
pfr_glyph_load_compound(PFR_Glyph glyph,FT_Byte * p,FT_Byte * limit)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_COMPOUND_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
pfr_glyph_load_rec(PFR_Glyph glyph,FT_Stream stream,FT_ULong gps_offset,FT_ULong offset,FT_ULong size)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 element%s (offset %lu):\n",
760                   count,
761                   count == 1 ? "" : "s",
762                   offset ));
763 
764       /* now, load each individual glyph */
765       for ( n = 0; n < count; n++ )
766       {
767         FT_Int        i, old_points, num_points;
768         PFR_SubGlyph  subglyph;
769 
770 
771         FT_TRACE4(( "  subglyph %d:\n", n ));
772 
773         subglyph   = glyph->subs + old_count + n;
774         old_points = base->n_points;
775 
776         error = pfr_glyph_load_rec( glyph, stream, gps_offset,
777                                     subglyph->gps_offset,
778                                     subglyph->gps_size );
779         if ( error )
780           break;
781 
782         /* note that `glyph->subs' might have been re-allocated */
783         subglyph   = glyph->subs + old_count + n;
784         num_points = base->n_points - old_points;
785 
786         /* translate and eventually scale the new glyph points */
787         if ( subglyph->x_scale != 0x10000L || subglyph->y_scale != 0x10000L )
788         {
789           FT_Vector*  vec = base->points + old_points;
790 
791 
792           for ( i = 0; i < num_points; i++, vec++ )
793           {
794             vec->x = FT_MulFix( vec->x, subglyph->x_scale ) +
795                        subglyph->x_delta;
796             vec->y = FT_MulFix( vec->y, subglyph->y_scale ) +
797                        subglyph->y_delta;
798           }
799         }
800         else
801         {
802           FT_Vector*  vec = loader->base.outline.points + old_points;
803 
804 
805           for ( i = 0; i < num_points; i++, vec++ )
806           {
807             vec->x += subglyph->x_delta;
808             vec->y += subglyph->y_delta;
809           }
810         }
811 
812         /* proceed to next sub-glyph */
813       }
814 
815       FT_TRACE4(( "end compound glyph with %d element%s\n",
816                   count,
817                   count == 1 ? "" : "s" ));
818     }
819     else
820     {
821       FT_TRACE4(( "simple glyph (offset %lu)\n", offset ));
822 
823       /* load a simple glyph */
824       error = pfr_glyph_load_simple( glyph, p, limit );
825 
826       FT_FRAME_EXIT();
827     }
828 
829   Exit:
830     return error;
831   }
832 
833 
834   FT_LOCAL_DEF( FT_Error )
pfr_glyph_load(PFR_Glyph glyph,FT_Stream stream,FT_ULong gps_offset,FT_ULong offset,FT_ULong size)835   pfr_glyph_load( PFR_Glyph  glyph,
836                   FT_Stream  stream,
837                   FT_ULong   gps_offset,
838                   FT_ULong   offset,
839                   FT_ULong   size )
840   {
841     /* initialize glyph loader */
842     FT_GlyphLoader_Rewind( glyph->loader );
843 
844     glyph->num_subs = 0;
845 
846     /* load the glyph, recursively when needed */
847     return pfr_glyph_load_rec( glyph, stream, gps_offset, offset, size );
848   }
849 
850 
851 /* END */
852