| /***************************************************************************/ |
| /* */ |
| /* t1decode.c */ |
| /* */ |
| /* PostScript Type 1 decoding routines (body). */ |
| /* */ |
| /* Copyright 2000-2015 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 <ft2build.h> |
| #include FT_INTERNAL_CALC_H |
| #include FT_INTERNAL_DEBUG_H |
| #include FT_INTERNAL_POSTSCRIPT_HINTS_H |
| #include FT_OUTLINE_H |
| |
| #include "t1decode.h" |
| #include "psobjs.h" |
| |
| #include "psauxerr.h" |
| |
| /* ensure proper sign extension */ |
| #define Fix2Int( f ) ( (FT_Int)(FT_Short)( (f) >> 16 ) ) |
| |
| /*************************************************************************/ |
| /* */ |
| /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ |
| /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ |
| /* messages during execution. */ |
| /* */ |
| #undef FT_COMPONENT |
| #define FT_COMPONENT trace_t1decode |
| |
| |
| typedef enum T1_Operator_ |
| { |
| op_none = 0, |
| op_endchar, |
| op_hsbw, |
| op_seac, |
| op_sbw, |
| op_closepath, |
| op_hlineto, |
| op_hmoveto, |
| op_hvcurveto, |
| op_rlineto, |
| op_rmoveto, |
| op_rrcurveto, |
| op_vhcurveto, |
| op_vlineto, |
| op_vmoveto, |
| op_dotsection, |
| op_hstem, |
| op_hstem3, |
| op_vstem, |
| op_vstem3, |
| op_div, |
| op_callothersubr, |
| op_callsubr, |
| op_pop, |
| op_return, |
| op_setcurrentpoint, |
| op_unknown15, |
| |
| op_max /* never remove this one */ |
| |
| } T1_Operator; |
| |
| |
| static |
| const FT_Int t1_args_count[op_max] = |
| { |
| 0, /* none */ |
| 0, /* endchar */ |
| 2, /* hsbw */ |
| 5, /* seac */ |
| 4, /* sbw */ |
| 0, /* closepath */ |
| 1, /* hlineto */ |
| 1, /* hmoveto */ |
| 4, /* hvcurveto */ |
| 2, /* rlineto */ |
| 2, /* rmoveto */ |
| 6, /* rrcurveto */ |
| 4, /* vhcurveto */ |
| 1, /* vlineto */ |
| 1, /* vmoveto */ |
| 0, /* dotsection */ |
| 2, /* hstem */ |
| 6, /* hstem3 */ |
| 2, /* vstem */ |
| 6, /* vstem3 */ |
| 2, /* div */ |
| -1, /* callothersubr */ |
| 1, /* callsubr */ |
| 0, /* pop */ |
| 0, /* return */ |
| 2, /* setcurrentpoint */ |
| 2 /* opcode 15 (undocumented and obsolete) */ |
| }; |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* t1_lookup_glyph_by_stdcharcode */ |
| /* */ |
| /* <Description> */ |
| /* Looks up a given glyph by its StandardEncoding charcode. Used to */ |
| /* implement the SEAC Type 1 operator. */ |
| /* */ |
| /* <Input> */ |
| /* face :: The current face object. */ |
| /* */ |
| /* charcode :: The character code to look for. */ |
| /* */ |
| /* <Return> */ |
| /* A glyph index in the font face. Returns -1 if the corresponding */ |
| /* glyph wasn't found. */ |
| /* */ |
| static FT_Int |
| t1_lookup_glyph_by_stdcharcode( T1_Decoder decoder, |
| FT_Int charcode ) |
| { |
| FT_UInt n; |
| const FT_String* glyph_name; |
| FT_Service_PsCMaps psnames = decoder->psnames; |
| |
| |
| /* check range of standard char code */ |
| if ( charcode < 0 || charcode > 255 ) |
| return -1; |
| |
| glyph_name = psnames->adobe_std_strings( |
| psnames->adobe_std_encoding[charcode]); |
| |
| for ( n = 0; n < decoder->num_glyphs; n++ ) |
| { |
| FT_String* name = (FT_String*)decoder->glyph_names[n]; |
| |
| |
| if ( name && |
| name[0] == glyph_name[0] && |
| ft_strcmp( name, glyph_name ) == 0 ) |
| return (FT_Int)n; |
| } |
| |
| return -1; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* t1operator_seac */ |
| /* */ |
| /* <Description> */ |
| /* Implements the `seac' Type 1 operator for a Type 1 decoder. */ |
| /* */ |
| /* <Input> */ |
| /* decoder :: The current CID decoder. */ |
| /* */ |
| /* asb :: The accent's side bearing. */ |
| /* */ |
| /* adx :: The horizontal offset of the accent. */ |
| /* */ |
| /* ady :: The vertical offset of the accent. */ |
| /* */ |
| /* bchar :: The base character's StandardEncoding charcode. */ |
| /* */ |
| /* achar :: The accent character's StandardEncoding charcode. */ |
| /* */ |
| /* <Return> */ |
| /* FreeType error code. 0 means success. */ |
| /* */ |
| static FT_Error |
| t1operator_seac( T1_Decoder decoder, |
| FT_Pos asb, |
| FT_Pos adx, |
| FT_Pos ady, |
| FT_Int bchar, |
| FT_Int achar ) |
| { |
| FT_Error error; |
| FT_Int bchar_index, achar_index; |
| #if 0 |
| FT_Int n_base_points; |
| FT_Outline* base = decoder->builder.base; |
| #endif |
| FT_Vector left_bearing, advance; |
| |
| #ifdef FT_CONFIG_OPTION_INCREMENTAL |
| T1_Face face = (T1_Face)decoder->builder.face; |
| #endif |
| |
| |
| if ( decoder->seac ) |
| { |
| FT_ERROR(( "t1operator_seac: invalid nested seac\n" )); |
| return FT_THROW( Syntax_Error ); |
| } |
| |
| if ( decoder->builder.metrics_only ) |
| { |
| FT_ERROR(( "t1operator_seac: unexpected seac\n" )); |
| return FT_THROW( Syntax_Error ); |
| } |
| |
| /* seac weirdness */ |
| adx += decoder->builder.left_bearing.x; |
| |
| /* `glyph_names' is set to 0 for CID fonts which do not */ |
| /* include an encoding. How can we deal with these? */ |
| #ifdef FT_CONFIG_OPTION_INCREMENTAL |
| if ( decoder->glyph_names == 0 && |
| !face->root.internal->incremental_interface ) |
| #else |
| if ( decoder->glyph_names == 0 ) |
| #endif /* FT_CONFIG_OPTION_INCREMENTAL */ |
| { |
| FT_ERROR(( "t1operator_seac:" |
| " glyph names table not available in this font\n" )); |
| return FT_THROW( Syntax_Error ); |
| } |
| |
| #ifdef FT_CONFIG_OPTION_INCREMENTAL |
| if ( face->root.internal->incremental_interface ) |
| { |
| /* the caller must handle the font encoding also */ |
| bchar_index = bchar; |
| achar_index = achar; |
| } |
| else |
| #endif |
| { |
| bchar_index = t1_lookup_glyph_by_stdcharcode( decoder, bchar ); |
| achar_index = t1_lookup_glyph_by_stdcharcode( decoder, achar ); |
| } |
| |
| if ( bchar_index < 0 || achar_index < 0 ) |
| { |
| FT_ERROR(( "t1operator_seac:" |
| " invalid seac character code arguments\n" )); |
| return FT_THROW( Syntax_Error ); |
| } |
| |
| /* if we are trying to load a composite glyph, do not load the */ |
| /* accent character and return the array of subglyphs. */ |
| if ( decoder->builder.no_recurse ) |
| { |
| FT_GlyphSlot glyph = (FT_GlyphSlot)decoder->builder.glyph; |
| FT_GlyphLoader loader = glyph->internal->loader; |
| FT_SubGlyph subg; |
| |
| |
| /* reallocate subglyph array if necessary */ |
| error = FT_GlyphLoader_CheckSubGlyphs( loader, 2 ); |
| if ( error ) |
| goto Exit; |
| |
| subg = loader->current.subglyphs; |
| |
| /* subglyph 0 = base character */ |
| subg->index = bchar_index; |
| subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES | |
| FT_SUBGLYPH_FLAG_USE_MY_METRICS; |
| subg->arg1 = 0; |
| subg->arg2 = 0; |
| subg++; |
| |
| /* subglyph 1 = accent character */ |
| subg->index = achar_index; |
| subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES; |
| subg->arg1 = (FT_Int)FIXED_TO_INT( adx - asb ); |
| subg->arg2 = (FT_Int)FIXED_TO_INT( ady ); |
| |
| /* set up remaining glyph fields */ |
| glyph->num_subglyphs = 2; |
| glyph->subglyphs = loader->base.subglyphs; |
| glyph->format = FT_GLYPH_FORMAT_COMPOSITE; |
| |
| loader->current.num_subglyphs = 2; |
| goto Exit; |
| } |
| |
| /* First load `bchar' in builder */ |
| /* now load the unscaled outline */ |
| |
| FT_GlyphLoader_Prepare( decoder->builder.loader ); /* prepare loader */ |
| |
| /* the seac operator must not be nested */ |
| decoder->seac = TRUE; |
| error = t1_decoder_parse_glyph( decoder, (FT_UInt)bchar_index ); |
| decoder->seac = FALSE; |
| if ( error ) |
| goto Exit; |
| |
| /* save the left bearing and width of the base character */ |
| /* as they will be erased by the next load. */ |
| |
| left_bearing = decoder->builder.left_bearing; |
| advance = decoder->builder.advance; |
| |
| decoder->builder.left_bearing.x = 0; |
| decoder->builder.left_bearing.y = 0; |
| |
| decoder->builder.pos_x = adx - asb; |
| decoder->builder.pos_y = ady; |
| |
| /* Now load `achar' on top of */ |
| /* the base outline */ |
| |
| /* the seac operator must not be nested */ |
| decoder->seac = TRUE; |
| error = t1_decoder_parse_glyph( decoder, (FT_UInt)achar_index ); |
| decoder->seac = FALSE; |
| if ( error ) |
| goto Exit; |
| |
| /* restore the left side bearing and */ |
| /* advance width of the base character */ |
| |
| decoder->builder.left_bearing = left_bearing; |
| decoder->builder.advance = advance; |
| |
| decoder->builder.pos_x = 0; |
| decoder->builder.pos_y = 0; |
| |
| Exit: |
| return error; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* t1_decoder_parse_charstrings */ |
| /* */ |
| /* <Description> */ |
| /* Parses a given Type 1 charstrings program. */ |
| /* */ |
| /* <Input> */ |
| /* decoder :: The current Type 1 decoder. */ |
| /* */ |
| /* charstring_base :: The base address of the charstring stream. */ |
| /* */ |
| /* charstring_len :: The length in bytes of the charstring stream. */ |
| /* */ |
| /* <Return> */ |
| /* FreeType error code. 0 means success. */ |
| /* */ |
| FT_LOCAL_DEF( FT_Error ) |
| t1_decoder_parse_charstrings( T1_Decoder decoder, |
| FT_Byte* charstring_base, |
| FT_UInt charstring_len ) |
| { |
| FT_Error error; |
| T1_Decoder_Zone zone; |
| FT_Byte* ip; |
| FT_Byte* limit; |
| T1_Builder builder = &decoder->builder; |
| FT_Pos x, y, orig_x, orig_y; |
| FT_Int known_othersubr_result_cnt = 0; |
| FT_Int unknown_othersubr_result_cnt = 0; |
| FT_Bool large_int; |
| FT_Fixed seed; |
| |
| T1_Hints_Funcs hinter; |
| |
| #ifdef FT_DEBUG_LEVEL_TRACE |
| FT_Bool bol = TRUE; |
| #endif |
| |
| |
| /* compute random seed from stack address of parameter */ |
| seed = (FT_Fixed)( ( (FT_Offset)(char*)&seed ^ |
| (FT_Offset)(char*)&decoder ^ |
| (FT_Offset)(char*)&charstring_base ) & |
| FT_ULONG_MAX ); |
| seed = ( seed ^ ( seed >> 10 ) ^ ( seed >> 20 ) ) & 0xFFFFL; |
| if ( seed == 0 ) |
| seed = 0x7384; |
| |
| /* First of all, initialize the decoder */ |
| decoder->top = decoder->stack; |
| decoder->zone = decoder->zones; |
| zone = decoder->zones; |
| |
| builder->parse_state = T1_Parse_Start; |
| |
| hinter = (T1_Hints_Funcs)builder->hints_funcs; |
| |
| /* a font that reads BuildCharArray without setting */ |
| /* its values first is buggy, but ... */ |
| FT_ASSERT( ( decoder->len_buildchar == 0 ) == |
| ( decoder->buildchar == NULL ) ); |
| |
| if ( decoder->buildchar && decoder->len_buildchar > 0 ) |
| ft_memset( &decoder->buildchar[0], |
| 0, |
| sizeof ( decoder->buildchar[0] ) * decoder->len_buildchar ); |
| |
| FT_TRACE4(( "\n" |
| "Start charstring\n" )); |
| |
| zone->base = charstring_base; |
| limit = zone->limit = charstring_base + charstring_len; |
| ip = zone->cursor = zone->base; |
| |
| error = FT_Err_Ok; |
| |
| x = orig_x = builder->pos_x; |
| y = orig_y = builder->pos_y; |
| |
| /* begin hints recording session, if any */ |
| if ( hinter ) |
| hinter->open( hinter->hints ); |
| |
| large_int = FALSE; |
| |
| /* now, execute loop */ |
| while ( ip < limit ) |
| { |
| FT_Long* top = decoder->top; |
| T1_Operator op = op_none; |
| FT_Int32 value = 0; |
| |
| |
| FT_ASSERT( known_othersubr_result_cnt == 0 || |
| unknown_othersubr_result_cnt == 0 ); |
| |
| #ifdef FT_DEBUG_LEVEL_TRACE |
| if ( bol ) |
| { |
| FT_TRACE5(( " (%d)", decoder->top - decoder->stack )); |
| bol = FALSE; |
| } |
| #endif |
| |
| /*********************************************************************/ |
| /* */ |
| /* Decode operator or operand */ |
| /* */ |
| /* */ |
| |
| /* first of all, decompress operator or value */ |
| switch ( *ip++ ) |
| { |
| case 1: |
| op = op_hstem; |
| break; |
| |
| case 3: |
| op = op_vstem; |
| break; |
| case 4: |
| op = op_vmoveto; |
| break; |
| case 5: |
| op = op_rlineto; |
| break; |
| case 6: |
| op = op_hlineto; |
| break; |
| case 7: |
| op = op_vlineto; |
| break; |
| case 8: |
| op = op_rrcurveto; |
| break; |
| case 9: |
| op = op_closepath; |
| break; |
| case 10: |
| op = op_callsubr; |
| break; |
| case 11: |
| op = op_return; |
| break; |
| |
| case 13: |
| op = op_hsbw; |
| break; |
| case 14: |
| op = op_endchar; |
| break; |
| |
| case 15: /* undocumented, obsolete operator */ |
| op = op_unknown15; |
| break; |
| |
| case 21: |
| op = op_rmoveto; |
| break; |
| case 22: |
| op = op_hmoveto; |
| break; |
| |
| case 30: |
| op = op_vhcurveto; |
| break; |
| case 31: |
| op = op_hvcurveto; |
| break; |
| |
| case 12: |
| if ( ip > limit ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " invalid escape (12+EOF)\n" )); |
| goto Syntax_Error; |
| } |
| |
| switch ( *ip++ ) |
| { |
| case 0: |
| op = op_dotsection; |
| break; |
| case 1: |
| op = op_vstem3; |
| break; |
| case 2: |
| op = op_hstem3; |
| break; |
| case 6: |
| op = op_seac; |
| break; |
| case 7: |
| op = op_sbw; |
| break; |
| case 12: |
| op = op_div; |
| break; |
| case 16: |
| op = op_callothersubr; |
| break; |
| case 17: |
| op = op_pop; |
| break; |
| case 33: |
| op = op_setcurrentpoint; |
| break; |
| |
| default: |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " invalid escape (12+%d)\n", |
| ip[-1] )); |
| goto Syntax_Error; |
| } |
| break; |
| |
| case 255: /* four bytes integer */ |
| if ( ip + 4 > limit ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " unexpected EOF in integer\n" )); |
| goto Syntax_Error; |
| } |
| |
| value = (FT_Int32)( ( (FT_UInt32)ip[0] << 24 ) | |
| ( (FT_UInt32)ip[1] << 16 ) | |
| ( (FT_UInt32)ip[2] << 8 ) | |
| (FT_UInt32)ip[3] ); |
| ip += 4; |
| |
| /* According to the specification, values > 32000 or < -32000 must */ |
| /* be followed by a `div' operator to make the result be in the */ |
| /* range [-32000;32000]. We expect that the second argument of */ |
| /* `div' is not a large number. Additionally, we don't handle */ |
| /* stuff like `<large1> <large2> <num> div <num> div' or */ |
| /* <large1> <large2> <num> div div'. This is probably not allowed */ |
| /* anyway. */ |
| if ( value > 32000 || value < -32000 ) |
| { |
| if ( large_int ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " no `div' after large integer\n" )); |
| } |
| else |
| large_int = TRUE; |
| } |
| else |
| { |
| if ( !large_int ) |
| value = (FT_Int32)( (FT_UInt32)value << 16 ); |
| } |
| |
| break; |
| |
| default: |
| if ( ip[-1] >= 32 ) |
| { |
| if ( ip[-1] < 247 ) |
| value = (FT_Int32)ip[-1] - 139; |
| else |
| { |
| if ( ++ip > limit ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " unexpected EOF in integer\n" )); |
| goto Syntax_Error; |
| } |
| |
| if ( ip[-2] < 251 ) |
| value = ( ( ip[-2] - 247 ) * 256 ) + ip[-1] + 108; |
| else |
| value = -( ( ( ip[-2] - 251 ) * 256 ) + ip[-1] + 108 ); |
| } |
| |
| if ( !large_int ) |
| value = (FT_Int32)( (FT_UInt32)value << 16 ); |
| } |
| else |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " invalid byte (%d)\n", ip[-1] )); |
| goto Syntax_Error; |
| } |
| } |
| |
| if ( unknown_othersubr_result_cnt > 0 ) |
| { |
| switch ( op ) |
| { |
| case op_callsubr: |
| case op_return: |
| case op_none: |
| case op_pop: |
| break; |
| |
| default: |
| /* all operands have been transferred by previous pops */ |
| unknown_othersubr_result_cnt = 0; |
| break; |
| } |
| } |
| |
| if ( large_int && !( op == op_none || op == op_div ) ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " no `div' after large integer\n" )); |
| |
| large_int = FALSE; |
| } |
| |
| /*********************************************************************/ |
| /* */ |
| /* Push value on stack, or process operator */ |
| /* */ |
| /* */ |
| if ( op == op_none ) |
| { |
| if ( top - decoder->stack >= T1_MAX_CHARSTRINGS_OPERANDS ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings: stack overflow\n" )); |
| goto Syntax_Error; |
| } |
| |
| #ifdef FT_DEBUG_LEVEL_TRACE |
| if ( large_int ) |
| FT_TRACE4(( " %ld", value )); |
| else |
| FT_TRACE4(( " %ld", Fix2Int( value ) )); |
| #endif |
| |
| *top++ = value; |
| decoder->top = top; |
| } |
| else if ( op == op_callothersubr ) /* callothersubr */ |
| { |
| FT_Int subr_no; |
| FT_Int arg_cnt; |
| |
| |
| #ifdef FT_DEBUG_LEVEL_TRACE |
| FT_TRACE4(( " callothersubr\n" )); |
| bol = TRUE; |
| #endif |
| |
| if ( top - decoder->stack < 2 ) |
| goto Stack_Underflow; |
| |
| top -= 2; |
| |
| subr_no = Fix2Int( top[1] ); |
| arg_cnt = Fix2Int( top[0] ); |
| |
| /***********************************************************/ |
| /* */ |
| /* remove all operands to callothersubr from the stack */ |
| /* */ |
| /* for handled othersubrs, where we know the number of */ |
| /* arguments, we increase the stack by the value of */ |
| /* known_othersubr_result_cnt */ |
| /* */ |
| /* for unhandled othersubrs the following pops adjust the */ |
| /* stack pointer as necessary */ |
| |
| if ( arg_cnt > top - decoder->stack ) |
| goto Stack_Underflow; |
| |
| top -= arg_cnt; |
| |
| known_othersubr_result_cnt = 0; |
| unknown_othersubr_result_cnt = 0; |
| |
| /* XXX TODO: The checks to `arg_count == <whatever>' */ |
| /* might not be correct; an othersubr expects a certain */ |
| /* number of operands on the PostScript stack (as opposed */ |
| /* to the T1 stack) but it doesn't have to put them there */ |
| /* by itself; previous othersubrs might have left the */ |
| /* operands there if they were not followed by an */ |
| /* appropriate number of pops */ |
| /* */ |
| /* On the other hand, Adobe Reader 7.0.8 for Linux doesn't */ |
| /* accept a font that contains charstrings like */ |
| /* */ |
| /* 100 200 2 20 callothersubr */ |
| /* 300 1 20 callothersubr pop */ |
| /* */ |
| /* Perhaps this is the reason why BuildCharArray exists. */ |
| |
| switch ( subr_no ) |
| { |
| case 0: /* end flex feature */ |
| if ( arg_cnt != 3 ) |
| goto Unexpected_OtherSubr; |
| |
| if ( decoder->flex_state == 0 || |
| decoder->num_flex_vectors != 7 ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " unexpected flex end\n" )); |
| goto Syntax_Error; |
| } |
| |
| /* the two `results' are popped by the following setcurrentpoint */ |
| top[0] = x; |
| top[1] = y; |
| known_othersubr_result_cnt = 2; |
| break; |
| |
| case 1: /* start flex feature */ |
| if ( arg_cnt != 0 ) |
| goto Unexpected_OtherSubr; |
| |
| decoder->flex_state = 1; |
| decoder->num_flex_vectors = 0; |
| if ( ( error = t1_builder_start_point( builder, x, y ) ) |
| != FT_Err_Ok || |
| ( error = t1_builder_check_points( builder, 6 ) ) |
| != FT_Err_Ok ) |
| goto Fail; |
| break; |
| |
| case 2: /* add flex vectors */ |
| { |
| FT_Int idx; |
| |
| |
| if ( arg_cnt != 0 ) |
| goto Unexpected_OtherSubr; |
| |
| if ( decoder->flex_state == 0 ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " missing flex start\n" )); |
| goto Syntax_Error; |
| } |
| |
| /* note that we should not add a point for index 0; */ |
| /* this will move our current position to the flex */ |
| /* point without adding any point to the outline */ |
| idx = decoder->num_flex_vectors++; |
| if ( idx > 0 && idx < 7 ) |
| t1_builder_add_point( builder, |
| x, |
| y, |
| (FT_Byte)( idx == 3 || idx == 6 ) ); |
| } |
| break; |
| |
| case 3: /* change hints */ |
| if ( arg_cnt != 1 ) |
| goto Unexpected_OtherSubr; |
| |
| known_othersubr_result_cnt = 1; |
| |
| if ( hinter ) |
| hinter->reset( hinter->hints, |
| (FT_UInt)builder->current->n_points ); |
| break; |
| |
| case 12: |
| case 13: |
| /* counter control hints, clear stack */ |
| top = decoder->stack; |
| break; |
| |
| case 14: |
| case 15: |
| case 16: |
| case 17: |
| case 18: /* multiple masters */ |
| { |
| PS_Blend blend = decoder->blend; |
| FT_UInt num_points, nn, mm; |
| FT_Long* delta; |
| FT_Long* values; |
| |
| |
| if ( !blend ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " unexpected multiple masters operator\n" )); |
| goto Syntax_Error; |
| } |
| |
| num_points = (FT_UInt)subr_no - 13 + ( subr_no == 18 ); |
| if ( arg_cnt != (FT_Int)( num_points * blend->num_designs ) ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " incorrect number of multiple masters arguments\n" )); |
| goto Syntax_Error; |
| } |
| |
| /* We want to compute */ |
| /* */ |
| /* a0*w0 + a1*w1 + ... + ak*wk */ |
| /* */ |
| /* but we only have a0, a1-a0, a2-a0, ..., ak-a0. */ |
| /* */ |
| /* However, given that w0 + w1 + ... + wk == 1, we can */ |
| /* rewrite it easily as */ |
| /* */ |
| /* a0 + (a1-a0)*w1 + (a2-a0)*w2 + ... + (ak-a0)*wk */ |
| /* */ |
| /* where k == num_designs-1. */ |
| /* */ |
| /* I guess that's why it's written in this `compact' */ |
| /* form. */ |
| /* */ |
| delta = top + num_points; |
| values = top; |
| for ( nn = 0; nn < num_points; nn++ ) |
| { |
| FT_Long tmp = values[0]; |
| |
| |
| for ( mm = 1; mm < blend->num_designs; mm++ ) |
| tmp += FT_MulFix( *delta++, blend->weight_vector[mm] ); |
| |
| *values++ = tmp; |
| } |
| |
| known_othersubr_result_cnt = (FT_Int)num_points; |
| break; |
| } |
| |
| case 19: |
| /* <idx> 1 19 callothersubr */ |
| /* => replace elements starting from index cvi( <idx> ) */ |
| /* of BuildCharArray with WeightVector */ |
| { |
| FT_Int idx; |
| PS_Blend blend = decoder->blend; |
| |
| |
| if ( arg_cnt != 1 || blend == NULL ) |
| goto Unexpected_OtherSubr; |
| |
| idx = Fix2Int( top[0] ); |
| |
| if ( idx < 0 || |
| (FT_UInt)idx + blend->num_designs > decoder->len_buildchar ) |
| goto Unexpected_OtherSubr; |
| |
| ft_memcpy( &decoder->buildchar[idx], |
| blend->weight_vector, |
| blend->num_designs * |
| sizeof ( blend->weight_vector[0] ) ); |
| } |
| break; |
| |
| case 20: |
| /* <arg1> <arg2> 2 20 callothersubr pop */ |
| /* ==> push <arg1> + <arg2> onto T1 stack */ |
| if ( arg_cnt != 2 ) |
| goto Unexpected_OtherSubr; |
| |
| top[0] += top[1]; /* XXX (over|under)flow */ |
| |
| known_othersubr_result_cnt = 1; |
| break; |
| |
| case 21: |
| /* <arg1> <arg2> 2 21 callothersubr pop */ |
| /* ==> push <arg1> - <arg2> onto T1 stack */ |
| if ( arg_cnt != 2 ) |
| goto Unexpected_OtherSubr; |
| |
| top[0] -= top[1]; /* XXX (over|under)flow */ |
| |
| known_othersubr_result_cnt = 1; |
| break; |
| |
| case 22: |
| /* <arg1> <arg2> 2 22 callothersubr pop */ |
| /* ==> push <arg1> * <arg2> onto T1 stack */ |
| if ( arg_cnt != 2 ) |
| goto Unexpected_OtherSubr; |
| |
| top[0] = FT_MulFix( top[0], top[1] ); |
| |
| known_othersubr_result_cnt = 1; |
| break; |
| |
| case 23: |
| /* <arg1> <arg2> 2 23 callothersubr pop */ |
| /* ==> push <arg1> / <arg2> onto T1 stack */ |
| if ( arg_cnt != 2 || top[1] == 0 ) |
| goto Unexpected_OtherSubr; |
| |
| top[0] = FT_DivFix( top[0], top[1] ); |
| |
| known_othersubr_result_cnt = 1; |
| break; |
| |
| case 24: |
| /* <val> <idx> 2 24 callothersubr */ |
| /* ==> set BuildCharArray[cvi( <idx> )] = <val> */ |
| { |
| FT_Int idx; |
| PS_Blend blend = decoder->blend; |
| |
| |
| if ( arg_cnt != 2 || blend == NULL ) |
| goto Unexpected_OtherSubr; |
| |
| idx = Fix2Int( top[1] ); |
| |
| if ( idx < 0 || (FT_UInt) idx >= decoder->len_buildchar ) |
| goto Unexpected_OtherSubr; |
| |
| decoder->buildchar[idx] = top[0]; |
| } |
| break; |
| |
| case 25: |
| /* <idx> 1 25 callothersubr pop */ |
| /* ==> push BuildCharArray[cvi( idx )] */ |
| /* onto T1 stack */ |
| { |
| FT_Int idx; |
| PS_Blend blend = decoder->blend; |
| |
| |
| if ( arg_cnt != 1 || blend == NULL ) |
| goto Unexpected_OtherSubr; |
| |
| idx = Fix2Int( top[0] ); |
| |
| if ( idx < 0 || (FT_UInt) idx >= decoder->len_buildchar ) |
| goto Unexpected_OtherSubr; |
| |
| top[0] = decoder->buildchar[idx]; |
| } |
| |
| known_othersubr_result_cnt = 1; |
| break; |
| |
| #if 0 |
| case 26: |
| /* <val> mark <idx> ==> set BuildCharArray[cvi( <idx> )] = <val>, */ |
| /* leave mark on T1 stack */ |
| /* <val> <idx> ==> set BuildCharArray[cvi( <idx> )] = <val> */ |
| XXX which routine has left its mark on the (PostScript) stack?; |
| break; |
| #endif |
| |
| case 27: |
| /* <res1> <res2> <val1> <val2> 4 27 callothersubr pop */ |
| /* ==> push <res1> onto T1 stack if <val1> <= <val2>, */ |
| /* otherwise push <res2> */ |
| if ( arg_cnt != 4 ) |
| goto Unexpected_OtherSubr; |
| |
| if ( top[2] > top[3] ) |
| top[0] = top[1]; |
| |
| known_othersubr_result_cnt = 1; |
| break; |
| |
| case 28: |
| /* 0 28 callothersubr pop */ |
| /* => push random value from interval [0, 1) onto stack */ |
| if ( arg_cnt != 0 ) |
| goto Unexpected_OtherSubr; |
| |
| { |
| FT_Fixed Rand; |
| |
| |
| Rand = seed; |
| if ( Rand >= 0x8000L ) |
| Rand++; |
| |
| top[0] = Rand; |
| |
| seed = FT_MulFix( seed, 0x10000L - seed ); |
| if ( seed == 0 ) |
| seed += 0x2873; |
| } |
| |
| known_othersubr_result_cnt = 1; |
| break; |
| |
| default: |
| if ( arg_cnt >= 0 && subr_no >= 0 ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " unknown othersubr [%d %d], wish me luck\n", |
| arg_cnt, subr_no )); |
| unknown_othersubr_result_cnt = arg_cnt; |
| break; |
| } |
| /* fall through */ |
| |
| Unexpected_OtherSubr: |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " invalid othersubr [%d %d]\n", arg_cnt, subr_no )); |
| goto Syntax_Error; |
| } |
| |
| top += known_othersubr_result_cnt; |
| |
| decoder->top = top; |
| } |
| else /* general operator */ |
| { |
| FT_Int num_args = t1_args_count[op]; |
| |
| |
| FT_ASSERT( num_args >= 0 ); |
| |
| if ( top - decoder->stack < num_args ) |
| goto Stack_Underflow; |
| |
| /* XXX Operators usually take their operands from the */ |
| /* bottom of the stack, i.e., the operands are */ |
| /* decoder->stack[0], ..., decoder->stack[num_args - 1]; */ |
| /* only div, callsubr, and callothersubr are different. */ |
| /* In practice it doesn't matter (?). */ |
| |
| #ifdef FT_DEBUG_LEVEL_TRACE |
| |
| switch ( op ) |
| { |
| case op_callsubr: |
| case op_div: |
| case op_callothersubr: |
| case op_pop: |
| case op_return: |
| break; |
| |
| default: |
| if ( top - decoder->stack != num_args ) |
| FT_TRACE0(( "t1_decoder_parse_charstrings:" |
| " too much operands on the stack" |
| " (seen %d, expected %d)\n", |
| top - decoder->stack, num_args )); |
| break; |
| } |
| |
| #endif /* FT_DEBUG_LEVEL_TRACE */ |
| |
| top -= num_args; |
| |
| switch ( op ) |
| { |
| case op_endchar: |
| FT_TRACE4(( " endchar\n" )); |
| |
| t1_builder_close_contour( builder ); |
| |
| /* close hints recording session */ |
| if ( hinter ) |
| { |
| if ( hinter->close( hinter->hints, |
| (FT_UInt)builder->current->n_points ) ) |
| goto Syntax_Error; |
| |
| /* apply hints to the loaded glyph outline now */ |
| error = hinter->apply( hinter->hints, |
| builder->current, |
| (PSH_Globals)builder->hints_globals, |
| decoder->hint_mode ); |
| if ( error ) |
| goto Fail; |
| } |
| |
| /* add current outline to the glyph slot */ |
| FT_GlyphLoader_Add( builder->loader ); |
| |
| /* the compiler should optimize away this empty loop but ... */ |
| |
| #ifdef FT_DEBUG_LEVEL_TRACE |
| |
| if ( decoder->len_buildchar > 0 ) |
| { |
| FT_UInt i; |
| |
| |
| FT_TRACE4(( "BuildCharArray = [ " )); |
| |
| for ( i = 0; i < decoder->len_buildchar; ++i ) |
| FT_TRACE4(( "%d ", decoder->buildchar[i] )); |
| |
| FT_TRACE4(( "]\n" )); |
| } |
| |
| #endif /* FT_DEBUG_LEVEL_TRACE */ |
| |
| FT_TRACE4(( "\n" )); |
| |
| /* return now! */ |
| return FT_Err_Ok; |
| |
| case op_hsbw: |
| FT_TRACE4(( " hsbw" )); |
| |
| builder->parse_state = T1_Parse_Have_Width; |
| |
| builder->left_bearing.x += top[0]; |
| builder->advance.x = top[1]; |
| builder->advance.y = 0; |
| |
| orig_x = x = builder->pos_x + top[0]; |
| orig_y = y = builder->pos_y; |
| |
| FT_UNUSED( orig_y ); |
| |
| /* the `metrics_only' indicates that we only want to compute */ |
| /* the glyph's metrics (lsb + advance width), not load the */ |
| /* rest of it; so exit immediately */ |
| if ( builder->metrics_only ) |
| return FT_Err_Ok; |
| |
| break; |
| |
| case op_seac: |
| return t1operator_seac( decoder, |
| top[0], |
| top[1], |
| top[2], |
| Fix2Int( top[3] ), |
| Fix2Int( top[4] ) ); |
| |
| case op_sbw: |
| FT_TRACE4(( " sbw" )); |
| |
| builder->parse_state = T1_Parse_Have_Width; |
| |
| builder->left_bearing.x += top[0]; |
| builder->left_bearing.y += top[1]; |
| builder->advance.x = top[2]; |
| builder->advance.y = top[3]; |
| |
| x = builder->pos_x + top[0]; |
| y = builder->pos_y + top[1]; |
| |
| /* the `metrics_only' indicates that we only want to compute */ |
| /* the glyph's metrics (lsb + advance width), not load the */ |
| /* rest of it; so exit immediately */ |
| if ( builder->metrics_only ) |
| return FT_Err_Ok; |
| |
| break; |
| |
| case op_closepath: |
| FT_TRACE4(( " closepath" )); |
| |
| /* if there is no path, `closepath' is a no-op */ |
| if ( builder->parse_state == T1_Parse_Have_Path || |
| builder->parse_state == T1_Parse_Have_Moveto ) |
| t1_builder_close_contour( builder ); |
| |
| builder->parse_state = T1_Parse_Have_Width; |
| break; |
| |
| case op_hlineto: |
| FT_TRACE4(( " hlineto" )); |
| |
| if ( ( error = t1_builder_start_point( builder, x, y ) ) |
| != FT_Err_Ok ) |
| goto Fail; |
| |
| x += top[0]; |
| goto Add_Line; |
| |
| case op_hmoveto: |
| FT_TRACE4(( " hmoveto" )); |
| |
| x += top[0]; |
| if ( !decoder->flex_state ) |
| { |
| if ( builder->parse_state == T1_Parse_Start ) |
| goto Syntax_Error; |
| builder->parse_state = T1_Parse_Have_Moveto; |
| } |
| break; |
| |
| case op_hvcurveto: |
| FT_TRACE4(( " hvcurveto" )); |
| |
| if ( ( error = t1_builder_start_point( builder, x, y ) ) |
| != FT_Err_Ok || |
| ( error = t1_builder_check_points( builder, 3 ) ) |
| != FT_Err_Ok ) |
| goto Fail; |
| |
| x += top[0]; |
| t1_builder_add_point( builder, x, y, 0 ); |
| x += top[1]; |
| y += top[2]; |
| t1_builder_add_point( builder, x, y, 0 ); |
| y += top[3]; |
| t1_builder_add_point( builder, x, y, 1 ); |
| break; |
| |
| case op_rlineto: |
| FT_TRACE4(( " rlineto" )); |
| |
| if ( ( error = t1_builder_start_point( builder, x, y ) ) |
| != FT_Err_Ok ) |
| goto Fail; |
| |
| x += top[0]; |
| y += top[1]; |
| |
| Add_Line: |
| if ( ( error = t1_builder_add_point1( builder, x, y ) ) |
| != FT_Err_Ok ) |
| goto Fail; |
| break; |
| |
| case op_rmoveto: |
| FT_TRACE4(( " rmoveto" )); |
| |
| x += top[0]; |
| y += top[1]; |
| if ( !decoder->flex_state ) |
| { |
| if ( builder->parse_state == T1_Parse_Start ) |
| goto Syntax_Error; |
| builder->parse_state = T1_Parse_Have_Moveto; |
| } |
| break; |
| |
| case op_rrcurveto: |
| FT_TRACE4(( " rrcurveto" )); |
| |
| if ( ( error = t1_builder_start_point( builder, x, y ) ) |
| != FT_Err_Ok || |
| ( error = t1_builder_check_points( builder, 3 ) ) |
| != FT_Err_Ok ) |
| goto Fail; |
| |
| x += top[0]; |
| y += top[1]; |
| t1_builder_add_point( builder, x, y, 0 ); |
| |
| x += top[2]; |
| y += top[3]; |
| t1_builder_add_point( builder, x, y, 0 ); |
| |
| x += top[4]; |
| y += top[5]; |
| t1_builder_add_point( builder, x, y, 1 ); |
| break; |
| |
| case op_vhcurveto: |
| FT_TRACE4(( " vhcurveto" )); |
| |
| if ( ( error = t1_builder_start_point( builder, x, y ) ) |
| != FT_Err_Ok || |
| ( error = t1_builder_check_points( builder, 3 ) ) |
| != FT_Err_Ok ) |
| goto Fail; |
| |
| y += top[0]; |
| t1_builder_add_point( builder, x, y, 0 ); |
| x += top[1]; |
| y += top[2]; |
| t1_builder_add_point( builder, x, y, 0 ); |
| x += top[3]; |
| t1_builder_add_point( builder, x, y, 1 ); |
| break; |
| |
| case op_vlineto: |
| FT_TRACE4(( " vlineto" )); |
| |
| if ( ( error = t1_builder_start_point( builder, x, y ) ) |
| != FT_Err_Ok ) |
| goto Fail; |
| |
| y += top[0]; |
| goto Add_Line; |
| |
| case op_vmoveto: |
| FT_TRACE4(( " vmoveto" )); |
| |
| y += top[0]; |
| if ( !decoder->flex_state ) |
| { |
| if ( builder->parse_state == T1_Parse_Start ) |
| goto Syntax_Error; |
| builder->parse_state = T1_Parse_Have_Moveto; |
| } |
| break; |
| |
| case op_div: |
| FT_TRACE4(( " div" )); |
| |
| /* if `large_int' is set, we divide unscaled numbers; */ |
| /* otherwise, we divide numbers in 16.16 format -- */ |
| /* in both cases, it is the same operation */ |
| *top = FT_DivFix( top[0], top[1] ); |
| ++top; |
| |
| large_int = FALSE; |
| break; |
| |
| case op_callsubr: |
| { |
| FT_Int idx; |
| |
| |
| FT_TRACE4(( " callsubr" )); |
| |
| idx = Fix2Int( top[0] ); |
| if ( idx < 0 || idx >= decoder->num_subrs ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " invalid subrs index\n" )); |
| goto Syntax_Error; |
| } |
| |
| if ( zone - decoder->zones >= T1_MAX_SUBRS_CALLS ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " too many nested subrs\n" )); |
| goto Syntax_Error; |
| } |
| |
| zone->cursor = ip; /* save current instruction pointer */ |
| |
| zone++; |
| |
| /* The Type 1 driver stores subroutines without the seed bytes. */ |
| /* The CID driver stores subroutines with seed bytes. This */ |
| /* case is taken care of when decoder->subrs_len == 0. */ |
| zone->base = decoder->subrs[idx]; |
| |
| if ( decoder->subrs_len ) |
| zone->limit = zone->base + decoder->subrs_len[idx]; |
| else |
| { |
| /* We are using subroutines from a CID font. We must adjust */ |
| /* for the seed bytes. */ |
| zone->base += ( decoder->lenIV >= 0 ? decoder->lenIV : 0 ); |
| zone->limit = decoder->subrs[idx + 1]; |
| } |
| |
| zone->cursor = zone->base; |
| |
| if ( !zone->base ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " invoking empty subrs\n" )); |
| goto Syntax_Error; |
| } |
| |
| decoder->zone = zone; |
| ip = zone->base; |
| limit = zone->limit; |
| break; |
| } |
| |
| case op_pop: |
| FT_TRACE4(( " pop" )); |
| |
| if ( known_othersubr_result_cnt > 0 ) |
| { |
| known_othersubr_result_cnt--; |
| /* ignore, we pushed the operands ourselves */ |
| break; |
| } |
| |
| if ( unknown_othersubr_result_cnt == 0 ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " no more operands for othersubr\n" )); |
| goto Syntax_Error; |
| } |
| |
| unknown_othersubr_result_cnt--; |
| top++; /* `push' the operand to callothersubr onto the stack */ |
| break; |
| |
| case op_return: |
| FT_TRACE4(( " return" )); |
| |
| if ( zone <= decoder->zones ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " unexpected return\n" )); |
| goto Syntax_Error; |
| } |
| |
| zone--; |
| ip = zone->cursor; |
| limit = zone->limit; |
| decoder->zone = zone; |
| break; |
| |
| case op_dotsection: |
| FT_TRACE4(( " dotsection" )); |
| |
| break; |
| |
| case op_hstem: |
| FT_TRACE4(( " hstem" )); |
| |
| /* record horizontal hint */ |
| if ( hinter ) |
| { |
| /* top[0] += builder->left_bearing.y; */ |
| hinter->stem( hinter->hints, 1, top ); |
| } |
| break; |
| |
| case op_hstem3: |
| FT_TRACE4(( " hstem3" )); |
| |
| /* record horizontal counter-controlled hints */ |
| if ( hinter ) |
| hinter->stem3( hinter->hints, 1, top ); |
| break; |
| |
| case op_vstem: |
| FT_TRACE4(( " vstem" )); |
| |
| /* record vertical hint */ |
| if ( hinter ) |
| { |
| top[0] += orig_x; |
| hinter->stem( hinter->hints, 0, top ); |
| } |
| break; |
| |
| case op_vstem3: |
| FT_TRACE4(( " vstem3" )); |
| |
| /* record vertical counter-controlled hints */ |
| if ( hinter ) |
| { |
| FT_Pos dx = orig_x; |
| |
| |
| top[0] += dx; |
| top[2] += dx; |
| top[4] += dx; |
| hinter->stem3( hinter->hints, 0, top ); |
| } |
| break; |
| |
| case op_setcurrentpoint: |
| FT_TRACE4(( " setcurrentpoint" )); |
| |
| /* From the T1 specification, section 6.4: */ |
| /* */ |
| /* The setcurrentpoint command is used only in */ |
| /* conjunction with results from OtherSubrs procedures. */ |
| |
| /* known_othersubr_result_cnt != 0 is already handled */ |
| /* above. */ |
| |
| /* Note, however, that both Ghostscript and Adobe */ |
| /* Distiller handle this situation by silently ignoring */ |
| /* the inappropriate `setcurrentpoint' instruction. So */ |
| /* we do the same. */ |
| #if 0 |
| |
| if ( decoder->flex_state != 1 ) |
| { |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " unexpected `setcurrentpoint'\n" )); |
| goto Syntax_Error; |
| } |
| else |
| ... |
| #endif |
| |
| x = top[0]; |
| y = top[1]; |
| decoder->flex_state = 0; |
| break; |
| |
| case op_unknown15: |
| FT_TRACE4(( " opcode_15" )); |
| /* nothing to do except to pop the two arguments */ |
| break; |
| |
| default: |
| FT_ERROR(( "t1_decoder_parse_charstrings:" |
| " unhandled opcode %d\n", op )); |
| goto Syntax_Error; |
| } |
| |
| /* XXX Operators usually clear the operand stack; */ |
| /* only div, callsubr, callothersubr, pop, and */ |
| /* return are different. */ |
| /* In practice it doesn't matter (?). */ |
| |
| decoder->top = top; |
| |
| #ifdef FT_DEBUG_LEVEL_TRACE |
| FT_TRACE4(( "\n" )); |
| bol = TRUE; |
| #endif |
| |
| } /* general operator processing */ |
| |
| } /* while ip < limit */ |
| |
| FT_TRACE4(( "..end..\n\n" )); |
| |
| Fail: |
| return error; |
| |
| Syntax_Error: |
| return FT_THROW( Syntax_Error ); |
| |
| Stack_Underflow: |
| return FT_THROW( Stack_Underflow ); |
| } |
| |
| |
| /* parse a single Type 1 glyph */ |
| FT_LOCAL_DEF( FT_Error ) |
| t1_decoder_parse_glyph( T1_Decoder decoder, |
| FT_UInt glyph ) |
| { |
| return decoder->parse_callback( decoder, glyph ); |
| } |
| |
| |
| /* initialize T1 decoder */ |
| FT_LOCAL_DEF( FT_Error ) |
| t1_decoder_init( T1_Decoder decoder, |
| FT_Face face, |
| FT_Size size, |
| FT_GlyphSlot slot, |
| FT_Byte** glyph_names, |
| PS_Blend blend, |
| FT_Bool hinting, |
| FT_Render_Mode hint_mode, |
| T1_Decoder_Callback parse_callback ) |
| { |
| FT_MEM_ZERO( decoder, sizeof ( *decoder ) ); |
| |
| /* retrieve PSNames interface from list of current modules */ |
| { |
| FT_Service_PsCMaps psnames; |
| |
| |
| FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS ); |
| if ( !psnames ) |
| { |
| FT_ERROR(( "t1_decoder_init:" |
| " the `psnames' module is not available\n" )); |
| return FT_THROW( Unimplemented_Feature ); |
| } |
| |
| decoder->psnames = psnames; |
| } |
| |
| t1_builder_init( &decoder->builder, face, size, slot, hinting ); |
| |
| /* decoder->buildchar and decoder->len_buildchar have to be */ |
| /* initialized by the caller since we cannot know the length */ |
| /* of the BuildCharArray */ |
| |
| decoder->num_glyphs = (FT_UInt)face->num_glyphs; |
| decoder->glyph_names = glyph_names; |
| decoder->hint_mode = hint_mode; |
| decoder->blend = blend; |
| decoder->parse_callback = parse_callback; |
| |
| decoder->funcs = t1_decoder_funcs; |
| |
| return FT_Err_Ok; |
| } |
| |
| |
| /* finalize T1 decoder */ |
| FT_LOCAL_DEF( void ) |
| t1_decoder_done( T1_Decoder decoder ) |
| { |
| t1_builder_done( &decoder->builder ); |
| } |
| |
| |
| /* END */ |