diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h index 1b3fb0a6b..4a4174fd8 100644 --- a/include/freetype/freetype.h +++ b/include/freetype/freetype.h @@ -645,7 +645,7 @@ /* */ /* */ /* A bit-field constant, used to indicate that a given face contains */ - /* fixed-width characters (like Courier, MonoType, etc). */ + /* fixed-width characters (like Courier, Lucida, MonoType, etc..) */ /* */ #define FT_FACE_FLAG_FIXED_WIDTH 4 diff --git a/include/freetype/internal/ftdebug.h b/include/freetype/internal/ftdebug.h index 453306552..c8c32ea14 100644 --- a/include/freetype/internal/ftdebug.h +++ b/include/freetype/internal/ftdebug.h @@ -170,7 +170,7 @@ /* print a message and exit */ FT_EXPORT_DEF(void) FT_Panic ( const char* fmt, ... ); -#define FT_ERROR(varformat) do { FT_XCAT( FT_Message, varformat ) } while(0) +#define FT_ERROR(varformat) do { FT_XCAT( FT_Message, varformat ); } while(0) #endif /* FT_DEBUG_LEVEL_TRACE || FT_DEBUG_LEVEL_ERROR */ diff --git a/include/freetype/t1tables.h b/include/freetype/t1tables.h index ef688ef77..61187a287 100644 --- a/include/freetype/t1tables.h +++ b/include/freetype/t1tables.h @@ -140,8 +140,13 @@ } T1_Blend_Flags; /* maximum number of multiple-masters designs, per-se the spec */ - #define T1_MAX_MM_DESIGNS 16 - #define T1_MAX_MM_AXIS 4 + #define T1_MAX_MM_DESIGNS 16 + + /* maximum number of multiple-masters axis, per-se the spec */ + #define T1_MAX_MM_AXIS 4 + + /* maximum number of elements in a design map */ + #define T1_MAX_MM_MAP_POINTS 20 /* this structure is used to store the BlendDesignMap entry for an axis */ typedef struct T1_DesignMap_ @@ -152,6 +157,7 @@ } T1_DesignMap; + typedef struct T1_Blend_ { FT_UInt num_designs; @@ -161,6 +167,9 @@ FT_Fixed* design_pos[ T1_MAX_MM_DESIGNS ]; T1_DesignMap design_map[ T1_MAX_MM_AXIS ]; + FT_Fixed* weight_vector; + FT_Fixed* default_weight_vector; + T1_FontInfo* font_infos[ T1_MAX_MM_DESIGNS+1 ]; T1_Private* privates [ T1_MAX_MM_DESIGNS+1 ]; @@ -173,7 +182,7 @@ typedef struct CID_FontDict_ { T1_FontInfo font_info; - T1_Private private; + T1_Private private_dict; FT_UInt num_subrs; FT_ULong subrmap_offset; diff --git a/src/type1z/t1gload.c b/src/type1z/t1gload.c index 9fbb56f7d..c34c51c3e 100644 --- a/src/type1z/t1gload.c +++ b/src/type1z/t1gload.c @@ -205,6 +205,7 @@ decoder->zone = 0; decoder->flex_state = 0; decoder->num_flex_vectors = 0; + decoder->blend = 0; /* Clear loader */ MEM_Set( &decoder->builder, 0, sizeof(decoder->builder) ); @@ -830,6 +831,72 @@ 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 */ + { + T1_Blend* blend = decoder->blend; + T1_UInt num_points, nn, mm; + T1_Int* delta; + T1_Int* values; + + if (!blend) + { + FT_ERROR(( "T1.Parse_CharStrings: unexpected multiple masters operator !!\n" )); + goto Syntax_Error; + } + + num_points = top[1] - 13 + (top[1] == 18); + if (top[0] != num_points*blend->num_designs) + { + FT_ERROR(( "T1.Parse_CharStrings: incorrect number of mm arguments\n" )); + goto Syntax_Error; + } + + top -= blend->num_designs*num_points; + if (top < decoder->stack) + goto Stack_Underflow; + + /* we want to compute: */ + /* */ + /* a0*w0 + a1*w1 + ... + ak*wk */ + /* */ + /* but we only have the 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++ ) + { + T1_Int x = values[0]; + for ( mm = 1; mm < blend->num_designs; mm++ ) + x += FT_MulFix( *delta++, blend->weight_vector[mm] ); + + *values++ = x; + } + /* note that "top" will be incremented later by calls to "pop" */ + break; + } + default: Unexpected_OtherSubr: FT_ERROR(( "T1.Parse_CharStrings: invalid othersubr [%d %d]!!\n", @@ -1086,8 +1153,9 @@ case op_pop: /****************************************************/ { FT_TRACE4(( " pop" )); - FT_ERROR(( "T1.Parse_CharStrings : unexpected POP\n" )); - goto Syntax_Error; + /* theorically, the arguments are already on the stack */ + top++; + break; } @@ -1202,6 +1270,7 @@ T1_Init_Decoder( &decoder ); T1_Init_Builder( &decoder.builder, face, 0, 0 ); + decoder.blend = face->blend; decoder.builder.metrics_only = 1; decoder.builder.load_points = 0; @@ -1270,6 +1339,7 @@ T1_Init_Decoder( &decoder ); T1_Init_Builder( &decoder.builder, face, size, glyph ); + decoder.blend = ((T1_Face)glyph->root.face)->blend; decoder.builder.no_recurse = (FT_Bool)!!(load_flags & FT_LOAD_NO_RECURSE); /* now load the unscaled outline */ diff --git a/src/type1z/t1gload.h b/src/type1z/t1gload.h index 0ac000a62..0a87ab09d 100644 --- a/src/type1z/t1gload.h +++ b/src/type1z/t1gload.h @@ -139,6 +139,8 @@ T1_Int num_flex_vectors; T1_Vector flex_vectors[7]; + T1_Blend* blend; /* for multiple masters */ + } T1_Decoder; diff --git a/src/type1z/t1load.c b/src/type1z/t1load.c index 86c40e965..b3beb3e8d 100644 --- a/src/type1z/t1load.c +++ b/src/type1z/t1load.c @@ -102,9 +102,12 @@ { /* allocate the blend "private" and "font_info" dictionaries */ if ( ALLOC_ARRAY( blend->font_infos[1], num_designs, T1_FontInfo ) || - ALLOC_ARRAY( blend->privates[1], num_designs, T1_Private ) ) + ALLOC_ARRAY( blend->privates[1], num_designs, T1_Private ) || + ALLOC_ARRAY( blend->weight_vector, num_designs*2, FT_Fixed ) ) goto Exit; + blend->default_weight_vector = blend->weight_vector + num_designs; + blend->font_infos[0] = &face->type1.font_info; blend->privates [0] = &face->type1.private_dict; blend->num_designs = num_designs; @@ -144,7 +147,7 @@ } - static void t1_done_blend( T1_Face face ) + LOCAL_FUNC void T1_Done_Blend( T1_Face face ) { FT_Memory memory = face->root.memory; T1_Blend* blend = face->blend; @@ -169,6 +172,10 @@ blend->font_infos[n] = 0; } + /* release weight vectors */ + FREE( blend->weight_vector ); + blend->default_weight_vector = 0; + /* release axis names */ for ( n = 0; n < num_axis; n++ ) FREE( blend->axis_names[n] ); @@ -178,7 +185,6 @@ { T1_DesignMap* dmap = blend->design_map + n; FREE( dmap->design_points ); - FREE( dmap->blend_points ); dmap->num_points = 0; } @@ -186,6 +192,261 @@ } } + + static void parse_blend_axis_types( T1_Face face, T1_Loader* loader ) + { + T1_Token_Rec axis_tokens[ T1_MAX_MM_AXIS ]; + T1_Int n, num_axis; + FT_Error error = 0; + T1_Blend* blend; + FT_Memory memory; + + /* take an array of objects */ + T1_ToTokenArray( &loader->parser, axis_tokens, T1_MAX_MM_AXIS, &num_axis ); + if (num_axis <= 0 || num_axis > T1_MAX_MM_AXIS) + { + FT_ERROR(( "T1.parse_blend_axis_types: incorrect number of axis: %d\n", + num_axis )); + error = FT_Err_Invalid_File_Format; + goto Exit; + } + + /* allocate blend if necessary */ + error = t1_allocate_blend( face, 0, (T1_UInt)num_axis ); + if (error) goto Exit; + + blend = face->blend; + memory = face->root.memory; + + /* each token is an immediate containing the name of the axis */ + for ( n = 0; n < num_axis; n++ ) + { + T1_Token_Rec* token = axis_tokens + n; + T1_Byte* name; + T1_Int len; + + /* skip first slash, if any */ + if (token->start[0] == '/') + token->start++; + + len = token->limit - token->start; + if (len <= 0) + { + error = FT_Err_Invalid_File_Format; + goto Exit; + } + + if ( ALLOC( blend->axis_names[n], len+1 ) ) + goto Exit; + + name = (T1_Byte*)blend->axis_names[n]; + MEM_Copy( name, token->start, len ); + name[len] = 0; + } + + Exit: + loader->parser.error = error; + } + + + static void parse_blend_design_positions( T1_Face face, T1_Loader* loader ) + { + T1_Token_Rec design_tokens[ T1_MAX_MM_DESIGNS ]; + T1_Int num_designs; + T1_Int num_axis; + T1_Parser* parser = &loader->parser; + + FT_Error error = 0; + T1_Blend* blend; + + /* get the array of design tokens - compute number of designs */ + T1_ToTokenArray( parser, design_tokens, T1_MAX_MM_DESIGNS, &num_designs ); + if (num_designs <= 0 || num_designs > T1_MAX_MM_DESIGNS) + { + FT_ERROR(( "T1.design positions: incorrect number of designs: %d\n", + num_designs )); + error = FT_Err_Invalid_File_Format; + goto Exit; + } + + { + T1_Byte* old_cursor = parser->cursor; + T1_Byte* old_limit = parser->limit; + T1_UInt n; + + blend = face->blend; + for ( n = 0; n < (T1_UInt)num_designs; n++ ) + { + T1_Token_Rec axis_tokens[ T1_MAX_MM_DESIGNS ]; + T1_Token_Rec* token; + T1_Int axis, n_axis; + + /* read axis/coordinates tokens */ + token = design_tokens + n; + parser->cursor = token->start - 1; + parser->limit = token->limit + 1; + T1_ToTokenArray( parser, axis_tokens, T1_MAX_MM_AXIS, &n_axis ); + + if (n == 0) + { + num_axis = n_axis; + error = t1_allocate_blend( face, num_designs, num_axis ); + if (error) goto Exit; + blend = face->blend; + } + else if (n_axis != num_axis) + { + FT_ERROR(( "T1.design_positions: incorrect table\n" )); + error = FT_Err_Invalid_File_Format; + goto Exit; + } + + /* now, read each axis token into the design position */ + for (axis = 0; axis < n_axis; axis++ ) + { + T1_Token_Rec* token2 = axis_tokens + axis; + parser->cursor = token2->start; + parser->limit = token2->limit; + blend->design_pos[n][axis] = T1_ToFixed( parser, 0 ); + } + } + + loader->parser.cursor = old_cursor; + loader->parser.limit = old_limit; + } + + Exit: + loader->parser.error = error; + } + + static void parse_blend_design_map( T1_Face face, T1_Loader* loader ) + { + FT_Error error = 0; + T1_Parser* parser = &loader->parser; + T1_Blend* blend; + T1_Token_Rec axis_tokens[ T1_MAX_MM_AXIS ]; + T1_Int n, num_axis; + T1_Byte* old_cursor; + T1_Byte* old_limit; + FT_Memory memory = face->root.memory; + + T1_ToTokenArray( parser, axis_tokens, T1_MAX_MM_AXIS, &num_axis ); + if (num_axis <= 0 || num_axis > T1_MAX_MM_AXIS) + { + FT_ERROR(( "T1.design map: incorrect number of axis: %d\n", + num_axis )); + error = FT_Err_Invalid_File_Format; + goto Exit; + } + old_cursor = parser->cursor; + old_limit = parser->limit; + + error = t1_allocate_blend( face, 0, num_axis ); + if (error) goto Exit; + blend = face->blend; + + /* now, read each axis design map */ + for ( n = 0; n < num_axis; n++ ) + { + T1_DesignMap* map = blend->design_map + n; + T1_Token_Rec* token; + T1_Int p, num_points; + + token = axis_tokens + n; + parser->cursor = token->start; + parser->limit = token->limit; + + /* count the number of map points */ + { + T1_Byte* p = token->start; + T1_Byte* limit = token->limit; + + num_points = 0; + for ( ; p < limit; p++ ) + if (p[0] == '[') + num_points++; + } + if (num_points <= 0 || num_points > T1_MAX_MM_MAP_POINTS) + { + FT_ERROR(( "T1.design map: incorrect table\n" )); + error = FT_Err_Invalid_File_Format; + goto Exit; + } + + /* allocate design map data */ + if ( ALLOC_ARRAY( map->design_points, num_points*2, FT_Fixed ) ) + goto Exit; + map->blend_points = map->design_points + num_points; + map->num_points = (FT_Byte)num_points; + + for ( p = 0; p < num_points; p++ ) + { + map->design_points[p] = T1_ToInt( parser ); + map->blend_points [p] = T1_ToFixed( parser, 0 ); + } + } + + parser->cursor = old_cursor; + parser->limit = old_limit; + Exit: + parser->error = error; + } + + static void parse_weight_vector( T1_Face face, T1_Loader* loader ) + { + FT_Error error = 0; + T1_Parser* parser = &loader->parser; + T1_Blend* blend = face->blend; + T1_Token_Rec master; + T1_UInt n; + T1_Byte* old_cursor; + T1_Byte* old_limit; + + if (!blend || blend->num_designs == 0) + { + FT_ERROR(( "t1.weight_vector: too early !!\n" )); + error = FT_Err_Invalid_File_Format; + goto Exit; + } + + T1_ToToken( parser, &master ); + if (master.type != t1_token_array) + { + FT_ERROR(( "t1.weight_vector: incorrect format !!\n" )); + error = FT_Err_Invalid_File_Format; + goto Exit; + } + + old_cursor = parser->cursor; + old_limit = parser->limit; + + parser->cursor = master.start; + parser->limit = master.limit; + for ( n = 0; n < blend->num_designs; n++ ) + { + blend->default_weight_vector[n] = + blend->weight_vector[n] = T1_ToFixed( parser, 0 ); + } + + parser->cursor = old_cursor; + parser->limit = old_limit; + Exit: + parser->error = error; + } + + /* the keyword /shareddict appears in some multiple master fonts with a lot */ + /* of Postscript garbage behind it (that's completely out of spec !!), we */ + /* detect it and terminate the parsing */ + static void parse_shared_dict( T1_Face face, T1_Loader* loader ) + { + T1_Parser* parser = &loader->parser; + + UNUSED(face); + + parser->cursor = parser->limit; + parser->error = 0; + } + /***************************************************************************/ /***************************************************************************/ /***** *****/ @@ -802,6 +1063,13 @@ T1_KEYWORD_CALLBACK( "Encoding", parse_encoding ), T1_KEYWORD_CALLBACK( "Subrs", parse_subrs ), T1_KEYWORD_CALLBACK( "CharStrings", parse_charstrings ), +#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT + T1_KEYWORD_CALLBACK( "BlendDesignPositions", parse_blend_design_positions ), + T1_KEYWORD_CALLBACK( "BlendDesignMap", parse_blend_design_map ), + T1_KEYWORD_CALLBACK( "BlendAxisTypes", parse_blend_axis_types ), + T1_KEYWORD_CALLBACK( "WeightVector", parse_weight_vector ), + T1_KEYWORD_CALLBACK( "shareddict", parse_shared_dict ), +#endif T1_KEYWORD_CALLBACK( 0, 0 ) }; @@ -864,7 +1132,7 @@ while (cur2 < limit && is_alpha(*cur2)) cur2++; len = cur2-cur; - if (len > 0 && len < 20) + if (len > 0 && len < 22) { if (!loader->fontdata) { diff --git a/src/type1z/t1load.h b/src/type1z/t1load.h index 811dfa526..47ca78b39 100644 --- a/src/type1z/t1load.h +++ b/src/type1z/t1load.h @@ -46,6 +46,10 @@ LOCAL_DEF T1_Error T1_Open_Face( T1_Face face ); +#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT + LOCAL_DEF + void T1_Done_Blend( T1_Face face ); +#endif #ifdef __cplusplus } diff --git a/src/type1z/t1objs.c b/src/type1z/t1objs.c index 87b94d2c8..46327511c 100644 --- a/src/type1z/t1objs.c +++ b/src/type1z/t1objs.c @@ -138,6 +138,12 @@ { memory = face->root.memory; +#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT + /* release multiple masters information */ + T1_Done_Blend( face ); + face->blend = 0; +#endif + /* release font info strings */ { T1_FontInfo* info = &type1->font_info; diff --git a/src/type1z/t1parse.c b/src/type1z/t1parse.c index 69035b3f6..27581062c 100644 --- a/src/type1z/t1parse.c +++ b/src/type1z/t1parse.c @@ -621,6 +621,7 @@ } +#if 0 static T1_String* t1_tostring( T1_Byte* *cursor, T1_Byte* limit, FT_Memory memory ) { @@ -670,6 +671,7 @@ *cursor = cur; return result; } +#endif static int t1_tobool( T1_Byte* *cursor, T1_Byte* limit ) @@ -732,7 +734,7 @@ /* if this is an array, and we have no blend, an error occurs */ if (max_objects == 0) goto Fail; - + count = max_objects; index = 1; } @@ -894,6 +896,7 @@ } +#if 0 LOCAL_FUNC T1_String* T1_ToString( T1_Parser* parser ) { @@ -906,7 +909,7 @@ { return t1_tobool( &parser->cursor, parser->limit ); } - +#endif static diff --git a/src/type1z/t1parse.h b/src/type1z/t1parse.h index fb25883e6..da80211a6 100644 --- a/src/type1z/t1parse.h +++ b/src/type1z/t1parse.h @@ -280,25 +280,14 @@ T1_Fixed* values, T1_Int power_ten ); +#if 0 LOCAL_DEF T1_String* T1_ToString( T1_Parser* parser ); - LOCAL_DEF T1_Bool T1_ToBool( T1_Parser* parser ); - -#if 0 - LOCAL_DEF - T1_Int T1_ToImmediate( T1_Parser* parser ); #endif -#if 0 - /* load a single field in an object */ - LOCAL_DEF - T1_Error T1_Load_Field( T1_Parser* parser, - void* object, - T1_Field_Rec* field ); -#endif