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