From 813aca51d28704f7ffc470721167738fa8decb3d Mon Sep 17 00:00:00 2001 From: Werner Lemberg Date: Sun, 14 Feb 2016 16:03:15 +0100 Subject: [PATCH] [cff] Make old CFF engine show MM CFFs (without variations). The new code only displays the first master in the font. * src/cff/cffgload.c (cff_decode_parse_charstrings): Add new parameter to allow function calls from dictionaries also. : Partially implement it. Update all callers. * src/cff/cffgload.h: Updated. * src/cff/cffparse.c (cff_parser_init): Add new parameter to pass the number of Multiple Master designs. Update all callers. (cff_parse_multiple_master): New function to rudimentarily parse operator. (cff_parser_run): Handle `T2' operator. * src/cff/cffparse.h: Updated. (CFF_ParserRec): Add `num_designs' field. * src/cff/cffload.c: Updated. * src/cff/cfftoken.h: Handle `MultipleMaster' operator. * src/cff/cfftypes.h (CFF_FontRecDictRec): Add `num_designs' field. * src/sfnt/sfobjs.c (sfnt_init_face): Don't handle `fvar' table for MM CFFs. --- ChangeLog | 30 ++++++++ src/cff/cffgload.c | 89 ++++++++++++++++++++--- src/cff/cffgload.h | 3 +- src/cff/cffload.c | 12 +++- src/cff/cffparse.c | 174 ++++++++++++++++++++++++++++++++++++++++++++- src/cff/cffparse.h | 5 +- src/cff/cfftoken.h | 10 ++- src/cff/cfftypes.h | 5 ++ src/sfnt/sfobjs.c | 4 ++ 9 files changed, 317 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index 21b5c9ef8..eb0097d7d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +2016-02-14 Werner Lemberg + + [cff] Make old CFF engine show MM CFFs (without variations). + + The new code only displays the first master in the font. + + * src/cff/cffgload.c (cff_decode_parse_charstrings): Add new + parameter to allow function calls from dictionaries also. + : Partially implement it. + Update all callers. + * src/cff/cffgload.h: Updated. + + * src/cff/cffparse.c (cff_parser_init): Add new parameter to pass the + number of Multiple Master designs. + Update all callers. + (cff_parse_multiple_master): New function to rudimentarily parse + operator. + (cff_parser_run): Handle `T2' operator. + * src/cff/cffparse.h: Updated. + (CFF_ParserRec): Add `num_designs' field. + + * src/cff/cffload.c: Updated. + + * src/cff/cfftoken.h: Handle `MultipleMaster' operator. + + * src/cff/cfftypes.h (CFF_FontRecDictRec): Add `num_designs' field. + + * src/sfnt/sfobjs.c (sfnt_init_face): Don't handle `fvar' table for + MM CFFs. + 2016-02-09 Werner Lemberg [docmaker] Don't emit trailing newlines. diff --git a/src/cff/cffgload.c b/src/cff/cffgload.c index df57d67e5..45f37a5f2 100644 --- a/src/cff/cffgload.c +++ b/src/cff/cffgload.c @@ -826,7 +826,7 @@ /* the seac operator must not be nested */ decoder->seac = TRUE; error = cff_decoder_parse_charstrings( decoder, charstring, - charstring_len ); + charstring_len, 0 ); decoder->seac = FALSE; cff_free_glyph_data( face, &charstring, charstring_len ); @@ -856,7 +856,7 @@ /* the seac operator must not be nested */ decoder->seac = TRUE; error = cff_decoder_parse_charstrings( decoder, charstring, - charstring_len ); + charstring_len, 0 ); decoder->seac = FALSE; cff_free_glyph_data( face, &charstring, charstring_len ); @@ -895,13 +895,17 @@ /* */ /* charstring_len :: The length in bytes of the charstring stream. */ /* */ + /* in_dict :: Set to 1 if function is called from top or */ + /* private DICT (needed for Multiple Master CFFs). */ + /* */ /* */ /* FreeType error code. 0 means success. */ /* */ FT_LOCAL_DEF( FT_Error ) cff_decoder_parse_charstrings( CFF_Decoder* decoder, FT_Byte* charstring_base, - FT_ULong charstring_len ) + FT_ULong charstring_len, + FT_Bool in_dict ) { FT_Error error; CFF_Decoder_Zone* zone; @@ -913,6 +917,8 @@ FT_Fixed* stack; FT_Int charstring_type = decoder->cff->top_font.font_dict.charstring_type; + FT_UShort num_designs = + decoder->cff->top_font.font_dict.num_designs; T2_Hints_Funcs hinter; @@ -1241,6 +1247,44 @@ if ( op == cff_op_unknown ) continue; + /* in Multiple Master CFFs, T2 charstrings can appear in */ + /* dictionaries, but some operators are prohibited */ + if ( in_dict ) + { + switch ( op ) + { + case cff_op_hstem: + case cff_op_vstem: + case cff_op_vmoveto: + case cff_op_rlineto: + case cff_op_hlineto: + case cff_op_vlineto: + case cff_op_rrcurveto: + case cff_op_hstemhm: + case cff_op_hintmask: + case cff_op_cntrmask: + case cff_op_rmoveto: + case cff_op_hmoveto: + case cff_op_vstemhm: + case cff_op_rcurveline: + case cff_op_rlinecurve: + case cff_op_vvcurveto: + case cff_op_hhcurveto: + case cff_op_vhcurveto: + case cff_op_hvcurveto: + case cff_op_hflex: + case cff_op_flex: + case cff_op_hflex1: + case cff_op_flex1: + case cff_op_callsubr: + case cff_op_callgsubr: + goto MM_Error; + + default: + break; + } + } + /* check arguments */ req_args = cff_argument_counts[op]; if ( req_args & CFF_COUNT_CHECK_WIDTH ) @@ -1278,7 +1322,9 @@ case cff_op_endchar: /* If there is a width specified for endchar, we either have */ /* 1 argument or 5 arguments. We like to argue. */ - set_width_ok = ( num_args == 5 ) || ( num_args == 1 ); + set_width_ok = in_dict + ? 0 + : ( ( num_args == 5 ) || ( num_args == 1 ) ); break; default: @@ -1971,6 +2017,10 @@ return error; case cff_op_endchar: + /* in dictionaries, `endchar' simply indicates end of data */ + if ( in_dict ) + return error; + FT_TRACE4(( " endchar\n" )); /* We are going to emulate the seac operator. */ @@ -2236,9 +2286,25 @@ case cff_op_blend: /* this operator was removed from the Type2 specification */ /* in version 16-March-2000 */ - FT_TRACE4(( " blend\n" )); + { + FT_Int num_results = (FT_Int)( args[0] >> 16 ); - goto Unimplemented; + + FT_TRACE4(( " blend\n" )); + + if ( num_results < 0 ) + goto Syntax_Error; + + if ( num_results * (FT_Int)num_designs > num_args ) + goto Stack_Underflow; + + /* since we currently don't handle interpolation of multiple */ + /* master fonts, return the `num_results' values of the */ + /* first master */ + args -= num_results * ( num_designs - 1 ); + num_args -= num_results * ( num_designs - 1 ); + } + break; case cff_op_dotsection: /* this operator is deprecated and ignored by the parser */ @@ -2535,6 +2601,11 @@ Fail: return error; + MM_Error: + FT_TRACE4(( "cff_decoder_parse_charstrings:" + " invalid opcode found in top DICT charstring\n")); + return FT_THROW( Invalid_File_Format ); + Syntax_Error: FT_TRACE4(( "cff_decoder_parse_charstrings: syntax error\n" )); return FT_THROW( Invalid_File_Format ); @@ -2608,7 +2679,8 @@ if ( !error ) error = cff_decoder_parse_charstrings( &decoder, charstring, - charstring_len ); + charstring_len, + 0 ); cff_free_glyph_data( face, &charstring, &charstring_len ); } @@ -2859,7 +2931,8 @@ if ( driver->hinting_engine == FT_CFF_HINTING_FREETYPE ) error = cff_decoder_parse_charstrings( &decoder, charstring, - charstring_len ); + charstring_len, + 0 ); else #endif { diff --git a/src/cff/cffgload.h b/src/cff/cffgload.h index 2257307ee..b875fbed9 100644 --- a/src/cff/cffgload.h +++ b/src/cff/cffgload.h @@ -227,7 +227,8 @@ FT_BEGIN_HEADER FT_LOCAL( FT_Error ) cff_decoder_parse_charstrings( CFF_Decoder* decoder, FT_Byte* charstring_base, - FT_ULong charstring_len ); + FT_ULong charstring_len, + FT_Bool in_dict ); #endif FT_LOCAL( FT_Error ) diff --git a/src/cff/cffload.c b/src/cff/cffload.c index 42cc37e1a..f25c3fba9 100644 --- a/src/cff/cffload.c +++ b/src/cff/cffload.c @@ -1316,7 +1316,11 @@ CFF_Private priv = &font->private_dict; - cff_parser_init( &parser, CFF_CODE_TOPDICT, &font->font_dict, library ); + cff_parser_init( &parser, + CFF_CODE_TOPDICT, + &font->font_dict, + library, + 0 ); /* set defaults */ FT_MEM_ZERO( top, sizeof ( *top ) ); @@ -1370,7 +1374,11 @@ priv->expansion_factor = (FT_Fixed)( 0.06 * 0x10000L ); priv->blue_scale = (FT_Fixed)( 0.039625 * 0x10000L * 1000 ); - cff_parser_init( &parser, CFF_CODE_PRIVATE, priv, library ); + cff_parser_init( &parser, + CFF_CODE_PRIVATE, + priv, + library, + top->num_designs ); if ( FT_STREAM_SEEK( base_offset + font->font_dict.private_offset ) || FT_FRAME_ENTER( font->font_dict.private_size ) ) diff --git a/src/cff/cffparse.c b/src/cff/cffparse.c index acbdc5a82..5b203b9a2 100644 --- a/src/cff/cffparse.c +++ b/src/cff/cffparse.c @@ -39,7 +39,8 @@ cff_parser_init( CFF_Parser parser, FT_UInt code, void* object, - FT_Library library) + FT_Library library, + FT_UShort num_designs ) { FT_MEM_ZERO( parser, sizeof ( *parser ) ); @@ -47,6 +48,7 @@ parser->object_code = code; parser->object = object; parser->library = library; + parser->num_designs = num_designs; } @@ -649,6 +651,47 @@ } + /* We assume that the `MultipleMaster' operator comes before any */ + /* top DICT operators that contain T2 charstrings. Otherwise, */ + /* `num_designs' is zero, leading to errors in handling the */ + /* `blend' operator later on. */ + + static FT_Error + cff_parse_multiple_master( CFF_Parser parser ) + { + CFF_FontRecDict dict = (CFF_FontRecDict)parser->object; + FT_Error error; + + + FT_TRACE1(( "Multiple Master CFFs not supported yet," + " handling first master design only\n" )); + + error = FT_ERR( Stack_Underflow ); + + /* currently, we handle only the first argument */ + if ( parser->top >= parser->stack + 5 ) + { + FT_Long num_designs = cff_parse_num( parser->stack ); + + + if ( num_designs > 16 || num_designs < 2 ) + { + FT_ERROR(( "cff_parse_multiple_master:" + " Invalid number of designs\n" )); + error = FT_THROW( Invalid_File_Format ); + } + else + { + dict->num_designs = (FT_UShort)num_designs; + parser->num_designs = dict->num_designs; + error = FT_Err_Ok; + } + } + + return error; + } + + static FT_Error cff_parse_cid_ros( CFF_Parser parser ) { @@ -972,7 +1015,7 @@ if ( parser->top - parser->stack >= CFF_MAX_STACK_DEPTH ) goto Stack_Overflow; - *parser->top ++ = p; + *parser->top++ = p; /* now, skip it */ if ( v == 30 ) @@ -1001,6 +1044,133 @@ else if ( v > 246 ) p += 1; } + else if ( v == 31 ) + { + /* a Type 2 charstring */ + + CFF_Decoder decoder; + CFF_FontRec cff_rec; + FT_Byte* charstring_base; + FT_ULong charstring_len; + + FT_Fixed* stack; + FT_Byte* q; + + + charstring_base = ++p; + + /* search `endchar' operator */ + for (;;) + { + if ( p >= limit ) + goto Exit; + if ( *p == 14 ) + break; + p++; + } + + charstring_len = (FT_ULong)( p - charstring_base ) + 1; + + /* construct CFF_Decoder object */ + FT_MEM_ZERO( &decoder, sizeof ( decoder ) ); + FT_MEM_ZERO( &cff_rec, sizeof ( cff_rec ) ); + + cff_rec.top_font.font_dict.num_designs = parser->num_designs; + decoder.cff = &cff_rec; + + error = cff_decoder_parse_charstrings( &decoder, + charstring_base, + charstring_len, + 1 ); + + /* Now copy the stack data in the temporary decoder object, */ + /* converting it back to charstring number representations */ + /* (this is ugly, I know). */ + /* */ + /* We overwrite the original top DICT charstring under the */ + /* assumption that the charstring representation of the result */ + /* of `cff_decoder_parse_charstrings' is shorter, which should */ + /* be always true. */ + + q = charstring_base - 1; + stack = decoder.stack; + + while ( stack < decoder.top ) + { + FT_ULong num; + FT_Bool neg; + + + if ( parser->top - parser->stack >= CFF_MAX_STACK_DEPTH ) + goto Stack_Overflow; + + *parser->top++ = q; + + if ( *stack < 0 ) + { + num = (FT_ULong)-*stack; + neg = 1; + } + else + { + num = (FT_ULong)*stack; + neg = 0; + } + + if ( num & 0xFFFFU ) + { + if ( neg ) + num = (FT_ULong)-num; + + *q++ = 255; + *q++ = ( num & 0xFF000000U ) >> 24; + *q++ = ( num & 0x00FF0000U ) >> 16; + *q++ = ( num & 0x0000FF00U ) >> 8; + *q++ = num & 0x000000FFU; + } + else + { + num >>= 16; + + if ( neg ) + { + if ( num <= 107 ) + *q++ = (FT_Byte)( 139 - num ); + else if ( num <= 1131 ) + { + *q++ = (FT_Byte)( ( ( num - 108 ) >> 8 ) + 251 ); + *q++ = (FT_Byte)( ( num - 108 ) & 0xFF ); + } + else + { + num = (FT_ULong)-num; + + *q++ = 28; + *q++ = (FT_Byte)( num >> 8 ); + *q++ = (FT_Byte)( num & 0xFF ); + } + } + else + { + if ( num <= 107 ) + *q++ = (FT_Byte)( num + 139 ); + else if ( num <= 1131 ) + { + *q++ = (FT_Byte)( ( ( num - 108 ) >> 8 ) + 247 ); + *q++ = (FT_Byte)( ( num - 108 ) & 0xFF ); + } + else + { + *q++ = 28; + *q++ = (FT_Byte)( num >> 8 ); + *q++ = (FT_Byte)( num & 0xFF ); + } + } + } + + stack++; + } + } else { /* This is not a number, hence it's an operator. Compute its code */ diff --git a/src/cff/cffparse.h b/src/cff/cffparse.h index bb9425ac7..682ada04a 100644 --- a/src/cff/cffparse.h +++ b/src/cff/cffparse.h @@ -47,6 +47,8 @@ FT_BEGIN_HEADER FT_UInt object_code; void* object; + FT_UShort num_designs; /* a copy of `CFF_FontRecDict->num_designs' */ + } CFF_ParserRec, *CFF_Parser; @@ -54,7 +56,8 @@ FT_BEGIN_HEADER cff_parser_init( CFF_Parser parser, FT_UInt code, void* object, - FT_Library library); + FT_Library library, + FT_UShort num_designs ); FT_LOCAL( FT_Error ) cff_parser_run( CFF_Parser parser, diff --git a/src/cff/cfftoken.h b/src/cff/cfftoken.h index 44b65805b..22637c780 100644 --- a/src/cff/cfftoken.h +++ b/src/cff/cfftoken.h @@ -38,6 +38,9 @@ CFF_FIELD_NUM ( 13, unique_id, "UniqueID" ) CFF_FIELD_CALLBACK( 5, font_bbox, "FontBBox" ) CFF_FIELD_NUM ( 0x108, stroke_width, "StrokeWidth" ) +#if 0 + CFF_FIELD_DELTA ( 14, xuid, 16, "XUID" ) +#endif CFF_FIELD_NUM ( 15, charset_offset, "charset" ) CFF_FIELD_NUM ( 16, encoding_offset, "Encoding" ) CFF_FIELD_NUM ( 17, charstrings_offset, "CharStrings" ) @@ -48,8 +51,13 @@ #if 0 CFF_FIELD_STRING ( 0x116, base_font_name, "BaseFontName" ) CFF_FIELD_DELTA ( 0x117, base_font_blend, 16, "BaseFontBlend" ) +#endif + + /* the next two operators were removed from the Type2 specification */ + /* in version 16-March-2000 */ CFF_FIELD_CALLBACK( 0x118, multiple_master, "MultipleMaster" ) - CFF_FIELD_CALLBACK( 0x119, blend_axis_types, "BlendAxisTypes" ) +#if 0 + CFF_FIELD_CALLBACK( 0x11A, blend_axis_types, "BlendAxisTypes" ) #endif CFF_FIELD_CALLBACK( 0x11E, cid_ros, "ROS" ) diff --git a/src/cff/cfftypes.h b/src/cff/cfftypes.h index 80f13fe2c..10fe9372f 100644 --- a/src/cff/cfftypes.h +++ b/src/cff/cfftypes.h @@ -145,6 +145,11 @@ FT_BEGIN_HEADER FT_ULong cid_fd_select_offset; FT_UInt cid_font_name; + /* the next field comes from the data of the deprecated */ + /* `MultipleMaster' operator; it is needed to parse the (also */ + /* deprecated) `blend' operator in Type 2 charstrings */ + FT_UShort num_designs; + } CFF_FontRecDictRec, *CFF_FontRecDict; diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c index 2cf2ee2a6..2e8c1ecde 100644 --- a/src/sfnt/sfobjs.c +++ b/src/sfnt/sfobjs.c @@ -949,6 +949,10 @@ instance_size * num_instances > fvar_len ) num_instances = 0; + /* we don't support Multiple Master CFFs yet */ + if ( !face->goto_table( face, TTAG_CFF, stream, 0 ) ) + num_instances = 0; + /* we support at most 2^15 - 1 instances */ if ( num_instances >= ( 1U << 15 ) - 1 ) {