diff --git a/ChangeLog b/ChangeLog index e503f5841..069676594 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2002-05-28 David Turner + + * include/freetype/internal/tttypes.h, src/sfnt/ttload.c, + src/sfnt/sfobjs.c, src/sfnt/sfdriver.c, src/base/ftnames.c: + fixing the SFNT name table loader to support various buggy fonts. + it now ignores empty name entries, entries with invalid pointer + offsets and certain fonts containing tables with broken "storageOffset" + fields. + + name strings are now loaded on demand, which reduces the memory + requirements for a given FT_Face tremendously (for example, the + name table of Arial.ttf is about 10Kb and contains 70 names !!) + + finally, this is a _quick_ fix. The whole name table loader and + interface will be rewritten in a much more cleanly way shortly, + once CSEH have been introduced in the sources. + 2002-05-24 Tim Mooney * builds/unix/ft-munmap.m4: New file, extracted FT_MUNMAP_DECL and diff --git a/include/freetype/internal/tttypes.h b/include/freetype/internal/tttypes.h index 7021786c3..b440aa58a 100644 --- a/include/freetype/internal/tttypes.h +++ b/include/freetype/internal/tttypes.h @@ -289,7 +289,7 @@ FT_BEGIN_HEADER FT_UShort languageID; FT_UShort nameID; FT_UShort stringLength; - FT_UShort stringOffset; + FT_ULong stringOffset; /* this last field is not defined in the spec */ /* but used by the FreeType engine */ @@ -317,15 +317,15 @@ FT_BEGIN_HEADER /* */ /* names :: An array of name records. */ /* */ - /* storage :: The names storage area. */ + /* stream :: the file's input stream. */ /* */ typedef struct TT_NameTableRec_ { FT_UShort format; - FT_UShort numNameRecords; - FT_UShort storageOffset; + FT_UInt numNameRecords; + FT_UInt storageOffset; TT_NameEntryRec* names; - FT_Byte* storage; + FT_Stream stream; } TT_NameTableRec, *TT_NameTable; @@ -1076,7 +1076,7 @@ FT_BEGIN_HEADER TT_CharMap_Func get_index; TT_CharNext_Func get_next_char; - + } TT_CMapTableRec; @@ -1664,7 +1664,7 @@ FT_BEGIN_HEADER /* for possible extensibility in other formats */ void* other; - + } TT_LoaderRec; diff --git a/src/base/ftnames.c b/src/base/ftnames.c index 7e9f1ee04..95275836d 100644 --- a/src/base/ftnames.c +++ b/src/base/ftnames.c @@ -53,15 +53,30 @@ if ( idx < (FT_UInt)ttface->num_names ) { - TT_NameEntryRec* name = ttface->name_table.names + idx; - - - aname->platform_id = name->platformID; - aname->encoding_id = name->encodingID; - aname->language_id = name->languageID; - aname->name_id = name->nameID; - aname->string = (FT_Byte*)name->string; - aname->string_len = name->stringLength; + TT_NameEntryRec* entry = ttface->name_table.names + idx; + + + /* load name on demand */ + if ( entry->stringLength > 0 && entry->string == NULL ) + { + FT_Memory memory = face->memory; + + + if ( FT_NEW_ARRAY ( entry->string, entry->stringLength ) || + FT_STREAM_SEEK ( entry->stringOffset ) || + FT_STREAM_READ_AT( entry->string, entry->stringLength ) ) + { + FT_FREE( entry->string ); + entry->stringLength = 0; + } + } + + aname->platform_id = entry->platformID; + aname->encoding_id = entry->encodingID; + aname->language_id = entry->languageID; + aname->name_id = entry->nameID; + aname->string = (FT_Byte*)entry->string; + aname->string_len = entry->stringLength; error = FT_Err_Ok; } diff --git a/src/sfnt/sfdriver.c b/src/sfnt/sfdriver.c index fc06ba414..e3a67ec74 100644 --- a/src/sfnt/sfdriver.c +++ b/src/sfnt/sfdriver.c @@ -124,39 +124,52 @@ /* either in Macintosh or Windows platform encodings */ found_win = -1; found_apple = -1; - + for ( n = 0; n < face->num_names; n++ ) { TT_NameEntryRec* name = face->name_table.names + n; - if ( name->nameID == 6 && name->string != NULL ) + if ( name->nameID == 6 && name->stringLength > 0 ) { if ( name->platformID == 3 && name->encodingID == 1 && name->languageID == 0x409 ) found_win = n; - + if ( name->platformID == 1 && name->encodingID == 0 && name->languageID == 0 ) found_apple = n; } } - + if ( found_win != -1 ) { FT_Memory memory = face->root.memory; TT_NameEntryRec* name = face->name_table.names + found_win; FT_UInt len = name->stringLength / 2; FT_Error error; - - if ( !FT_ALLOC( result, len + 1 ) ) + + if ( !FT_ALLOC( result, name->stringLength+1 ) ) { + FT_Stream stream = face->name_table.stream; FT_String* r = (FT_String*)result; FT_Byte* p = (FT_Byte*)name->string; - + + + if ( FT_STREAM_SEEK( name->stringOffset ) || + FT_FRAME_ENTER( name->stringLength ) ) + { + FT_FREE( result ); + name->stringLength = 0; + name->stringOffset = 0; + FT_FREE( name->string ); + goto Exit; + } + + p = (FT_Byte*) stream->cursor; for ( ; len > 0; len--, p += 2 ) { @@ -164,6 +177,8 @@ *r++ = p[1]; } *r = '\0'; + + FT_FRAME_EXIT(); } goto Exit; } @@ -178,10 +193,20 @@ if ( !FT_ALLOC( result, len + 1 ) ) { - FT_MEM_COPY( (char*)result, name->string, len ); + FT_Stream stream = face->name_table.stream; + + + if ( FT_STREAM_SEEK( name->stringOffset ) || + FT_STREAM_READ( result, len ) ) + { + name->stringOffset = 0; + name->stringLength = 0; + FT_FREE( name->string ); + FT_FREE( result ); + goto Exit; + } ((char*)result)[len] = '\0'; } - goto Exit; } Exit: diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c index f9c77bc1b..b9bbb331b 100644 --- a/src/sfnt/sfobjs.c +++ b/src/sfnt/sfobjs.c @@ -129,6 +129,9 @@ } + typedef FT_String* (*TT_NameEntry_ConvertFunc)( TT_NameEntry entry, + FT_Memory memory ); + /*************************************************************************/ /* */ /* */ @@ -157,6 +160,7 @@ FT_Int found_win = -1; FT_Int found_unicode = -1; + TT_NameEntry_ConvertFunc convert; rec = face->name_table.names; for ( n = 0; n < face->num_names; n++, rec++ ) @@ -170,7 +174,7 @@ /* thing and goes to suggest that all Unicode `name' table entries */ /* should be coded in UTF-16 (in big-endian format I suppose). */ /* */ - if ( rec->nameID == nameid && rec->string ) + if ( rec->nameID == nameid && rec->stringLength > 0 ) { switch ( rec->platformID ) { @@ -218,6 +222,7 @@ /* some fonts contain invalid Unicode or Macintosh formatted entries; */ /* we will thus favor names encoded in Windows formats if available */ /* */ + convert = NULL; if ( found_win >= 0 ) { rec = face->name_table.names + found_win; @@ -225,11 +230,11 @@ { case TT_MS_ID_UNICODE_CS: case TT_MS_ID_SYMBOL_CS: - result = tt_name_entry_ascii_from_utf16( rec, memory ); + convert = tt_name_entry_ascii_from_utf16; break; case TT_MS_ID_UCS_4: - result = tt_name_entry_ascii_from_ucs4( rec, memory ); + convert = tt_name_entry_ascii_from_ucs4; break; default: @@ -238,15 +243,38 @@ } else if ( found_apple >= 0 ) { - rec = face->name_table.names + found_apple; - result = tt_name_entry_ascii_from_other( rec, memory ); + rec = face->name_table.names + found_apple; + convert = tt_name_entry_ascii_from_other; } else if ( found_unicode >= 0 ) { - rec = face->name_table.names + found_unicode; - result = tt_name_entry_ascii_from_utf16( rec, memory ); + rec = face->name_table.names + found_unicode; + convert = tt_name_entry_ascii_from_utf16; } + if ( rec && convert ) + { + if ( rec->string == NULL ) + { + FT_Error error; + FT_Stream stream = face->name_table.stream; + + + if ( FT_NEW_ARRAY ( rec->string, rec->stringLength ) || + FT_STREAM_SEEK( rec->stringOffset ) || + FT_STREAM_READ( rec->string, rec->stringLength ) ) + { + FT_FREE( rec->string ); + rec->stringLength = 0; + result = NULL; + goto Exit; + } + } + + result = convert( rec, memory ); + } + + Exit: return result; } diff --git a/src/sfnt/ttload.c b/src/sfnt/ttload.c index 15f27867b..d8fed1f5f 100644 --- a/src/sfnt/ttload.c +++ b/src/sfnt/ttload.c @@ -934,14 +934,12 @@ { FT_Error error; FT_Memory memory = stream->memory; - FT_ULong table_pos, table_len; - FT_ULong storageOffset, storageSize; - FT_Byte* storage; - - TT_NameTable names; + FT_ULong storage_start, storage_limit; + FT_UInt count; + TT_NameTable table; - const FT_Frame_Field name_table_fields[] = + static const FT_Frame_Field name_table_fields[] = { #undef FT_STRUCTURE #define FT_STRUCTURE TT_NameTableRec @@ -953,7 +951,7 @@ FT_FRAME_END }; - const FT_Frame_Field name_record_fields[] = + static const FT_Frame_Field name_record_fields[] = { #undef FT_STRUCTURE #define FT_STRUCTURE TT_NameEntryRec @@ -969,6 +967,9 @@ }; + table = &face->name_table; + table->stream = stream; + FT_TRACE2(( "Names " )); error = face->goto_table( face, TTAG_name, stream, &table_len ); @@ -982,115 +983,74 @@ table_pos = FT_STREAM_POS(); - names = &face->name_table; - if ( FT_STREAM_READ_FIELDS( name_table_fields, names ) ) + if ( FT_STREAM_READ_FIELDS( name_table_fields, table ) ) goto Exit; - /* check the 'storageOffset' field */ - storageOffset = names->storageOffset; + /* some popular asian fonts have an invalid 'storageOffset' value */ + /* (it should be at least "6 + 12*num_names"). However, the string */ + /* offsets, computed as "storageOffset + entry->stringOffset" are */ + /* valid pointers within the name table... */ + /* */ + /* we thus can't check "storageOffset" right now */ + /* */ - /* Some broken Asian fonts have a storage offset whose value is */ - /* 12 * numNameRecords. We deal with them here. */ - if ( storageOffset == (FT_ULong)(12 * names->numNameRecords) ) - storageOffset += 6; - - if ( storageOffset < (FT_ULong)( 6 + 12 * names->numNameRecords ) || - table_len <= storageOffset ) + storage_start = table_pos + 6 + 12*table->numNameRecords; + storage_limit = table_pos + table_len; + + if ( storage_start > storage_limit ) { FT_ERROR(( "TT_Load_Names: invalid `name' table\n" )); error = SFNT_Err_Name_Table_Missing; goto Exit; } - storageSize = (FT_ULong)(table_len - storageOffset); - /* Allocate the array of name records. */ - if ( FT_ALLOC( names->names, - names->numNameRecords * sizeof ( TT_NameEntryRec ) + - storageSize ) || - FT_FRAME_ENTER( names->numNameRecords * 12L ) ) - goto Exit; + count = table->numNameRecords; + table->numNameRecords = 0; - storage = (FT_Byte*)( names->names + names->numNameRecords ); + if ( FT_NEW_ARRAY( table->names, count ) || + FT_FRAME_ENTER( count * 12 ) ) + goto Exit; /* Load the name records and determine how much storage is needed */ /* to hold the strings themselves. */ { - TT_NameEntryRec* cur = names->names; - TT_NameEntryRec* limit = cur + names->numNameRecords; + TT_NameEntryRec* entry = table->names; - for ( ; cur < limit; cur ++ ) + for ( ; count > 0; count-- ) { - if ( FT_STREAM_READ_FIELDS( name_record_fields, cur ) ) - break; + if ( FT_STREAM_READ_FIELDS( name_record_fields, entry ) ) + continue; + + /* check that the name is not empty */ + if ( entry->stringLength == 0 ) + continue; - /* invalid name entries will have "cur->string" set to NULL! */ - if ( (FT_ULong)( cur->stringOffset + cur->stringLength ) < storageSize ) - cur->string = storage + cur->stringOffset; - else + /* check that the name string is within the table */ + entry->stringOffset += table_pos + table->storageOffset; + if ( entry->stringOffset < storage_start || + entry->stringOffset + entry->stringLength > storage_limit ) { - /* that's an invalid entry */ - cur->stringOffset = 0; - cur->string = NULL; + /* invalid entry - ignore it */ + entry->stringOffset = 0; + entry->stringLength = 0; + continue; } + + entry++; } + + table->numNameRecords = entry - table->names; } FT_FRAME_EXIT(); - if ( error ) - goto Exit; - - storageOffset -= 6 + 12 * names->numNameRecords; - if ( FT_STREAM_SKIP( storageOffset ) || - FT_STREAM_READ( storage, storageSize ) ) - goto Exit; - -#ifdef FT_DEBUG_LEVEL_TRACE - - /* print Name Record Table in case of debugging */ - { - TT_NameEntryRec* cur = names->names; - TT_NameEntryRec* limit = cur + names->numNameRecords; - - - for ( ; cur < limit; cur++ ) - { - FT_UInt j; - - - FT_TRACE3(( "(%2d %2d %4x %2d) ", - cur->platformID, - cur->encodingID, - cur->languageID, - cur->nameID )); - - /* I know that M$ encoded strings are Unicode, */ - /* but this works reasonable well for debugging purposes. */ - if ( cur->string ) - for ( j = 0; j < (FT_UInt)cur->stringLength; j++ ) - { - FT_Byte c = *(FT_Byte*)( cur->string + j ); - - - if ( c >= 32 && c < 128 ) - FT_TRACE3(( "%c", c )); - } - else - FT_TRACE3(( "Invalid entry!\n" )); - - FT_TRACE3(( "\n" )); - } - } - -#endif /* FT_DEBUG_LEVEL_TRACE */ - FT_TRACE2(( "loaded\n" )); /* everything went well, update face->num_names */ - face->num_names = names->numNameRecords; + face->num_names = table->numNameRecords; Exit: return error; @@ -1112,18 +1072,23 @@ TT_Free_Names( TT_Face face ) { FT_Memory memory = face->root.driver->root.memory; - TT_NameTable names = &face->name_table; + TT_NameTable table = &face->name_table; + TT_NameEntry entry = table->names; + FT_UInt count = table->numNameRecords; - /* free strings table */ - FT_FREE( names->names ); + for ( ; count > 0; count--, entry++ ) + { + FT_FREE( entry->string ); + entry->stringLength = 0; + } - /* free strings storage */ - FT_FREE( names->storage ); + /* free strings table */ + FT_FREE( table->names ); - names->numNameRecords = 0; - names->format = 0; - names->storageOffset = 0; + table->numNameRecords = 0; + table->format = 0; + table->storageOffset = 0; } @@ -1161,7 +1126,7 @@ error = SFNT_Err_CMap_Table_Missing; goto Exit; } - + if ( !FT_FRAME_EXTRACT( face->cmap_size, face->cmap_table ) ) FT_TRACE2(( "`cmap' table loaded\n" )); else @@ -1169,13 +1134,13 @@ FT_ERROR(( "`cmap' table is too short!\n" )); face->cmap_size = 0; } - + Exit: return error; } #else /* !FT_CONFIG_OPTION_USE_CMAPS */ - + FT_LOCAL_DEF( FT_Error ) TT_Load_CMap( TT_Face face, FT_Stream stream )