diff --git a/ChangeLog b/ChangeLog index 6a8eca199..bb9e84645 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,47 @@ -2003-10-15: Graham Asher +2003-10-16 Werner Lemberg + + Completely revised Type 42 parser. It now handles both fonts + produced with ttftot42 (tested version 0.3.1) and + TrueTypeToType42.ps (tested version May 2001; it is necessary to + fix the broken header comment to be `%!PS-TrueTypeFont...'). + + * src/type42/t42objs.c (T42_GlyphSlot_Load): Change fourth + parameter to `FT_UInt'. + * src/type42/t42objs.h: Updated. + + * src/type42/t42parse.h (T42_ParserRec): Change type of `in_memory' + to FT_Bool. + (T42_Loader): Change type of `num_chars' and `num_glyphs' to + FT_UInt. + Add `swap_table' element. + * src/type42/t42parse.c (T42_KEYWORD_COUNT, T1_ToFixed, + T1_ToCoordArray, T1_ToTokenArray): Removed. + (T1_ToBytes): New macro. + (t42_is_alpha, t42_hexval): Removed. + (t42_is_space): Handle `\0'. + (t42_parse_encoding): Updated to use new PostScript parser routines + from psaux. + Handle `/Encoding [ ... ]' also. + (T42_Load_Status): New enumeration. + (t42_parse_sfnts): Updated to use new PostScript parser routines + from psaux. + (t42_parse_charstrings): Updated to use new PostScript parser + routines from psaux. + Handle `/CharStrings << ... >>' also. + Don't expect that /.notdef is the first element in dictionary. Copy + code from type1 module to handle this. + (t42_parse_dict): Updated to use new PostScript parser routines + from psaux. + Remove code for synthetic fonts (which can't occur in Type 42 + fonts). + (t42_loader_done): Release `swap_table'. + + * src/psaux/psobjs.c (skip_string): Increase `cur' properly. + + * src/type1/t1load.c (parse_charstrings): Make test for `.notdef' + faster. + +2003-10-15 Graham Asher * src/autohint/ahglobal.c (blue_chars), src/winfonts/winfnt.c (fnt_cmap_class_rec, fnt_cmap_class), src/bdf/bdflib.c (empty, diff --git a/src/psaux/psobjs.c b/src/psaux/psobjs.c index adc9772c6..c7f26015f 100644 --- a/src/psaux/psobjs.c +++ b/src/psaux/psobjs.c @@ -410,9 +410,7 @@ FT_Byte* limit = parser->limit; - cur++; - - while ( cur < limit ) + while ( ++cur < limit ) { int d; diff --git a/src/type1/t1load.c b/src/type1/t1load.c index e563d634f..0aee92efe 100644 --- a/src/type1/t1load.c +++ b/src/type1/t1load.c @@ -1190,9 +1190,7 @@ /* the format is simple: */ /* `/glyphname' + binary data */ - /* */ - /* note that we stop when we find a `def' */ - /* */ + T1_Skip_Spaces( parser ); cur = parser->root.cursor; @@ -1236,8 +1234,9 @@ /* add a trailing zero to the name table */ name_table->elements[n][len] = '\0'; - /* record index of /.notdef */ - if ( ft_strcmp( (const char*)".notdef", + /* record index of /.notdef */ + if ( *cur == '.' && + ft_strcmp( ".notdef", (const char*)(name_table->elements[n]) ) == 0 ) { notdef_index = n; @@ -1274,7 +1273,7 @@ loader->num_glyphs = n; - /* if /.notdef is found but does not occupy index 0, do our magic. */ + /* if /.notdef is found but does not occupy index 0, do our magic. */ if ( ft_strcmp( (const char*)".notdef", (const char*)name_table->elements[0] ) && notdef_found ) diff --git a/src/type42/t42drivr.c b/src/type42/t42drivr.c index faa891e30..b33c92c1c 100644 --- a/src/type42/t42drivr.c +++ b/src/type42/t42drivr.c @@ -24,10 +24,9 @@ /* 2) Incremental fonts making use of the GlyphDirectory keyword */ /* will be loaded, but the rendering will be using the TrueType */ /* tables. */ - /* 3) The sfnts array is expected to be ASCII, not binary. */ - /* 4) As for Type1 fonts, CDevProc is not supported. */ - /* 5) The Metrics dictionary is not supported. */ - /* 6) AFM metrics are not supported. */ + /* 3) As for Type1 fonts, CDevProc is not supported. */ + /* 4) The Metrics dictionary is not supported. */ + /* 5) AFM metrics are not supported. */ /* */ /* In other words, this driver supports Type42 fonts derived from */ /* TrueType fonts in a non-CID manner, as done by usual conversion */ diff --git a/src/type42/t42objs.c b/src/type42/t42objs.c index e5a574817..c4ce3fefb 100644 --- a/src/type42/t42objs.c +++ b/src/type42/t42objs.c @@ -53,7 +53,8 @@ if ( error ) goto Exit; - error = t42_parse_dict( face, &loader, parser->base_dict, parser->base_len ); + error = t42_parse_dict( face, &loader, + parser->base_dict, parser->base_len ); if ( type1->font_type != 42 ) { @@ -65,7 +66,8 @@ /* to the Type1 data */ type1->num_glyphs = loader.num_glyphs; - if ( !loader.charstrings.init ) { + if ( !loader.charstrings.init ) + { FT_ERROR(( "T42_Open_Face: no charstrings array in face!\n" )); error = T42_Err_Invalid_File_Format; } @@ -121,8 +123,10 @@ if ( ft_strcmp( (const char*)".notdef", (const char*)glyph_name ) != 0 ) { - if ( charcode < min_char ) min_char = charcode; - if ( charcode > max_char ) max_char = charcode; + if ( charcode < min_char ) + min_char = charcode; + if ( charcode > max_char ) + max_char = charcode; } break; } @@ -149,12 +153,12 @@ FT_Int num_params, FT_Parameter* params ) { - FT_Error error; + FT_Error error; FT_Service_PsNames psnames; - PSAux_Service psaux; - FT_Face root = (FT_Face)&face->root; - T1_Font type1 = &face->type1; - PS_FontInfo info = &type1->font_info; + PSAux_Service psaux; + FT_Face root = (FT_Face)&face->root; + T1_Font type1 = &face->type1; + PS_FontInfo info = &type1->font_info; FT_UNUSED( num_params ); FT_UNUSED( params ); @@ -189,7 +193,7 @@ goto Exit; } - /* Now, load the font program into the face object */ + /* Now load the font program into the face object */ /* Init the face object fields */ /* Now set up root face fields */ @@ -350,7 +354,7 @@ #if 0 /* Select default charmap */ - if (root->num_charmaps) + if ( root->num_charmaps ) root->charmap = root->charmaps[0]; #endif } @@ -452,8 +456,6 @@ } - - FT_LOCAL_DEF( FT_Error ) T42_Size_Init( T42_Size size ) { @@ -601,7 +603,7 @@ FT_LOCAL_DEF( FT_Error ) T42_GlyphSlot_Load( FT_GlyphSlot glyph, FT_Size size, - FT_Int glyph_index, + FT_UInt glyph_index, FT_Int32 load_flags ) { FT_Error error; diff --git a/src/type42/t42objs.h b/src/type42/t42objs.h index 17f58948b..c15595103 100644 --- a/src/type42/t42objs.h +++ b/src/type42/t42objs.h @@ -102,7 +102,7 @@ FT_BEGIN_HEADER FT_LOCAL( FT_Error ) T42_GlyphSlot_Load( FT_GlyphSlot glyph, FT_Size size, - FT_Int glyph_index, + FT_UInt glyph_index, FT_Int32 load_flags ); FT_LOCAL( void ) diff --git a/src/type42/t42parse.c b/src/type42/t42parse.c index 7ce4707a4..f3b408869 100644 --- a/src/type42/t42parse.c +++ b/src/type42/t42parse.c @@ -93,10 +93,6 @@ }; -#define T42_KEYWORD_COUNT \ - ( sizeof ( t42_keywords ) / sizeof ( t42_keywords[0] ) ) - - #define T1_Add_Table( p, i, o, l ) (p)->funcs.add( (p), i, o, l ) #define T1_Done_Table( p ) \ do \ @@ -114,17 +110,15 @@ #define T1_Skip_Spaces( p ) (p)->root.funcs.skip_spaces( &(p)->root ) #define T1_Skip_PS_Token( p ) (p)->root.funcs.skip_PS_token( &(p)->root ) -#define T1_ToInt( p ) (p)->root.funcs.to_int( &(p)->root ) -#define T1_ToFixed( p, t ) (p)->root.funcs.to_fixed( &(p)->root, t ) +#define T1_ToInt( p ) \ + (p)->root.funcs.to_int( &(p)->root ) +#define T1_ToBytes( p, b, m, n, d ) \ + (p)->root.funcs.to_bytes( &(p)->root, b, m, n, d ) -#define T1_ToCoordArray( p, m, c ) \ - (p)->root.funcs.to_coord_array( &(p)->root, m, c ) #define T1_ToFixedArray( p, m, f, t ) \ (p)->root.funcs.to_fixed_array( &(p)->root, m, f, t ) #define T1_ToToken( p, t ) \ (p)->root.funcs.to_token( &(p)->root, t ) -#define T1_ToTokenArray( p, t, m, c ) \ - (p)->root.funcs.to_token_array( &(p)->root, t, m, c ) #define T1_Load_Field( p, f, o, m, pf ) \ (p)->root.funcs.load_field( &(p)->root, f, o, m, pf ) @@ -195,9 +189,9 @@ } /* Now check font format; we must see `%!PS-TrueTypeFont' */ - if (size <= 17 || - ( ft_strncmp( (const char*)parser->base_dict, - "%!PS-TrueTypeFont", 17) ) ) + if ( size <= 17 || + ( ft_strncmp( (const char*)parser->base_dict, + "%!PS-TrueTypeFont", 17 ) ) ) error = T42_Err_Unknown_File_Format; else { @@ -228,24 +222,12 @@ } - static int - t42_is_alpha( FT_Byte c ) - { - /* Note: we must accept "+" as a valid character, as it is used in */ - /* embedded type1 fonts in PDF documents. */ - /* */ - return ( ft_isalnum( c ) || - c == '.' || - c == '_' || - c == '-' || - c == '+' ); - } - - static int t42_is_space( FT_Byte c ) { - return ( c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f' ); + return ( c == ' ' || c == '\t' || + c == '\r' || c == '\n' || c == '\f' || + c == '\0' ); } @@ -288,8 +270,8 @@ matrix->yy = temp[3]; /* note that the offsets must be expressed in integer font units */ - offset->x = temp[4] >> 16; - offset->y = temp[5] >> 16; + offset->x = temp[4] >> 16; + offset->y = temp[5] >> 16; } @@ -297,49 +279,53 @@ t42_parse_encoding( T42_Face face, T42_Loader loader ) { - T42_Parser parser = &loader->parser; - FT_Byte* cur = parser->root.cursor; - FT_Byte* limit = parser->root.limit; + T42_Parser parser = &loader->parser; + FT_Byte* cur; + FT_Byte* limit = parser->root.limit; PSAux_Service psaux = (PSAux_Service)face->psaux; - /* skip whitespace */ - while ( t42_is_space( *cur ) ) + T1_Skip_Spaces( parser ); + cur = parser->root.cursor; + + if ( cur >= limit ) { - cur++; - if ( cur >= limit ) - { - FT_ERROR(( "t42_parse_encoding: out of bounds!\n" )); - parser->root.error = T42_Err_Invalid_File_Format; - return; - } + FT_ERROR(( "t42_parse_encoding: out of bounds!\n" )); + parser->root.error = T42_Err_Invalid_File_Format; + return; } - /* if we have a number, then the encoding is an array, */ - /* and we must load it now */ - if ( ft_isdigit( *cur ) ) + /* if we have a number or `[', the encoding is an array, */ + /* and we must load it now */ + if ( ft_isdigit( *cur ) || *cur == '[' ) { - T1_Encoding encode = &face->type1.encoding; - FT_Int count, n; - PS_Table char_table = &loader->encoding_table; - FT_Memory memory = parser->root.memory; + T1_Encoding encode = &face->type1.encoding; + FT_UInt count, n; + PS_Table char_table = &loader->encoding_table; + FT_Memory memory = parser->root.memory; FT_Error error; + FT_Bool only_immediates = 0; - if ( encode->char_index ) - /* with synthetic fonts, it's possible we get here twice */ - return; + /* read the number of entries in the encoding; should be 256 */ + if ( *cur == '[' ) + { + count = 256; + only_immediates = 1; + parser->root.cursor++; + } + else + count = (FT_UInt)T1_ToInt( parser ); - /* read the number of entries in the encoding, should be 256 */ - count = (FT_Int)T1_ToInt( parser ); - if ( parser->root.error ) + T1_Skip_Spaces( parser ); + if ( parser->root.cursor >= limit ) return; /* we use a T1_Table to store our charnames */ loader->num_chars = encode->num_chars = count; - if ( FT_NEW_ARRAY( encode->char_index, count ) || - FT_NEW_ARRAY( encode->char_name, count ) || + if ( FT_NEW_ARRAY( encode->char_index, count ) || + FT_NEW_ARRAY( encode->char_name, count ) || FT_SET_ERROR( psaux->ps_table_funcs->init( char_table, count, memory ) ) ) { @@ -356,86 +342,97 @@ T1_Add_Table( char_table, n, notdef, 8 ); } - /* Now, we will need to read a record of the form */ - /* ... charcode /charname ... for each entry in our table */ + /* Now we need to read records of the form */ + /* */ + /* ... charcode /charname ... */ + /* */ + /* for each entry in our table. */ /* */ /* We simply look for a number followed by an immediate */ /* name. Note that this ignores correctly the sequence */ - /* that is often seen in type1 fonts: */ + /* that is often seen in type42 fonts: */ /* */ /* 0 1 255 { 1 index exch /.notdef put } for dup */ /* */ /* used to clean the encoding array before anything else. */ /* */ - /* We stop when we encounter a `def'. */ + /* Alternatively, if the array is directly given as */ + /* */ + /* /Encoding [ ... ] */ + /* */ + /* we only read immediates. */ - cur = parser->root.cursor; - limit = parser->root.limit; - n = 0; + n = 0; - for ( ; cur < limit; ) + while ( parser->root.cursor < limit ) { - FT_Byte c; - - - c = *cur; + T1_Skip_Spaces( parser ); + cur = parser->root.cursor; - /* we stop when we encounter a `def' */ - if ( c == 'd' && cur + 3 < limit ) + /* we stop when we encounter `def' or `]' */ + if ( *cur == 'd' && cur + 3 < limit ) { - if ( cur[1] == 'e' && - cur[2] == 'f' && - t42_is_space( cur[-1] ) && - t42_is_space( cur[3] ) ) + if ( cur[1] == 'e' && + cur[2] == 'f' && + t42_is_space( cur[3] ) ) { FT_TRACE6(( "encoding end\n" )); + cur += 3; break; } } + if ( *cur == ']' ) + { + FT_TRACE6(( "encoding end\n" )); + cur++; + break; + } - /* otherwise, we must find a number before anything else */ - if ( ft_isdigit( c ) ) + /* check whether we've found an entry */ + if ( ft_isdigit( *cur ) || only_immediates ) { FT_Int charcode; - parser->root.cursor = cur; - charcode = (FT_Int)T1_ToInt( parser ); - cur = parser->root.cursor; + if ( only_immediates ) + charcode = n; + else + { + charcode = (FT_Int)T1_ToInt( parser ); + T1_Skip_Spaces( parser ); + } - /* skip whitespace */ - while ( cur < limit && t42_is_space( *cur ) ) - cur++; + cur = parser->root.cursor; - if ( cur < limit && *cur == '/' ) + if ( *cur == '/' && cur + 2 < limit && n < count ) { - /* bingo, we have an immediate name -- it must be a */ - /* character name */ - FT_Byte* cur2 = cur + 1; FT_PtrDist len; - while ( cur2 < limit && t42_is_alpha( *cur2 ) ) - cur2++; + cur++; + + parser->root.cursor = cur; + T1_Skip_PS_Token( parser ); - len = cur2 - cur - 1; + len = parser->root.cursor - cur; parser->root.error = T1_Add_Table( char_table, charcode, - cur + 1, len + 1 ); + cur, len + 1 ); char_table->elements[charcode][len] = '\0'; if ( parser->root.error ) return; - cur = cur2; + n++; } } else - cur++; + T1_Skip_PS_Token( parser ); } face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY; parser->root.cursor = cur; } + /* Otherwise, we should have either `StandardEncoding', */ /* `ExpertEncoding', or `ISOLatin1Encoding' */ else @@ -452,7 +449,8 @@ ft_strncmp( (const char*)cur, "ISOLatin1Encoding", 17 ) == 0 ) face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1; - else { + else + { FT_ERROR(( "t42_parse_encoding: invalid token!\n" )); parser->root.error = T42_Err_Invalid_File_Format; } @@ -460,34 +458,13 @@ } - static FT_UInt - t42_hexval( FT_Byte v ) + typedef enum { - FT_UInt d; + BEFORE_START, + BEFORE_TABLE_DIR, + OTHER_TABLES - d = (FT_UInt)( v - 'A' ); - if ( d < 6 ) - { - d += 10; - goto Exit; - } - - d = (FT_UInt)( v - 'a' ); - if ( d < 6 ) - { - d += 10; - goto Exit; - } - - d = (FT_UInt)( v - '0' ); - if ( d < 10 ) - goto Exit; - - d = 0; - - Exit: - return d; - } + } T42_Load_Status; static void @@ -496,154 +473,176 @@ { T42_Parser parser = &loader->parser; FT_Memory memory = parser->root.memory; - FT_Byte* cur = parser->root.cursor; + FT_Byte* cur; FT_Byte* limit = parser->root.limit; FT_Error error; - FT_Int num_tables = 0, status; - FT_ULong count, ttf_size = 0, string_size = 0; - FT_Bool in_string = 0; - FT_Byte v = 0; + FT_Int num_tables = 0; + FT_ULong count, ttf_size = 0; + FT_Long n, string_size, old_string_size, real_size; + FT_Byte* string_buf = NULL; + FT_Bool alloc = 0; - /* The format is `/sfnts [ <...> <...> ... ] def' */ + T42_Load_Status status; - while ( t42_is_space( *cur ) ) - cur++; - if (*cur++ == '[') - { - status = 0; - count = 0; - } - else + /* The format is */ + /* */ + /* /sfnts [ ... ] def */ + /* */ + /* or */ + /* */ + /* /sfnts [ */ + /* RD */ + /* RD */ + /* ... */ + /* ] def */ + /* */ + /* with exactly one space after the `RD' token. */ + + T1_Skip_Spaces( parser ); + + if ( parser->root.cursor >= limit || *parser->root.cursor++ != '[' ) { FT_ERROR(( "t42_parse_sfnts: can't find begin of sfnts vector!\n" )); error = T42_Err_Invalid_File_Format; goto Fail; } - while ( cur < limit - 2 ) + T1_Skip_Spaces( parser ); + status = BEFORE_START; + old_string_size = 0; + count = 0; + + while ( parser->root.cursor < limit ) { - while ( t42_is_space( *cur ) ) - cur++; + cur = parser->root.cursor; - switch ( *cur ) + if ( *cur == ']' ) { - case ']': - parser->root.cursor = cur++; - return; + parser->root.cursor++; + goto Exit; + } - case '<': - in_string = 1; - string_size = 0; - cur++; - continue; + else if ( *cur == '<' ) + { + T1_Skip_PS_Token( parser ); + if ( parser->root.error ) + goto Exit; - case '>': - if ( !in_string ) - { - FT_ERROR(( "t42_parse_sfnts: found unpaired `>'!\n" )); - error = T42_Err_Invalid_File_Format; + /* don't include delimiters */ + string_size = (FT_Long)( ( parser->root.cursor - cur - 2 + 1 ) / 2 ); + if ( FT_REALLOC( string_buf, old_string_size, string_size ) ) goto Fail; - } - /* A string can have, as a last byte, */ - /* a zero byte for padding. If so, ignore it */ - if ( ( v == 0 ) && ( string_size % 2 == 1 ) ) - count--; - in_string = 0; - cur++; - continue; + alloc = 1; - case '%': - if ( !in_string ) - { - /* Comment found; skip till end of line */ - while ( *cur != '\n' ) - cur++; - continue; - } - else - { - FT_ERROR(( "t42_parse_sfnts: found `%' in string!\n" )); - error = T42_Err_Invalid_File_Format; - goto Fail; - } + parser->root.cursor = cur; + (void)T1_ToBytes( parser, string_buf, string_size, &real_size, 1 ); + old_string_size = string_size; + string_size = real_size; + } + + else if ( ft_isdigit( *cur ) ) + { + string_size = T1_ToInt( parser ); + + T1_Skip_PS_Token( parser ); /* `RD' */ - default: - if ( !ft_isxdigit( *cur ) || !ft_isxdigit( *(cur + 1) ) ) + string_buf = parser->root.cursor + 1; /* one space after `RD' */ + + parser->root.cursor += string_size + 1; + if ( parser->root.cursor >= limit ) { - FT_ERROR(( "t42_parse_sfnts: found non-hex characters in string" )); + FT_ERROR(( "t42_parse_sfnts: too many binary data!\n" )); error = T42_Err_Invalid_File_Format; goto Fail; } - - v = (FT_Byte)( 16 * t42_hexval( cur[0] ) + t42_hexval( cur[1] ) ); - cur += 2; - string_size++; } - switch ( status ) + /* A string can have a trailing zero byte for padding. Ignore it. */ + if ( string_buf[string_size - 1] == 0 && ( string_size % 2 == 1 ) ) + string_size--; + + for ( n = 0; n < string_size; n++ ) { - case 0: /* The '[' was read, so load offset table, 12 bytes */ - if ( count < 12 ) + switch ( status ) { - face->ttf_data[count++] = v; - continue; - } - else - { - num_tables = 16 * face->ttf_data[4] + face->ttf_data[5]; - status = 1; - ttf_size = 12 + 16 * num_tables; + case BEFORE_START: + /* load offset table, 12 bytes */ + if ( count < 12 ) + { + face->ttf_data[count++] = string_buf[n]; + continue; + } + else + { + num_tables = 16 * face->ttf_data[4] + face->ttf_data[5]; + status = BEFORE_TABLE_DIR; + ttf_size = 12 + 16 * num_tables; - if ( FT_REALLOC( face->ttf_data, 12, ttf_size ) ) - goto Fail; - } - /* No break, fall-through */ + if ( FT_REALLOC( face->ttf_data, 12, ttf_size ) ) + goto Fail; + } + /* fall through */ - case 1: /* The offset table is read; read now the table directory */ - if ( count < ttf_size ) - { - face->ttf_data[count++] = v; - continue; - } - else - { - int i; - FT_ULong len; + case BEFORE_TABLE_DIR: + /* the offset table is read; read the table directory */ + if ( count < ttf_size ) + { + face->ttf_data[count++] = string_buf[n]; + continue; + } + else + { + int i; + FT_ULong len; - for ( i = 0; i < num_tables; i++ ) - { - FT_Byte* p = face->ttf_data + 12 + 16*i + 12; + for ( i = 0; i < num_tables; i++ ) + { + FT_Byte* p = face->ttf_data + 12 + 16 * i + 12; - len = FT_PEEK_ULONG( p ); - /* Pad to a 4-byte boundary length */ - ttf_size += ( len + 3 ) & ~3; - } + len = FT_PEEK_ULONG( p ); + + /* Pad to a 4-byte boundary length */ + ttf_size += ( len + 3 ) & ~3; + } + + status = OTHER_TABLES; + face->ttf_size = ttf_size; - status = 2; - face->ttf_size = ttf_size; + if ( FT_REALLOC( face->ttf_data, 12 + 16 * num_tables, + ttf_size + 1 ) ) + goto Fail; + } + /* fall through */ - if ( FT_REALLOC( face->ttf_data, 12 + 16 * num_tables, - ttf_size + 1 ) ) + case OTHER_TABLES: + /* all other tables are just copied */ + if ( count >= ttf_size ) + { + FT_ERROR(( "t42_parse_sfnts: too many binary data!\n" )); + error = T42_Err_Invalid_File_Format; goto Fail; + } + face->ttf_data[count++] = string_buf[n]; } - /* No break, fall-through */ - - case 2: /* We are reading normal tables; just swallow them */ - face->ttf_data[count++] = v; - } + + T1_Skip_Spaces( parser ); } - /* If control reaches this point, the format was not valid */ + /* if control reaches this point, the format was not valid */ error = T42_Err_Invalid_File_Format; Fail: parser->root.error = error; + + Exit: + if ( alloc ) + FT_FREE( string_buf ); } @@ -651,22 +650,75 @@ t42_parse_charstrings( T42_Face face, T42_Loader loader ) { - T42_Parser parser = &loader->parser; - PS_Table code_table = &loader->charstrings; - PS_Table name_table = &loader->glyph_names; - FT_Memory memory = parser->root.memory; + T42_Parser parser = &loader->parser; + PS_Table code_table = &loader->charstrings; + PS_Table name_table = &loader->glyph_names; + PS_Table swap_table = &loader->swap_table; + FT_Memory memory = parser->root.memory; FT_Error error; - PSAux_Service psaux = (PSAux_Service)face->psaux; + PSAux_Service psaux = (PSAux_Service)face->psaux; FT_Byte* cur; - FT_Byte* limit = parser->root.limit; - FT_Int n; + FT_Byte* limit = parser->root.limit; + FT_UInt n; + FT_UInt notdef_index = 0; + FT_Byte notdef_found = 0; - loader->num_glyphs = (FT_Int)T1_ToInt( parser ); - if ( parser->root.error ) - return; + T1_Skip_Spaces( parser ); + + if ( parser->root.cursor >= limit ) + { + FT_ERROR(( "t42_parse_charstrings: out of bounds!\n" )); + error = T42_Err_Invalid_File_Format; + goto Fail; + } + + if ( ft_isdigit( *parser->root.cursor ) ) + { + loader->num_glyphs = (FT_UInt)T1_ToInt( parser ); + if ( parser->root.error ) + return; + } + else if ( *parser->root.cursor == '<' ) + { + /* We have `<< ... >>'. Count the number of `/' in the dictionary */ + /* to get its size. */ + FT_UInt count = 0; + + + T1_Skip_PS_Token( parser ); + T1_Skip_Spaces( parser ); + cur = parser->root.cursor; + + while ( parser->root.cursor < limit ) + { + if ( *parser->root.cursor == '/' ) + count++; + else if ( *parser->root.cursor == '>' ) + { + loader->num_glyphs = count; + parser->root.cursor = cur; /* rewind */ + break; + } + T1_Skip_PS_Token( parser ); + T1_Skip_Spaces( parser ); + } + } + else + { + FT_ERROR(( "t42_parse_charstrings: invalid token!\n" )); + error = T42_Err_Invalid_File_Format; + goto Fail; + } + + if ( parser->root.cursor >= limit ) + { + FT_ERROR(( "t42_parse_charstrings: out of bounds!\n" )); + error = T42_Err_Invalid_File_Format; + goto Fail; + } /* initialize tables */ @@ -682,57 +734,82 @@ if ( error ) goto Fail; + /* Initialize table for swapping index notdef_index and */ + /* index 0 names and codes (if necessary). */ + + error = psaux->ps_table_funcs->init( swap_table, 4, memory ); + if ( error ) + goto Fail; + n = 0; for (;;) { - /* the format is simple: */ - /* `/glyphname' + index + def */ - /* */ - /* note that we stop when we find an `end' */ - /* */ + /* The format is simple: */ + /* `/glyphname' + index [+ def] */ + T1_Skip_Spaces( parser ); cur = parser->root.cursor; if ( cur >= limit ) break; - /* we stop when we find an `end' keyword */ - if ( *cur == 'e' && - cur + 3 < limit && - cur[1] == 'n' && - cur[2] == 'd' ) + /* We stop when we find an `end' keyword or '>' */ + if ( *cur == 'e' && + cur + 3 < limit && + cur[1] == 'n' && + cur[2] == 'd' && + t42_is_space( cur[3] ) ) + break; + if ( *cur == '>' ) break; - if ( *cur != '/' ) - T1_Skip_PS_Token( parser ); - else + T1_Skip_PS_Token( parser ); + + if ( *cur == '/' ) { - FT_Byte* cur2 = cur + 1; - FT_Int len; + FT_PtrDist len; - while ( cur2 < limit && t42_is_alpha( *cur2 ) ) - cur2++; - len = (FT_Int)( cur2 - cur - 1 ); + if ( cur + 1 >= limit ) + { + FT_ERROR(( "t42_parse_charstrings: out of bounds!\n" )); + error = T42_Err_Invalid_File_Format; + goto Fail; + } - error = T1_Add_Table( name_table, n, cur + 1, len + 1 ); + cur++; /* skip `/' */ + len = parser->root.cursor - cur; + + error = T1_Add_Table( name_table, n, cur, len + 1 ); if ( error ) goto Fail; /* add a trailing zero to the name table */ name_table->elements[n][len] = '\0'; - parser->root.cursor = cur2; + /* record index of /.notdef */ + if ( *cur == '.' && + ft_strcmp( ".notdef", + (const char*)(name_table->elements[n]) ) == 0 ) + { + notdef_index = n; + notdef_found = 1; + } + T1_Skip_Spaces( parser ); - cur2 = cur = parser->root.cursor; - if ( cur >= limit ) - break; + cur = parser->root.cursor; - while ( cur2 < limit && t42_is_alpha( *cur2 ) ) - cur2++; - len = (FT_Int)( cur2 - cur ); + (void)T1_ToInt( parser ); + if ( parser->root.cursor >= limit ) + { + FT_ERROR(( "t42_parse_charstrings: out of bounds!\n" )); + error = T42_Err_Invalid_File_Format; + goto Fail; + } + + len = parser->root.cursor - cur; error = T1_Add_Table( code_table, n, cur, len + 1 ); if ( error ) @@ -746,15 +823,79 @@ } } - /* Index 0 must be a .notdef element */ - if ( ft_strcmp( (char *)name_table->elements[0], ".notdef" ) ) + loader->num_glyphs = n; + + if ( !notdef_found ) { - FT_ERROR(( "t42_parse_charstrings: Index 0 is not `.notdef'!\n" )); + FT_ERROR(( "t42_parse_charstrings: no /.notdef glyph!\n" )); error = T42_Err_Invalid_File_Format; goto Fail; } - loader->num_glyphs = n; + /* if /.notdef does not occupy index 0, do our magic. */ + if ( ft_strcmp( (const char*)".notdef", + (const char*)name_table->elements[0] ) ) + { + /* Swap glyph in index 0 with /.notdef glyph. First, add index 0 */ + /* name and code entries to swap_table. Then place notdef_index */ + /* name and code entries into swap_table. Then swap name and code */ + /* entries at indices notdef_index and 0 using values stored in */ + /* swap_table. */ + + /* Index 0 name */ + error = T1_Add_Table( swap_table, 0, + name_table->elements[0], + name_table->lengths [0] ); + if ( error ) + goto Fail; + + /* Index 0 code */ + error = T1_Add_Table( swap_table, 1, + code_table->elements[0], + code_table->lengths [0] ); + if ( error ) + goto Fail; + + /* Index notdef_index name */ + error = T1_Add_Table( swap_table, 2, + name_table->elements[notdef_index], + name_table->lengths [notdef_index] ); + if ( error ) + goto Fail; + + /* Index notdef_index code */ + error = T1_Add_Table( swap_table, 3, + code_table->elements[notdef_index], + code_table->lengths [notdef_index] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( name_table, notdef_index, + swap_table->elements[0], + swap_table->lengths [0] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( code_table, notdef_index, + swap_table->elements[1], + swap_table->lengths [1] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( name_table, 0, + swap_table->elements[2], + swap_table->lengths [2] ); + if ( error ) + goto Fail; + + error = T1_Add_Table( code_table, 0, + swap_table->elements[3], + swap_table->lengths [3] ); + if ( error ) + goto Fail; + + } + return; Fail: @@ -774,14 +915,16 @@ /* if the keyword has a dedicated callback, call it */ - if ( field->type == T1_FIELD_TYPE_CALLBACK ) { + if ( field->type == T1_FIELD_TYPE_CALLBACK ) + { field->reader( (FT_Face)face, loader ); error = loader->parser.root.error; goto Exit; } - /* now, the keyword is either a simple field, or a table of fields; */ - /* we are now going to take care of it */ + /* now the keyword is either a simple field or a table of fields; */ + /* we are now going to take care of it */ + switch ( field->location ) { case T1_FIELD_LOCATION_FONT_INFO: @@ -818,28 +961,27 @@ FT_Long size ) { T42_Parser parser = &loader->parser; - FT_Byte* cur = base; - FT_Byte* limit = cur + size; - FT_UInt n_keywords = (FT_UInt)( sizeof ( t42_keywords ) / - sizeof ( t42_keywords[0] ) ); - - FT_Byte keyword_flags[T42_KEYWORD_COUNT]; - - { - FT_UInt n; + FT_Byte* limit; + int n_keywords = sizeof ( t42_keywords ) / + sizeof ( t42_keywords[0] ); - for ( n = 0; n < T42_KEYWORD_COUNT; n++ ) - keyword_flags[n] = 0; - } - parser->root.cursor = base; parser->root.limit = base + size; - parser->root.error = 0; + parser->root.error = T42_Err_Ok; + + limit = parser->root.limit; + + T1_Skip_Spaces( parser ); - for ( ; cur < limit; cur++ ) + while ( parser->root.cursor < limit ) { - /* look for `FontDirectory', which causes problems on some fonts */ + FT_Byte* cur; + + + cur = parser->root.cursor; + + /* look for `FontDirectory' which causes problems for some fonts */ if ( *cur == 'F' && cur + 25 < limit && ft_strncmp( (char*)cur, "FontDirectory", 13 ) == 0 ) { @@ -847,13 +989,21 @@ /* skip the `FontDirectory' keyword */ - cur += 13; - cur2 = cur; + T1_Skip_PS_Token( parser ); + T1_Skip_Spaces ( parser ); + cur = cur2 = parser->root.cursor; - /* lookup the `known' keyword */ - while ( cur < limit && *cur != 'k' && - ft_strncmp( (char*)cur, "known", 5 ) ) - cur++; + /* look up the `known' keyword */ + while ( cur < limit ) + { + if ( *cur == 'k' && cur + 5 < limit && + ft_strncmp( (char*)cur, "known", 5 ) == 0 ) + break; + + T1_Skip_PS_Token( parser ); + T1_Skip_Spaces ( parser ); + cur = parser->root.cursor; + } if ( cur < limit ) { @@ -861,34 +1011,37 @@ /* skip the `known' keyword and the token following it */ - cur += 5; - loader->parser.root.cursor = cur; - T1_ToToken( &loader->parser, &token ); + T1_Skip_PS_Token( parser ); + T1_ToToken( parser, &token ); /* if the last token was an array, skip it! */ if ( token.type == T1_TOKEN_TYPE_ARRAY ) cur2 = parser->root.cursor; } - cur = cur2; + parser->root.cursor = cur2; } + /* look for immediates */ else if ( *cur == '/' && cur + 2 < limit ) { - FT_Byte* cur2; - FT_UInt i, len; + FT_PtrDist len; cur++; - cur2 = cur; - while ( cur2 < limit && t42_is_alpha( *cur2 ) ) - cur2++; - len = (FT_UInt)( cur2 - cur ); - if ( len > 0 && len < 22 ) /* XXX What shall it this 22? */ + parser->root.cursor = cur; + T1_Skip_PS_Token( parser ); + + len = parser->root.cursor - cur; + + if ( len > 0 && len < 22 && parser->root.cursor < limit ) { - /* now, compare the immediate name to the keyword table */ + int i; + - /* Loop through all known keywords */ + /* now compare the immediate name to the keyword table */ + + /* loop through all known keywords */ for ( i = 0; i < n_keywords; i++ ) { T1_Field keyword = (T1_Field)&t42_keywords[i]; @@ -898,32 +1051,27 @@ if ( !name ) continue; - if ( ( len == ft_strlen( (const char *)name ) ) && - ( ft_memcmp( cur, name, len ) == 0 ) ) + if ( cur[0] == name[0] && + len == ft_strlen( (const char *)name ) && + ft_memcmp( cur, name, len ) == 0 ) { /* we found it -- run the parsing callback! */ - parser->root.cursor = cur2; - T1_Skip_Spaces( parser ); - - /* only record the first instance of each field/keyword */ - /* to deal with synthetic fonts correctly */ - if ( keyword_flags[i] == 0 ) - { - parser->root.error = t42_load_keyword(face, - loader, - keyword ); - if ( parser->root.error ) - return parser->root.error; - } - keyword_flags[i] = 1; - - cur = parser->root.cursor; + parser->root.error = t42_load_keyword( face, + loader, + keyword ); + if ( parser->root.error ) + return parser->root.error; break; } } } } + else + T1_Skip_PS_Token( parser ); + + T1_Skip_Spaces( parser ); } + return parser->root.error; } @@ -955,6 +1103,7 @@ T1_Release_Table( &loader->encoding_table ); T1_Release_Table( &loader->charstrings ); T1_Release_Table( &loader->glyph_names ); + T1_Release_Table( &loader->swap_table ); /* finalize parser */ t42_parser_done( parser ); diff --git a/src/type42/t42parse.h b/src/type42/t42parse.h index 7339aa63b..f77ec4af4 100644 --- a/src/type42/t42parse.h +++ b/src/type42/t42parse.h @@ -33,7 +33,7 @@ FT_BEGIN_HEADER FT_Byte* base_dict; FT_Long base_len; - FT_Byte in_memory; + FT_Bool in_memory; } T42_ParserRec, *T42_Parser; @@ -42,13 +42,14 @@ FT_BEGIN_HEADER { T42_ParserRec parser; /* parser used to read the stream */ - FT_Int num_chars; /* number of characters in encoding */ + FT_UInt num_chars; /* number of characters in encoding */ PS_TableRec encoding_table; /* PS_Table used to store the */ /* encoding character names */ - FT_Int num_glyphs; + FT_UInt num_glyphs; PS_TableRec glyph_names; PS_TableRec charstrings; + PS_TableRec swap_table; /* For moving .notdef glyph to index 0. */ } T42_LoaderRec, *T42_Loader;