/**************************************************************************** * * pfrgload.c * * FreeType PFR glyph loader (body). * * Copyright 2002-2018 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 FT_INTERNAL_DEBUG_H #include "pfrerror.h" #undef FT_COMPONENT #define FT_COMPONENT trace_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.%d", 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.%d", 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 */