| /**************************************************************************** |
| * |
| * pfrgload.c |
| * |
| * FreeType PFR glyph loader (body). |
| * |
| * Copyright (C) 2002-2021 by |
| * David Turner, Robert Wilhelm, and Werner Lemberg. |
| * |
| * This file is part of the FreeType project, and may only be used, |
| * modified, and distributed under the terms of the FreeType project |
| * license, LICENSE.TXT. By continuing to use, modify, or distribute |
| * this file you indicate that you have read the license and |
| * understand and accept it fully. |
| * |
| */ |
| |
| |
| #include "pfrgload.h" |
| #include "pfrsbit.h" |
| #include "pfrload.h" /* for macro definitions */ |
| #include <freetype/internal/ftdebug.h> |
| |
| #include "pfrerror.h" |
| |
| #undef FT_COMPONENT |
| #define FT_COMPONENT pfr |
| |
| |
| /*************************************************************************/ |
| /*************************************************************************/ |
| /***** *****/ |
| /***** PFR GLYPH BUILDER *****/ |
| /***** *****/ |
| /*************************************************************************/ |
| /*************************************************************************/ |
| |
| |
| FT_LOCAL_DEF( void ) |
| pfr_glyph_init( PFR_Glyph glyph, |
| FT_GlyphLoader loader ) |
| { |
| FT_ZERO( glyph ); |
| |
| glyph->loader = loader; |
| glyph->path_begun = 0; |
| |
| FT_GlyphLoader_Rewind( loader ); |
| } |
| |
| |
| FT_LOCAL_DEF( void ) |
| pfr_glyph_done( PFR_Glyph glyph ) |
| { |
| FT_Memory memory = glyph->loader->memory; |
| |
| |
| FT_FREE( glyph->x_control ); |
| glyph->y_control = NULL; |
| |
| glyph->max_xy_control = 0; |
| #if 0 |
| glyph->num_x_control = 0; |
| glyph->num_y_control = 0; |
| #endif |
| |
| FT_FREE( glyph->subs ); |
| |
| glyph->max_subs = 0; |
| glyph->num_subs = 0; |
| |
| glyph->loader = NULL; |
| glyph->path_begun = 0; |
| } |
| |
| |
| /* close current contour, if any */ |
| static void |
| pfr_glyph_close_contour( PFR_Glyph glyph ) |
| { |
| FT_GlyphLoader loader = glyph->loader; |
| FT_Outline* outline = &loader->current.outline; |
| FT_Int last, first; |
| |
| |
| if ( !glyph->path_begun ) |
| return; |
| |
| /* compute first and last point indices in current glyph outline */ |
| last = outline->n_points - 1; |
| first = 0; |
| if ( outline->n_contours > 0 ) |
| first = outline->contours[outline->n_contours - 1]; |
| |
| /* if the last point falls on the same location as the first one */ |
| /* we need to delete it */ |
| if ( last > first ) |
| { |
| FT_Vector* p1 = outline->points + first; |
| FT_Vector* p2 = outline->points + last; |
| |
| |
| if ( p1->x == p2->x && p1->y == p2->y ) |
| { |
| outline->n_points--; |
| last--; |
| } |
| } |
| |
| /* don't add empty contours */ |
| if ( last >= first ) |
| outline->contours[outline->n_contours++] = (short)last; |
| |
| glyph->path_begun = 0; |
| } |
| |
| |
| /* reset glyph to start the loading of a new glyph */ |
| static void |
| pfr_glyph_start( PFR_Glyph glyph ) |
| { |
| glyph->path_begun = 0; |
| } |
| |
| |
| static FT_Error |
| pfr_glyph_line_to( PFR_Glyph glyph, |
| FT_Vector* to ) |
| { |
| FT_GlyphLoader loader = glyph->loader; |
| FT_Outline* outline = &loader->current.outline; |
| FT_Error error; |
| |
| |
| /* check that we have begun a new path */ |
| if ( !glyph->path_begun ) |
| { |
| error = FT_THROW( Invalid_Table ); |
| FT_ERROR(( "pfr_glyph_line_to: invalid glyph data\n" )); |
| goto Exit; |
| } |
| |
| error = FT_GLYPHLOADER_CHECK_POINTS( loader, 1, 0 ); |
| if ( !error ) |
| { |
| FT_Int n = outline->n_points; |
| |
| |
| outline->points[n] = *to; |
| outline->tags [n] = FT_CURVE_TAG_ON; |
| |
| outline->n_points++; |
| } |
| |
| Exit: |
| return error; |
| } |
| |
| |
| static FT_Error |
| pfr_glyph_curve_to( PFR_Glyph glyph, |
| FT_Vector* control1, |
| FT_Vector* control2, |
| FT_Vector* to ) |
| { |
| FT_GlyphLoader loader = glyph->loader; |
| FT_Outline* outline = &loader->current.outline; |
| FT_Error error; |
| |
| |
| /* check that we have begun a new path */ |
| if ( !glyph->path_begun ) |
| { |
| error = FT_THROW( Invalid_Table ); |
| FT_ERROR(( "pfr_glyph_line_to: invalid glyph data\n" )); |
| goto Exit; |
| } |
| |
| error = FT_GLYPHLOADER_CHECK_POINTS( loader, 3, 0 ); |
| if ( !error ) |
| { |
| FT_Vector* vec = outline->points + outline->n_points; |
| FT_Byte* tag = (FT_Byte*)outline->tags + outline->n_points; |
| |
| |
| vec[0] = *control1; |
| vec[1] = *control2; |
| vec[2] = *to; |
| tag[0] = FT_CURVE_TAG_CUBIC; |
| tag[1] = FT_CURVE_TAG_CUBIC; |
| tag[2] = FT_CURVE_TAG_ON; |
| |
| outline->n_points = (FT_Short)( outline->n_points + 3 ); |
| } |
| |
| Exit: |
| return error; |
| } |
| |
| |
| static FT_Error |
| pfr_glyph_move_to( PFR_Glyph glyph, |
| FT_Vector* to ) |
| { |
| FT_GlyphLoader loader = glyph->loader; |
| FT_Error error; |
| |
| |
| /* close current contour if any */ |
| pfr_glyph_close_contour( glyph ); |
| |
| /* indicate that a new contour has started */ |
| glyph->path_begun = 1; |
| |
| /* check that there is space for a new contour and a new point */ |
| error = FT_GLYPHLOADER_CHECK_POINTS( loader, 1, 1 ); |
| if ( !error ) |
| { |
| /* add new start point */ |
| error = pfr_glyph_line_to( glyph, to ); |
| } |
| |
| return error; |
| } |
| |
| |
| static void |
| pfr_glyph_end( PFR_Glyph glyph ) |
| { |
| /* close current contour if any */ |
| pfr_glyph_close_contour( glyph ); |
| |
| /* merge the current glyph into the stack */ |
| FT_GlyphLoader_Add( glyph->loader ); |
| } |
| |
| |
| /*************************************************************************/ |
| /*************************************************************************/ |
| /***** *****/ |
| /***** PFR GLYPH LOADER *****/ |
| /***** *****/ |
| /*************************************************************************/ |
| /*************************************************************************/ |
| |
| |
| /* load a simple glyph */ |
| static FT_Error |
| pfr_glyph_load_simple( PFR_Glyph glyph, |
| FT_Byte* p, |
| FT_Byte* limit ) |
| { |
| FT_Error error = FT_Err_Ok; |
| FT_Memory memory = glyph->loader->memory; |
| FT_UInt flags, x_count, y_count, i, count, mask; |
| FT_Int x; |
| |
| |
| PFR_CHECK( 1 ); |
| flags = PFR_NEXT_BYTE( p ); |
| |
| /* test for composite glyphs */ |
| if ( flags & PFR_GLYPH_IS_COMPOUND ) |
| goto Failure; |
| |
| x_count = 0; |
| y_count = 0; |
| |
| if ( flags & PFR_GLYPH_1BYTE_XYCOUNT ) |
| { |
| PFR_CHECK( 1 ); |
| count = PFR_NEXT_BYTE( p ); |
| x_count = count & 15; |
| y_count = count >> 4; |
| } |
| else |
| { |
| if ( flags & PFR_GLYPH_XCOUNT ) |
| { |
| PFR_CHECK( 1 ); |
| x_count = PFR_NEXT_BYTE( p ); |
| } |
| |
| if ( flags & PFR_GLYPH_YCOUNT ) |
| { |
| PFR_CHECK( 1 ); |
| y_count = PFR_NEXT_BYTE( p ); |
| } |
| } |
| |
| count = x_count + y_count; |
| |
| /* re-allocate array when necessary */ |
| if ( count > glyph->max_xy_control ) |
| { |
| FT_UInt new_max = FT_PAD_CEIL( count, 8 ); |
| |
| |
| if ( FT_RENEW_ARRAY( glyph->x_control, |
| glyph->max_xy_control, |
| new_max ) ) |
| goto Exit; |
| |
| glyph->max_xy_control = new_max; |
| } |
| |
| glyph->y_control = glyph->x_control + x_count; |
| |
| mask = 0; |
| x = 0; |
| |
| for ( i = 0; i < count; i++ ) |
| { |
| if ( ( i & 7 ) == 0 ) |
| { |
| PFR_CHECK( 1 ); |
| mask = PFR_NEXT_BYTE( p ); |
| } |
| |
| if ( mask & 1 ) |
| { |
| PFR_CHECK( 2 ); |
| x = PFR_NEXT_SHORT( p ); |
| } |
| else |
| { |
| PFR_CHECK( 1 ); |
| x += PFR_NEXT_BYTE( p ); |
| } |
| |
| glyph->x_control[i] = x; |
| |
| mask >>= 1; |
| } |
| |
| /* XXX: we ignore the secondary stroke and edge definitions */ |
| /* since we don't support native PFR hinting */ |
| /* */ |
| if ( flags & PFR_GLYPH_SINGLE_EXTRA_ITEMS ) |
| { |
| error = pfr_extra_items_skip( &p, limit ); |
| if ( error ) |
| goto Exit; |
| } |
| |
| pfr_glyph_start( glyph ); |
| |
| /* now load a simple glyph */ |
| { |
| FT_Vector pos[4]; |
| FT_Vector* cur; |
| |
| |
| pos[0].x = pos[0].y = 0; |
| pos[3] = pos[0]; |
| |
| for (;;) |
| { |
| FT_UInt format, format_low, args_format = 0, args_count, n; |
| |
| |
| /**************************************************************** |
| * read instruction |
| */ |
| PFR_CHECK( 1 ); |
| format = PFR_NEXT_BYTE( p ); |
| format_low = format & 15; |
| |
| switch ( format >> 4 ) |
| { |
| case 0: /* end glyph */ |
| FT_TRACE6(( "- end glyph" )); |
| args_count = 0; |
| break; |
| |
| case 1: /* general line operation */ |
| FT_TRACE6(( "- general line" )); |
| goto Line1; |
| |
| case 4: /* move to inside contour */ |
| FT_TRACE6(( "- move to inside" )); |
| goto Line1; |
| |
| case 5: /* move to outside contour */ |
| FT_TRACE6(( "- move to outside" )); |
| Line1: |
| args_format = format_low; |
| args_count = 1; |
| break; |
| |
| case 2: /* horizontal line to */ |
| FT_TRACE6(( "- horizontal line to cx.%d", format_low )); |
| if ( format_low >= x_count ) |
| goto Failure; |
| pos[0].x = glyph->x_control[format_low]; |
| pos[0].y = pos[3].y; |
| pos[3] = pos[0]; |
| args_count = 0; |
| break; |
| |
| case 3: /* vertical line to */ |
| FT_TRACE6(( "- vertical line to cy.%d", format_low )); |
| if ( format_low >= y_count ) |
| goto Failure; |
| pos[0].x = pos[3].x; |
| pos[0].y = glyph->y_control[format_low]; |
| pos[3] = pos[0]; |
| args_count = 0; |
| break; |
| |
| case 6: /* horizontal to vertical curve */ |
| FT_TRACE6(( "- hv curve " )); |
| args_format = 0xB8E; |
| args_count = 3; |
| break; |
| |
| case 7: /* vertical to horizontal curve */ |
| FT_TRACE6(( "- vh curve" )); |
| args_format = 0xE2B; |
| args_count = 3; |
| break; |
| |
| default: /* general curve to */ |
| FT_TRACE6(( "- general curve" )); |
| args_count = 4; |
| args_format = format_low; |
| } |
| |
| /************************************************************ |
| * now read arguments |
| */ |
| cur = pos; |
| for ( n = 0; n < args_count; n++ ) |
| { |
| FT_UInt idx; |
| FT_Int delta; |
| |
| |
| /* read the X argument */ |
| switch ( args_format & 3 ) |
| { |
| case 0: /* 8-bit index */ |
| PFR_CHECK( 1 ); |
| idx = PFR_NEXT_BYTE( p ); |
| if ( idx >= x_count ) |
| goto Failure; |
| cur->x = glyph->x_control[idx]; |
| FT_TRACE7(( " cx#%d", idx )); |
| break; |
| |
| case 1: /* 16-bit absolute value */ |
| PFR_CHECK( 2 ); |
| cur->x = PFR_NEXT_SHORT( p ); |
| FT_TRACE7(( " x.%ld", cur->x )); |
| break; |
| |
| case 2: /* 8-bit delta */ |
| PFR_CHECK( 1 ); |
| delta = PFR_NEXT_INT8( p ); |
| cur->x = pos[3].x + delta; |
| FT_TRACE7(( " dx.%d", delta )); |
| break; |
| |
| default: |
| FT_TRACE7(( " |" )); |
| cur->x = pos[3].x; |
| } |
| |
| /* read the Y argument */ |
| switch ( ( args_format >> 2 ) & 3 ) |
| { |
| case 0: /* 8-bit index */ |
| PFR_CHECK( 1 ); |
| idx = PFR_NEXT_BYTE( p ); |
| if ( idx >= y_count ) |
| goto Failure; |
| cur->y = glyph->y_control[idx]; |
| FT_TRACE7(( " cy#%d", idx )); |
| break; |
| |
| case 1: /* 16-bit absolute value */ |
| PFR_CHECK( 2 ); |
| cur->y = PFR_NEXT_SHORT( p ); |
| FT_TRACE7(( " y.%ld", cur->y )); |
| break; |
| |
| case 2: /* 8-bit delta */ |
| PFR_CHECK( 1 ); |
| delta = PFR_NEXT_INT8( p ); |
| cur->y = pos[3].y + delta; |
| FT_TRACE7(( " dy.%d", delta )); |
| break; |
| |
| default: |
| FT_TRACE7(( " -" )); |
| cur->y = pos[3].y; |
| } |
| |
| /* read the additional format flag for the general curve */ |
| if ( n == 0 && args_count == 4 ) |
| { |
| PFR_CHECK( 1 ); |
| args_format = PFR_NEXT_BYTE( p ); |
| args_count--; |
| } |
| else |
| args_format >>= 4; |
| |
| /* save the previous point */ |
| pos[3] = cur[0]; |
| cur++; |
| } |
| |
| FT_TRACE7(( "\n" )); |
| |
| /************************************************************ |
| * finally, execute instruction |
| */ |
| switch ( format >> 4 ) |
| { |
| case 0: /* end glyph => EXIT */ |
| pfr_glyph_end( glyph ); |
| goto Exit; |
| |
| case 1: /* line operations */ |
| case 2: |
| case 3: |
| error = pfr_glyph_line_to( glyph, pos ); |
| goto Test_Error; |
| |
| case 4: /* move to inside contour */ |
| case 5: /* move to outside contour */ |
| error = pfr_glyph_move_to( glyph, pos ); |
| goto Test_Error; |
| |
| default: /* curve operations */ |
| error = pfr_glyph_curve_to( glyph, pos, pos + 1, pos + 2 ); |
| |
| Test_Error: /* test error condition */ |
| if ( error ) |
| goto Exit; |
| } |
| } /* for (;;) */ |
| } |
| |
| Exit: |
| return error; |
| |
| Failure: |
| Too_Short: |
| error = FT_THROW( Invalid_Table ); |
| FT_ERROR(( "pfr_glyph_load_simple: invalid glyph data\n" )); |
| goto Exit; |
| } |
| |
| |
| /* load a composite/compound glyph */ |
| static FT_Error |
| pfr_glyph_load_compound( PFR_Glyph glyph, |
| FT_Byte* p, |
| FT_Byte* limit ) |
| { |
| FT_Error error = FT_Err_Ok; |
| FT_GlyphLoader loader = glyph->loader; |
| FT_Memory memory = loader->memory; |
| PFR_SubGlyph subglyph; |
| FT_UInt flags, i, count, org_count; |
| FT_Int x_pos, y_pos; |
| |
| |
| PFR_CHECK( 1 ); |
| flags = PFR_NEXT_BYTE( p ); |
| |
| /* test for composite glyphs */ |
| if ( !( flags & PFR_GLYPH_IS_COMPOUND ) ) |
| goto Failure; |
| |
| count = flags & 0x3F; |
| |
| /* ignore extra items when present */ |
| /* */ |
| if ( flags & PFR_GLYPH_COMPOUND_EXTRA_ITEMS ) |
| { |
| error = pfr_extra_items_skip( &p, limit ); |
| if ( error ) |
| goto Exit; |
| } |
| |
| /* we can't rely on the FT_GlyphLoader to load sub-glyphs, because */ |
| /* the PFR format is dumb, using direct file offsets to point to the */ |
| /* sub-glyphs (instead of glyph indices). Sigh. */ |
| /* */ |
| /* For now, we load the list of sub-glyphs into a different array */ |
| /* but this will prevent us from using the auto-hinter at its best */ |
| /* quality. */ |
| /* */ |
| org_count = glyph->num_subs; |
| |
| if ( org_count + count > glyph->max_subs ) |
| { |
| FT_UInt new_max = ( org_count + count + 3 ) & (FT_UInt)-4; |
| |
| |
| /* we arbitrarily limit the number of subglyphs */ |
| /* to avoid endless recursion */ |
| if ( new_max > 64 ) |
| { |
| error = FT_THROW( Invalid_Table ); |
| FT_ERROR(( "pfr_glyph_load_compound:" |
| " too many compound glyphs components\n" )); |
| goto Exit; |
| } |
| |
| if ( FT_RENEW_ARRAY( glyph->subs, glyph->max_subs, new_max ) ) |
| goto Exit; |
| |
| glyph->max_subs = new_max; |
| } |
| |
| subglyph = glyph->subs + org_count; |
| |
| for ( i = 0; i < count; i++, subglyph++ ) |
| { |
| FT_UInt format; |
| |
| |
| x_pos = 0; |
| y_pos = 0; |
| |
| PFR_CHECK( 1 ); |
| format = PFR_NEXT_BYTE( p ); |
| |
| /* read scale when available */ |
| subglyph->x_scale = 0x10000L; |
| if ( format & PFR_SUBGLYPH_XSCALE ) |
| { |
| PFR_CHECK( 2 ); |
| subglyph->x_scale = PFR_NEXT_SHORT( p ) * 16; |
| } |
| |
| subglyph->y_scale = 0x10000L; |
| if ( format & PFR_SUBGLYPH_YSCALE ) |
| { |
| PFR_CHECK( 2 ); |
| subglyph->y_scale = PFR_NEXT_SHORT( p ) * 16; |
| } |
| |
| /* read offset */ |
| switch ( format & 3 ) |
| { |
| case 1: |
| PFR_CHECK( 2 ); |
| x_pos = PFR_NEXT_SHORT( p ); |
| break; |
| |
| case 2: |
| PFR_CHECK( 1 ); |
| x_pos += PFR_NEXT_INT8( p ); |
| break; |
| |
| default: |
| ; |
| } |
| |
| switch ( ( format >> 2 ) & 3 ) |
| { |
| case 1: |
| PFR_CHECK( 2 ); |
| y_pos = PFR_NEXT_SHORT( p ); |
| break; |
| |
| case 2: |
| PFR_CHECK( 1 ); |
| y_pos += PFR_NEXT_INT8( p ); |
| break; |
| |
| default: |
| ; |
| } |
| |
| subglyph->x_delta = x_pos; |
| subglyph->y_delta = y_pos; |
| |
| /* read glyph position and size now */ |
| if ( format & PFR_SUBGLYPH_2BYTE_SIZE ) |
| { |
| PFR_CHECK( 2 ); |
| subglyph->gps_size = PFR_NEXT_USHORT( p ); |
| } |
| else |
| { |
| PFR_CHECK( 1 ); |
| subglyph->gps_size = PFR_NEXT_BYTE( p ); |
| } |
| |
| if ( format & PFR_SUBGLYPH_3BYTE_OFFSET ) |
| { |
| PFR_CHECK( 3 ); |
| subglyph->gps_offset = PFR_NEXT_ULONG( p ); |
| } |
| else |
| { |
| PFR_CHECK( 2 ); |
| subglyph->gps_offset = PFR_NEXT_USHORT( p ); |
| } |
| |
| glyph->num_subs++; |
| } |
| |
| Exit: |
| return error; |
| |
| Failure: |
| Too_Short: |
| error = FT_THROW( Invalid_Table ); |
| FT_ERROR(( "pfr_glyph_load_compound: invalid glyph data\n" )); |
| goto Exit; |
| } |
| |
| |
| static FT_Error |
| pfr_glyph_load_rec( PFR_Glyph glyph, |
| FT_Stream stream, |
| FT_ULong gps_offset, |
| FT_ULong offset, |
| FT_ULong size ) |
| { |
| FT_Error error; |
| FT_Byte* p; |
| FT_Byte* limit; |
| |
| |
| if ( FT_STREAM_SEEK( gps_offset + offset ) || |
| FT_FRAME_ENTER( size ) ) |
| goto Exit; |
| |
| p = (FT_Byte*)stream->cursor; |
| limit = p + size; |
| |
| if ( size > 0 && *p & PFR_GLYPH_IS_COMPOUND ) |
| { |
| FT_UInt n, old_count, count; |
| FT_GlyphLoader loader = glyph->loader; |
| FT_Outline* base = &loader->base.outline; |
| |
| |
| old_count = glyph->num_subs; |
| |
| /* this is a compound glyph - load it */ |
| error = pfr_glyph_load_compound( glyph, p, limit ); |
| |
| FT_FRAME_EXIT(); |
| |
| if ( error ) |
| goto Exit; |
| |
| count = glyph->num_subs - old_count; |
| |
| FT_TRACE4(( "compound glyph with %d element%s (offset %lu):\n", |
| count, |
| count == 1 ? "" : "s", |
| offset )); |
| |
| /* now, load each individual glyph */ |
| for ( n = 0; n < count; n++ ) |
| { |
| FT_Int i, old_points, num_points; |
| PFR_SubGlyph subglyph; |
| |
| |
| FT_TRACE4(( " subglyph %d:\n", n )); |
| |
| subglyph = glyph->subs + old_count + n; |
| old_points = base->n_points; |
| |
| error = pfr_glyph_load_rec( glyph, stream, gps_offset, |
| subglyph->gps_offset, |
| subglyph->gps_size ); |
| if ( error ) |
| break; |
| |
| /* note that `glyph->subs' might have been re-allocated */ |
| subglyph = glyph->subs + old_count + n; |
| num_points = base->n_points - old_points; |
| |
| /* translate and eventually scale the new glyph points */ |
| if ( subglyph->x_scale != 0x10000L || subglyph->y_scale != 0x10000L ) |
| { |
| FT_Vector* vec = base->points + old_points; |
| |
| |
| for ( i = 0; i < num_points; i++, vec++ ) |
| { |
| vec->x = FT_MulFix( vec->x, subglyph->x_scale ) + |
| subglyph->x_delta; |
| vec->y = FT_MulFix( vec->y, subglyph->y_scale ) + |
| subglyph->y_delta; |
| } |
| } |
| else |
| { |
| FT_Vector* vec = loader->base.outline.points + old_points; |
| |
| |
| for ( i = 0; i < num_points; i++, vec++ ) |
| { |
| vec->x += subglyph->x_delta; |
| vec->y += subglyph->y_delta; |
| } |
| } |
| |
| /* proceed to next sub-glyph */ |
| } |
| |
| FT_TRACE4(( "end compound glyph with %d element%s\n", |
| count, |
| count == 1 ? "" : "s" )); |
| } |
| else |
| { |
| FT_TRACE4(( "simple glyph (offset %lu)\n", offset )); |
| |
| /* load a simple glyph */ |
| error = pfr_glyph_load_simple( glyph, p, limit ); |
| |
| FT_FRAME_EXIT(); |
| } |
| |
| Exit: |
| return error; |
| } |
| |
| |
| FT_LOCAL_DEF( FT_Error ) |
| pfr_glyph_load( PFR_Glyph glyph, |
| FT_Stream stream, |
| FT_ULong gps_offset, |
| FT_ULong offset, |
| FT_ULong size ) |
| { |
| /* initialize glyph loader */ |
| FT_GlyphLoader_Rewind( glyph->loader ); |
| |
| glyph->num_subs = 0; |
| |
| /* load the glyph, recursively when needed */ |
| return pfr_glyph_load_rec( glyph, stream, gps_offset, offset, size ); |
| } |
| |
| |
| /* END */ |