From 7c388ba4911d76856d85bc1b9e0059414c1b05c9 Mon Sep 17 00:00:00 2001 From: David Turner Date: Fri, 26 May 2000 02:07:40 +0000 Subject: [PATCH] added support for multiple master fonts in "type1z". It is now working, but there is no way currently to change the default weight vector (tested with custom vectors though). Note that you should remove the "type1" driver from the module list to be able to test it.. --- include/freetype/freetype.h | 2 +- include/freetype/internal/ftdebug.h | 2 +- include/freetype/t1tables.h | 15 +- src/type1z/t1gload.c | 74 +++++++- src/type1z/t1gload.h | 2 + src/type1z/t1load.c | 276 +++++++++++++++++++++++++++- src/type1z/t1load.h | 4 + src/type1z/t1objs.c | 6 + src/type1z/t1parse.c | 7 +- src/type1z/t1parse.h | 13 +- 10 files changed, 376 insertions(+), 25 deletions(-) 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