diff --git a/ChangeLog b/ChangeLog index bd45d755e..7f4017e9f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2017-01-26 Werner Lemberg + + [sfnt] Support `name' table format 1. + + * include/freetype/internal/tttypes.h (TT_LangTagRec): New + structure. + (TT_NameTableRec): Add fields `numLangTagRecords' and `langTags'. + + * src/sfnt/ttload.c (tt_face_load_name): Add support for language + tags. + Reduce array size of name strings in case of invalid entries. + (tt_face_free_name): Updated. + + * docs/CHANGES: Updated. + 2017-01-25 Werner Lemberg [sfnt] s/TT_NameEntry/TT_Name/. diff --git a/docs/CHANGES b/docs/CHANGES index 2f60cf578..0dec77062 100644 --- a/docs/CHANGES +++ b/docs/CHANGES @@ -39,6 +39,13 @@ CHANGES BETWEEN 2.7.1 and 2.7.2 The old macro names are deprecated (but still available). + - Support for SFNT `name' tables has been improved. + + . Format 1 `name' tables are now supported. + + . Language ID and name ID values have been updated to OpenType version + 1.8.1. + ====================================================================== diff --git a/include/freetype/internal/tttypes.h b/include/freetype/internal/tttypes.h index d6cf93424..e7796471e 100644 --- a/include/freetype/internal/tttypes.h +++ b/include/freetype/internal/tttypes.h @@ -279,11 +279,41 @@ FT_BEGIN_HEADER /* this last field is not defined in the spec */ /* but used by the FreeType engine */ - FT_Byte* string; + FT_Byte* string; } TT_NameRec, *TT_Name; + /*************************************************************************/ + /* */ + /* */ + /* TT_LangTagRec */ + /* */ + /* */ + /* A structure modeling language tag records in SFNT `name' tables, */ + /* introduced in OpenType version 1.6. */ + /* */ + /* */ + /* stringLength :: The length of the string in bytes. */ + /* */ + /* stringOffset :: The offset to the string in the `name' table. */ + /* */ + /* string :: A pointer to the string's bytes. Note that these */ + /* are UTF-16BE encoded characters. */ + /* */ + typedef struct TT_LangTagRec_ + { + FT_UShort stringLength; + FT_ULong stringOffset; + + /* this last field is not defined in the spec */ + /* but used by the FreeType engine */ + + FT_Byte* string; + + } TT_LangTagRec, *TT_LangTag; + + /*************************************************************************/ /* */ /* */ @@ -293,24 +323,30 @@ FT_BEGIN_HEADER /* A structure modeling the TrueType name table. */ /* */ /* */ - /* format :: The format of the name table. */ + /* format :: The format of the name table. */ /* */ - /* numNameRecords :: The number of names in table. */ + /* numNameRecords :: The number of names in table. */ /* */ - /* storageOffset :: The offset of the name table in the `name' */ - /* TrueType table. */ + /* storageOffset :: The offset of the name table in the `name' */ + /* TrueType table. */ /* */ - /* names :: An array of name records. */ + /* names :: An array of name records. */ /* */ - /* stream :: the file's input stream. */ + /* numLangTagRecords :: The number of language tags in table. */ + /* */ + /* langTags :: An array of language tag records. */ + /* */ + /* stream :: The file's input stream. */ /* */ typedef struct TT_NameTableRec_ { - FT_UShort format; - FT_UInt numNameRecords; - FT_UInt storageOffset; - TT_NameRec* names; - FT_Stream stream; + FT_UShort format; + FT_UInt numNameRecords; + FT_UInt storageOffset; + TT_NameRec* names; + FT_UInt numLangTagRecords; + TT_LangTagRec* langTags; + FT_Stream stream; } TT_NameTableRec, *TT_NameTable; diff --git a/src/sfnt/ttload.c b/src/sfnt/ttload.c index 3ab211cd0..4695253f2 100644 --- a/src/sfnt/ttload.c +++ b/src/sfnt/ttload.c @@ -808,7 +808,6 @@ FT_Memory memory = stream->memory; FT_ULong table_pos, table_len; FT_ULong storage_start, storage_limit; - FT_UInt count; TT_NameTable table; static const FT_Frame_Field name_table_fields[] = @@ -838,6 +837,17 @@ FT_FRAME_END }; + static const FT_Frame_Field langTag_record_fields[] = + { +#undef FT_STRUCTURE +#define FT_STRUCTURE TT_LangTagRec + + /* no FT_FRAME_START */ + FT_FRAME_USHORT( stringLength ), + FT_FRAME_USHORT( stringOffset ), + FT_FRAME_END + }; + table = &face->name_table; table->stream = stream; @@ -848,18 +858,17 @@ table_pos = FT_STREAM_POS(); - if ( FT_STREAM_READ_FIELDS( name_table_fields, table ) ) goto Exit; - /* 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. */ - /* */ - storage_start = table_pos + 6 + 12*table->numNameRecords; + /* Some popular Asian fonts have an invalid `storageOffset' value (it */ + /* should be at least `6 + 12*numNameRecords'). However, the string */ + /* offsets, computed as `storageOffset + entry->stringOffset', are */ + /* valid pointers within the name table... */ + /* */ + /* We thus can't check `storageOffset' right now. */ + /* */ + storage_start = table_pos + 6 + 12 * table->numNameRecords; storage_limit = table_pos + table_len; if ( storage_start > storage_limit ) @@ -869,18 +878,56 @@ goto Exit; } - /* Allocate the array of name records. */ - count = table->numNameRecords; - table->numNameRecords = 0; + /* `name' format 1 contains additional language tag records, */ + /* which we load first */ + if ( table->format == 1 ) + { + if ( FT_STREAM_SEEK( storage_start ) || + FT_READ_USHORT( table->numLangTagRecords ) ) + goto Exit; - if ( FT_NEW_ARRAY( table->names, count ) || - FT_FRAME_ENTER( count * 12 ) ) + storage_start += 2 + 4 * table->numLangTagRecords; + + /* allocate language tag records array */ + if ( FT_NEW_ARRAY( table->langTags, table->numLangTagRecords ) || + FT_FRAME_ENTER( table->numLangTagRecords * 4 ) ) + goto Exit; + + /* load language tags */ + { + TT_LangTag entry = table->langTags; + TT_LangTag limit = entry + table->numLangTagRecords; + + + for ( ; entry < limit; entry++ ) + { + (void)FT_STREAM_READ_FIELDS( langTag_record_fields, entry ); + + /* check that the langTag string is within the table */ + entry->stringOffset += table_pos + table->storageOffset; + if ( entry->stringOffset < storage_start || + entry->stringOffset + entry->stringLength > storage_limit ) + { + /* invalid entry; ignore it */ + entry->stringLength = 0; + } + } + } + + FT_FRAME_EXIT(); + + (void)FT_STREAM_SEEK( table_pos + 6 ); + } + + /* allocate name records array */ + if ( FT_NEW_ARRAY( table->names, table->numNameRecords ) || + FT_FRAME_ENTER( table->numNameRecords * 12 ) ) goto Exit; - /* Load the name records and determine how much storage is needed */ - /* to hold the strings themselves. */ + /* load name records */ { TT_Name entry = table->names; + FT_UInt count = table->numNameRecords; for ( ; count > 0; count-- ) @@ -897,22 +944,37 @@ if ( entry->stringOffset < storage_start || entry->stringOffset + entry->stringLength > storage_limit ) { - /* invalid entry - ignore it */ - entry->stringOffset = 0; - entry->stringLength = 0; + /* invalid entry; ignore it */ continue; } + /* assure that we have a valid language tag ID, and */ + /* that the corresponding langTag entry is valid, too */ + if ( table->format == 1 && entry->languageID >= 0x8000U ) + { + if ( entry->languageID - 0x8000U >= table->numLangTagRecords || + !table->langTags[entry->languageID - 0x8000U].stringLength ) + { + /* invalid entry; ignore it */ + continue; + } + } + entry++; } - table->numNameRecords = (FT_UInt)( entry - table->names ); + /* reduce array size to the actually used elements */ + count = (FT_UInt)( entry - table->names ); + (void)FT_RENEW_ARRAY( table->names, + table->numNameRecords, + count ); + table->numNameRecords = count; } FT_FRAME_EXIT(); /* everything went well, update face->num_names */ - face->num_names = (FT_UShort) table->numNameRecords; + face->num_names = (FT_UShort)table->numNameRecords; Exit: return error; @@ -935,25 +997,36 @@ { FT_Memory memory = face->root.driver->root.memory; TT_NameTable table = &face->name_table; - TT_Name entry = table->names; - FT_UInt count = table->numNameRecords; if ( table->names ) { - for ( ; count > 0; count--, entry++ ) - { - FT_FREE( entry->string ); - entry->stringLength = 0; - } + TT_Name entry = table->names; + TT_Name limit = entry + table->numNameRecords; + + + for ( ; entry < limit; entry++ ) + FT_FREE( entry->string ); - /* free strings table */ FT_FREE( table->names ); } - table->numNameRecords = 0; - table->format = 0; - table->storageOffset = 0; + if ( table->langTags ) + { + TT_LangTag entry = table->langTags; + TT_LangTag limit = entry + table->numLangTagRecords; + + + for ( ; entry < limit; entry++ ) + FT_FREE( entry->string ); + + FT_FREE( table->langTags ); + } + + table->numNameRecords = 0; + table->numLangTagRecords = 0; + table->format = 0; + table->storageOffset = 0; }