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