From 617a2e1c3c8176e8ae31313a784829f278b1766e Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 27 Feb 2002 21:25:47 +0000 Subject: [PATCH] adding several experimental sources: - OpenType Layout validation and parsing (common tables) - Type 1 charmap processing --- include/freetype/internal/ftobjs.h | 256 +++++---- src/otlayout/otlcommn.c | 820 +++++++++++++++++++++++++++++ src/otlayout/otlcommn.h | 211 ++++++++ src/sfnt/sfobjs.c | 1 + src/sfnt/ttcmap0.c | 454 +++++++++++----- src/type1/t1cmap.c | 414 +++++++++++++++ src/type1/t1cmap.h | 92 ++++ 7 files changed, 2017 insertions(+), 231 deletions(-) create mode 100644 src/otlayout/otlcommn.c create mode 100644 src/otlayout/otlcommn.h create mode 100644 src/type1/t1cmap.c create mode 100644 src/type1/t1cmap.h diff --git a/include/freetype/internal/ftobjs.h b/include/freetype/internal/ftobjs.h index 092ae3f6a..fe53a2350 100644 --- a/include/freetype/internal/ftobjs.h +++ b/include/freetype/internal/ftobjs.h @@ -74,6 +74,159 @@ FT_BEGIN_HEADER #endif + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** V A L I D A T I O N ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + /* handle to a validation object */ + typedef struct FT_ValidatorRec_* FT_Validator; + + /************************************************************************** + * + * there are three distinct validation levels defined here: + * + * FT_VALIDATE_DEFAULT :: + * a table that passes this validation level can be used reliably by + * FreeType. It generally means that all offsets have been checked to + * prevent out-of-bound reads, array counts are correct, etc.. + * + * + * FT_VALIDATE_TIGHT :: + * a table that passes this validation level can be used reliably and + * doesn't contain invalid data. For example, a charmap table that + * returns invalid glyph indices will not pass, even though it can + * be used with FreeType in default mode (the library will simply + * return an error later when trying to load the glyph) + * + * it also check that fields that must be a multiple of 2, 4 or 8 don't + * have incorrect values, etc.. + * + * + * FT_VALIDATE_PARANOID :: + * only for font facists. Checks that a table follows the specification + * 100%. Very few fonts will be able to pass this level anyway but it + * can be useful for certain tools like font editors/converters.. + */ + typedef enum FT_ValidationLevel_ + { + FT_VALIDATE_DEFAULT = 0, + FT_VALIDATE_TIGHT, + FT_VALIDATE_PARANOID + + } FT_ValidationLevel; + + + /* validator structure */ + typedef struct FT_ValidatorRec_ + { + FT_Byte* base; /* address of table in memory */ + FT_Byte* limit; /* 'base' + sizeof(table) in memory */ + FT_ValidationLevel level; /* validation level */ + FT_Error error; /* error returned. 0 means success */ + + jmp_buf jump_buffer; /* used for exception handling */ + + } FT_ValidatorRec; + + + /* sets the error field in a validator, then calls 'longjmp' to return */ + /* to high-level caller. Using 'setjmp/longjmp' avoids many stupid */ + /* error checks within the validation routines.. */ + /* */ + FT_BASE( void ) + ft_validate_error( FT_Valid valid, + FT_Error error ); + + /* calls ft_validate_error. Assumes that the 'valid' local variable holds */ + /* a pointer to the current validator object.. */ + /* */ +#define FT_INVALID(_error) ft_validate_error( valid, _error ) + + /* called when a broken table is detected */ +#define FT_INVALID_TOO_SHORT FT_INVALID( FT_Err_Invalid_Format ) + + /* called when an invalid offset is detected */ +#define FT_INVALID_OFFSET FT_INVALID( FT_Err_Invalid_Offset ) + + /* called when an invalid format/value is detected */ +#define FT_INVALID_FORMAT FT_INVALID( FT_Err_Invalid_Format ) + + /* called when an invalid glyph index is detected */ +#define FT_INVALID_GLYPH_ID FT_INVALID( FT_Err_Invalid_Glyph_Id ) + + + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + /**** ****/ + /**** ****/ + /**** C H A R M A P S ****/ + /**** ****/ + /**** ****/ + /*************************************************************************/ + /*************************************************************************/ + /*************************************************************************/ + + /* handle to internal charmap object */ + typedef struct FT_CMapRec_* FT_CMap; + + /* handle to charmap class structure */ + typedef const struct FT_CMap_ClassRec_* FT_CMap_Class; + + /* internal charmap object structure */ + typedef struct FT_CMapRec_ + { + FT_CharMapRec charmap; + FT_CMap_Class clazz; + FT_Pointer data; + + } FT_CMapRec; + + /* typecase any pointer to a charmap handle */ +#define FT_CMAP(x) ((FT_CMap)(x)) + + /* obvious macros */ +#define FT_CMAP_PLATFORM_ID(x) FT_CMAP(x)->charmap.platform_id +#define FT_CMAP_ENCODING_ID(x) FT_CMAP(x)->charmap.encoding_id +#define FT_CMAP_ENCODING(x) FT_CMAP(x)->charmap.encoding +#define FT_CMAP_FACE(x) FT_CMAP(x)->charmap.face + + + /* class method definitions */ + typedef FT_Error (*FT_CMap_InitFunc)( FT_CMap cmap, + FT_Pointer data ); + + typedef void (*FT_CMap_DoneFunc)( FT_CMap cmap ); + + typedef FT_Error (*FT_CMap_ValidateFunc)( FT_Pointer cmap_data, + FT_Validator valid ); + + typedef FT_UInt (*FT_CMap_CharIndexFunc)( FT_Pointer cmap_data, + FT_ULong char_code ); + + typedef FT_UInt (*FT_CMap_CharNextFunc)( FT_Pointer cmap_data, + FT_ULong *achar_code ); + + typedef struct FT_CMap_ClassRec_ + { + FT_UInt size; + FT_CMap_InitFunc init; + FT_CMap_DoneFunc done; + FT_CMap_ValidateFunc validate; + FT_CMap_CharIndexFunc char_index; + FT_CMap_CharNextFunc char_next; + + } FT_CMap_ClassRec; + + /*************************************************************************/ /* */ /* */ @@ -330,109 +483,6 @@ FT_BEGIN_HEADER FT_Done_GlyphSlot( FT_GlyphSlot slot ); -#if 0 - - /*************************************************************************/ - /*************************************************************************/ - /*************************************************************************/ - /**** ****/ - /**** ****/ - /**** G L Y P H L O A D E R ****/ - /**** ****/ - /**** ****/ - /*************************************************************************/ - /*************************************************************************/ - /*************************************************************************/ - - -#define FT_SUBGLYPH_FLAG_ARGS_ARE_WORDS 1 -#define FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES 2 -#define FT_SUBGLYPH_FLAG_ROUND_XY_TO_GRID 4 -#define FT_SUBGLYPH_FLAG_SCALE 8 -#define FT_SUBGLYPH_FLAG_XY_SCALE 0x40 -#define FT_SUBGLYPH_FLAG_2X2 0x80 -#define FT_SUBGLYPH_FLAG_USE_MY_METRICS 0x200 - - - enum - { - FT_GLYPH_OWN_BITMAP = 1 - }; - - - struct FT_SubGlyph_ - { - FT_Int index; - FT_UShort flags; - FT_Int arg1; - FT_Int arg2; - FT_Matrix transform; - }; - - - typedef struct FT_GlyphLoad_ - { - FT_Outline outline; /* outline */ - FT_UInt num_subglyphs; /* number of subglyphs */ - FT_SubGlyph* subglyphs; /* subglyphs */ - FT_Vector* extra_points; /* extra points table */ - - } FT_GlyphLoad; - - - struct FT_GlyphLoader_ - { - FT_Memory memory; - FT_UInt max_points; - FT_UInt max_contours; - FT_UInt max_subglyphs; - FT_Bool use_extra; - - FT_GlyphLoad base; - FT_GlyphLoad current; - - void* other; /* for possible future extension? */ - - }; - - - FT_BASE( FT_Error ) - FT_GlyphLoader_New( FT_Memory memory, - FT_GlyphLoader* *aloader ); - - FT_BASE( FT_Error ) - FT_GlyphLoader_Create_Extra( FT_GlyphLoader* loader ); - - FT_BASE( void ) - FT_GlyphLoader_Done( FT_GlyphLoader* loader ); - - FT_BASE( void ) - FT_GlyphLoader_Reset( FT_GlyphLoader* loader ); - - FT_BASE( void ) - FT_GlyphLoader_Rewind( FT_GlyphLoader* loader ); - - FT_BASE( FT_Error ) - FT_GlyphLoader_Check_Points( FT_GlyphLoader* loader, - FT_UInt n_points, - FT_UInt n_contours ); - - FT_BASE( FT_Error ) - FT_GlyphLoader_Check_Subglyphs( FT_GlyphLoader* loader, - FT_UInt n_subs ); - - FT_BASE( void ) - FT_GlyphLoader_Prepare( FT_GlyphLoader* loader ); - - FT_BASE( void ) - FT_GlyphLoader_Add( FT_GlyphLoader* loader ); - - FT_BASE( FT_Error ) - FT_GlyphLoader_Copy_Points( FT_GlyphLoader* target, - FT_GlyphLoader* source ); - -#endif - /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ diff --git a/src/otlayout/otlcommn.c b/src/otlayout/otlcommn.c new file mode 100644 index 000000000..7d22fc8d2 --- /dev/null +++ b/src/otlayout/otlcommn.c @@ -0,0 +1,820 @@ +#include "otlayout.h" + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** COVERAGE TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + OTL_LOCALDEF( void ) + otl_coverage_validate( OTL_Bytes table, + OTL_Validator valid ) + { + OTL_Bytes p; + OTL_UInt format; + + if ( table + 4 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + format = OTL_NEXT_UShort(p); + switch (format) + { + case 1: + { + OTL_UInt count = OTL_NEXT_UShort(p); + + if ( p + count*2 >= valid->limit ) + OTL_INVALID_TOO_SHORT; + + /* XXX: check glyph indices */ + } + break; + + case 2: + { + OTL_UInt n, num_ranges = OTL_NEXT_UShort(p); + OTL_UInt start, end, start_cover, total = 0, last = 0; + + if ( p + num_ranges*6 >= valid->limit ) + OTL_INVALID_TOO_SHORT; + + for ( n = 0; n < num_ranges; n++ ) + { + start = OTL_NEXT_UShort(p); + end = OTL_NEXT_UShort(p); + start_cover = OTL_NEXT_UShort(p); + + if ( start > end || start_cover != total ) + OTL_INVALID_DATA; + + if ( n > 0 && start <= last ) + OTL_INVALID_DATA; + + total += (end - start + 1); + last = end; + } + } + break; + + default: + OTL_INVALID_FORMAT; + } + } + + + OTL_LOCALDEF( OTL_UInt ) + otl_coverage_get_count( OTL_Bytes table ) + { + OTL_Bytes p = table; + OTL_UInt format = OTL_NEXT_UShort(p); + OTL_UInt count = OTL_NEXT_UShort(p); + OTL_UInt result = 0; + + switch ( format ) + { + case 1: + return count; + + case 2: + { + OTL_UInt start, end; + + for ( ; count > 0; count-- ) + { + start = OTL_NEXT_UShort(p); + end = OTL_NEXT_UShort(p); + p += 2; /* skip start_index */ + + result += ( end - start + 1 ); + } + } + break; + + default: + ; + } + + return result; + } + + + OTL_LOCALDEF( OTL_Int ) + otl_coverage_get_index( OTL_Bytes table, + OTL_UInt glyph_index ) + { + OTL_Bytes p = table; + OTL_UInt format = OTL_NEXT_UShort(p); + OTL_UInt count = OTL_NEXT_UShort(p); + + switch ( format ) + { + case 1: + { + OTL_UInt min = 0, max = count, mid, gindex; + + table += 4; + while ( min < max ) + { + mid = ( min + max ) >> 1; + p = table + 2*mid; + gindex = OTL_PEEK_UShort(p); + + if ( glyph_index == gindex ) + return (OTL_Int)mid; + + if ( glyph_index < gindex ) + max = mid; + else + min = mid + 1; + } + } + break; + + case 2: + { + OTL_UInt min = 0, max = count, mid; + OTL_UInt start, end, delta, start_cover; + + table += 4; + while ( min < max ) + { + mid = ( min + max ) >> 1; + p = table + 6*mid; + start = OTL_NEXT_UShort(p); + end = OTL_NEXT_UShort(p); + + if ( glyph_index < start ) + max = mid; + else if ( glyph_index > end ) + min = mid + 1; + else + return (OTL_Int)( glyph_index + OTL_NEXT_UShort(p) - start ); + } + } + break; + + default: + ; + } + return -1; + } + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CLASS DEFINITION TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + OTL_LOCALDEF( void ) + otl_class_definition_validate( OTL_Bytes table, + OTL_Validator valid ) + { + OTL_Bytes p = table; + OTL_UInt format; + + if ( p + 4 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + format = OTL_NEXT_UShort(p); + switch ( format ) + { + case 1: + { + OTL_UInt count, start = OTL_NEXT_UShort(p); + + if ( p + 2 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + count = OTL_NEXT_UShort(p); + + if ( p + count*2 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + /* XXX: check glyph indices */ + } + break; + + case 2: + { + OTL_UInt n, num_ranges = OTL_NEXT_UShort(p); + OTL_UInt start, end, value, last = 0; + + if ( p + num_ranges*6 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + for ( n = 0; n < num_ranges; n++ ) + { + start = OTL_NEXT_UShort(p); + end = OTL_NEXT_UShort(p); + value = OTL_NEXT_UShort(p); /* ignored */ + + if ( start > end || ( n > 0 && start <= last ) ) + OTL_INVALID_DATA; + + last = end; + } + } + break; + + default: + OTL_INVALID_FORMAT; + } + } + + + OTL_LOCALDEF( OTL_UInt ) + otl_class_definition_get_value( OTL_Bytes table, + OTL_UInt glyph_index ) + { + OTL_Bytes p = table; + OTL_UInt format = OTL_NEXT_UShort(p); + + switch ( format ) + { + case 1: + { + OTL_UInt start = OTL_NEXT_UShort(p); + OTL_UInt count = OTL_NEXT_UShort(p); + OTL_UInt index = (OTL_UInt)( glyph_index - start ); + + if ( index < count ) + { + p += 2*index; + return OTL_PEEK_UShort(p); + } + } + break; + + case 2: + { + OTL_UInt count = OTL_NEXT_UShort(p); + OTL_UInt min = 0, max = count, mid, gindex; + + table += 4; + while ( min < max ) + { + mid = ( min + max ) >> 1; + p = table + 6*mid; + start = OTL_NEXT_UShort(p); + end = OTL_NEXT_UShort(p); + + if ( glyph_index < start ) + max = mid; + else if ( glyph_index > end ) + min = mid+1; + else + return OTL_PEEK_UShort(p); + } + } + break; + + default: + ; + } + return 0; + } + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** DEVICE TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + OTL_LOCALDEF( void ) + otl_device_table_validate( OTL_Bytes table, + OTL_Validator valid ) + { + OTL_Bytes p = table; + OTL_UInt start, end, count, format, count; + + if ( p + 8 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + start = OTL_NEXT_UShort(p); + end = OTL_NEXT_UShort(p); + format = OTL_NEXT_UShort(p); + + if ( format < 1 || format > 3 || end < start ) + OTL_INVALID_DATA; + + count = (OTL_UInt)( end - start + 1 ); + + if ( p + ((1 << format)*count)/8> valid->limit ) + OTL_INVALID_TOO_SHORT; + } + + + OTL_LOCALDEF( OTL_UInt ) + otl_device_table_get_start( OTL_Bytes table ) + { + OTL_Bytes p = table; + + return OTL_PEEK_UShort(p); + } + + + OTL_LOCALDEF( OTL_UInt ) + otl_device_table_get_end( OTL_Bytes table ) + { + OTL_Bytes p = table + 2; + + return OTL_PEEK_UShort(p); + } + + + OTL_LOCALDEF( OTL_Int ) + otl_device_table_get_delta( OTL_Bytes table, + OTL_UInt size ) + { + OTL_Bytes p = table; + OTL_Int result = 0; + OTL_UInt start, end, format, index, value; + + start = OTL_NEXT_UShort(p); + end = OTL_NEXT_UShort(p); + format = OTL_NEXT_UShort(p); + + if ( size >= start && size <= end ) + { + /* we could do that with clever bit operations, but a switch is simply */ + /* much simpler to understand and maintain !! */ + /* */ + switch ( format ) + { + case 1: + { + index = (OTL_UInt)(( size - start )*2); + p += (index/16); + value = OTL_PEEK_UShort(p); + shift = index & 15; + result = (OTL_Short)(value << shift) >> (14-shift); + } + break; + + case 2: + { + index = (OTL_UInt)(( size - start )*4); + p += (index/16); + value = OTL_PEEK_UShort(p); + shift = index & 15; + result = (OTL_Short)(value << shift) >> (12-shift); + } + break; + + case 3: + { + index = (OTL_UInt)(( size - start )*8); + p += (index/16); + value = OTL_PEEK_UShort(p); + shift = index & 15; + result = (OTL_Short)(value << shift) >> (8-shift); + } + break; + + default: + ; + } + } + return result; + } + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** LOOKUP LISTS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + OTL_LOCALDEF( void ) + otl_lookup_validate( OTL_Bytes table, + OTL_Validator valid ) + { + OTL_Bytes p = table; + OTL_UInt num_tables; + + if ( table + 6 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + p += 4; + num_tables = OTL_NEXT_UShort(p); + + if ( p + num_tables*2 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + for ( ; num_tables > 0; num_tables-- ) + { + offset = OTL_NEXT_UShort(p); + + if ( table + offset >= valid->limit ) + OTL_INVALID_OFFSET; + } + + /* XXX: check sub-tables ?? */ + } + + + OTL_LOCALDEF( OTL_UInt ) + otl_lookup_get_count( OTL_Bytes table ) + { + OTL_Bytes p = table + 4; + + return OTL_PEEK_UShort(p); + } + + + OTL_LOCALDEF( OTL_Bytes ) + otl_lookup_get_table( OTL_Bytes table, + OTL_UInt index ) + { + OTL_Bytes p, result = NULL; + OTL_UInt count; + + p = table + 4; + count = OTL_NEXT_UShort(p); + if ( index < count ) + { + p += index*2; + result = table + OTL_PEEK_UShort(p); + } + return result; + } + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** LOOKUP LISTS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + OTL_LOCALDEF( void ) + otl_lookup_list_validate( OTL_Bytes table, + OTL_Validator valid ) + { + OTL_Bytes p = table, q; + OTL_UInt num_lookups, offset; + + if ( p + 2 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + num_lookups = OTL_NEXT_UShort(p); + + if ( p + num_lookups*2 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + for ( ; num_lookups > 0; num_lookups-- ) + { + + offset = OTL_NEXT_UShort(p); + + otl_lookup_validate( table + offset, valid ); + } + } + + + OTL_LOCALDEF( OTL_UInt ) + otl_lookup_list_get_count( OTL_Bytes table ) + { + OTL_Bytes p = table; + + return OTL_PEEK_UShort(p); + } + + + OTL_LOCALDEF( OTL_Bytes ) + otl_lookup_list_get_lookup( OTL_Bytes table, + OTL_UInt index ) + { + OTL_Bytes p, result = 0; + OTL_UInt count; + + p = table; + count = OTL_NEXT_UShort(p); + if ( index < count ) + { + p += index*2; + result = table + OTL_PEEK_UShort(p); + } + return result; + } + + + OTL_LOCALDEF( OTL_Bytes ) + otl_lookup_list_get_table( OTL_Bytes table, + OTL_UInt lookup_index, + OTL_UInt table_index ) + { + OTL_Bytes result = NULL; + + result = otl_lookup_list_get_lookup( table, lookup_index ); + if ( result ) + result = otl_lookup_get_table( result, table_index ); + + return result; + } + + + OTL_LOCALDEF( void ) + otl_lookup_list_foreach( OTL_Bytes table, + OTL_ForeachFunc func, + OTL_Pointer func_data ) + { + OTL_Bytes p = table; + OTL_UInt count = OTL_NEXT_UShort(p); + + for ( ; count > 0; count-- ) + func( table + OTL_NEXT_USHORT(p), func_data ); + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FEATURES *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + OTL_LOCALDEF( void ) + otl_feature_validate( OTL_Bytes table, + OTL_Validator valid ) + { + OTL_Bytes p = table; + OTL_UInt feat_params, num_lookups; + + if ( p + 4 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + feat_params = OTL_NEXT_UShort(p); /* ignored */ + num_lookups = OTL_NEXT_UShort(p); + + if ( p + num_lookups*2 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + /* XXX: check lookup indices */ + } + + + OTL_LOCALDEF( OTL_UInt ) + otl_feature_get_count( OTL_Bytes table ) + { + OTL_Bytes p = table + 4; + + return OTL_PEEK_UShort(p); + } + + + OTL_LOCALDEF( OTL_UInt ) + otl_feature_get_lookups( OTL_Bytes table, + OTL_UInt start, + OTL_UInt count, + OTL_UInt *lookups ) + { + OTL_Bytes p; + OTL_UInt num_features, result = 0; + + p = table + 4; + num_features = OTL_NEXT_UShort(p); + + p += start*2; + + for ( ; count > 0 && start < num_features; count--, start++ ) + { + lookups[0] = OTL_NEXT_UShort(p); + lookups++; + result++; + } + + return result; + } + + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FEATURE LIST *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + OTL_LOCALDEF( void ) + otl_feature_list_validate( OTL_Bytes table, + OTL_Validator valid ) + { + OTL_Bytes p = table; + OTL_UInt num_features, offset; + + if ( table + 2 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + num_features = OTL_NEXT_UShort(p); + + if ( p + num_features*2 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + for ( ; num_features > 0; num_features-- ) + { + p += 4; /* skip tag */ + offset = OTL_NEXT_UShort(p); + + otl_feature_table_validate( table + offset, valid ); + } + } + + + OTL_LOCALDEF( OTL_UInt ) + otl_feature_list_get_count( OTL_Bytes table ) + { + OTL_Bytes p = table; + + return OTL_PEEK_UShort(p); + } + + + OTL_LOCALDEF( OTL_Bytes ) + otl_feature_list_get_feature( OTL_Bytes table, + OTL_UInt index ) + { + OTL_Bytes p, result = NULL; + OTL_UInt count; + + p = table; + count = OTL_NEXT_UShort(p); + + if ( index < count ) + { + p += index*2; + result = table + OTL_PEEK_UShort(p); + } + return result; + } + + + OTL_LOCALDEF( void ) + otl_feature_list_foreach( OTL_Bytes table, + OTL_ForeachFunc func, + OTL_Pointer func_data ) + { + OTL_Bytes p; + OTL_UInt count; + + p = table; + count = OTL_NEXT_UShort(p); + + for ( ; count > 0; count-- ) + func( table + OTL_NEXT_UShort(p), func_data ); + } + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + OTL_LOCALDEF( void ) + otl_lang_validate( OTL_Bytes table, + OTL_Validator valid ) + { + OTL_Bytes p = table; + OTL_UInt lookup_order; + OTL_UInt req_feature; + OTL_UInt num_features; + + if ( table + 6 >= valid->limit ) + OTL_INVALID_TOO_SHORT; + + lookup_order = OTL_NEXT_UShort(p); + req_feature = OTL_NEXT_UShort(p); + num_features = OTL_NEXT_UShort(p); + + /* XXX: check req_feature if not 0xFFFFU */ + + if ( p + 2*num_features >= valid->limit ) + OTL_INVALID_TOO_SHORT; + + /* XXX: check features indices !! */ + } + + + OTL_LOCALDEF( OTL_UInt ) + otl_lang_get_count( OTL_Bytes table ) + { + OTL_Bytes p = table + 4; + + return OTL_PEEK_UShort(p); + } + + + OTL_LOCALDEF( OTL_UInt ) + otl_lang_get_features( OTL_Bytes table, + OTL_UInt start, + OTL_UInt count, + OTL_UInt *features ) + { + OTL_Bytes p = table + 4; + OTL_UInt num_features = OTL_NEXT_UShort(p); + OTL_UInt result = 0; + + p += start*2; + + for ( ; count > 0 && start < num_features; start++, count-- ) + { + features[0] = OTL_NEXT_UShort(p); + features++; + result++; + } + return result; + } + + + OTL_LOCALDEF( OTL_UInt ) + otl_lang_get_req_feature( OTL_Bytes table ) + { + OTL_Bytes p = table + 2; + + return OTL_PEEK_UShort(p); + } + + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + OTL_LOCALDEF( void ) + otl_script_table_validate( OTL_Bytes table, + OTL_Validator valid ) + { + OTL_UInt default_lang; + OTL_Bytes p = table; + + if ( table + 4 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + default_lang = OTL_NEXT_UShort(p); + num_langs = OTL_NEXT_UShort(p); + + if ( default_lang != 0 ) + { + if ( table + default_lang >= valid->limit ) + OTL_INVALID_OFFSET; + } + + if ( p + num_langs*6 >= valid->limit ) + OTL_INVALID_OFFSET; + + for ( ; num_langs > 0; num_langs-- ) + { + OTL_UInt offset; + + p += 4; /* skip tag */ + offset = OTL_NEXT_UShort(p); + + otl_lang_validate( table + offset, valid ); + } + } + + + OTL_LOCALDEF( void ) + otl_script_list_validate( OTL_Bytes list, + OTL_Validator valid ) + { + OTL_UInt num_scripts; + OTL_Bytes p = list; + + if ( list + 2 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + num_scripts = OTL_NEXT_UShort(p); + + if ( p + num_scripts*6 > valid->limit ) + OTL_INVALID_TOO_SHORT; + + for ( ; num_scripts > 0; num_scripts-- ) + { + OTL_UInt offset; + + p += 4; /* skip tag */ + offset = OTL_NEXT_UShort(p); + + otl_script_table_validate( list + offset, valid ); + } + } + + diff --git a/src/otlayout/otlcommn.h b/src/otlayout/otlcommn.h new file mode 100644 index 000000000..8ee2b27c6 --- /dev/null +++ b/src/otlayout/otlcommn.h @@ -0,0 +1,211 @@ +/*************************************************************************** + * + * otlcommn.h + * + * OpenType Layout common tables processing + * + * this header provides several routines used to process common table + * found in various OpenType Layout tables.. + */ +#ifndef __OTLAYOUT_COMMON_H__ +#define __OTLAYOUT_COMMON_H__ + +#include "otlayout.h" + +OTL_BEGIN_HEADER + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** COVERAGE TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* validate coverage table */ + OTL_LOCALDEF( void ) + otl_coverage_validate( OTL_Bytes base, + OTL_Validator valid ); + + /* return number of covered glyphs */ + OTL_LOCALDEF( OTL_UInt ) + otl_coverage_get_count( OTL_Bytes base ); + + + /* return the coverage index corresponding to a glyph glyph index. */ + /* returns -1 if the glyph isn't covered.. */ + OTL_LOCALDEF( OTL_Int ) + otl_coverage_get_index( OTL_Bytes base, + OTL_UInt glyph_index ); + + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** CLASS DEFINITION TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* validate class definition table */ + OTL_LOCALDEF( void ) + otl_class_definition_validate( OTL_Bytes table, + OTL_Validator valid ); + + /* return class value for a given glyph index */ + OTL_LOCALDEF( OTL_UInt ) + otl_class_definition_get_value( OTL_Bytes table, + OTL_UInt glyph_index ); + + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** DEVICE TABLE *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* validate a device table */ + OTL_LOCALDEF( void ) + otl_device_table_validate( OTL_Bytes table, + OTL_Validator valid ); + + + /* return a device table's first size */ + OTL_LOCALDEF( OTL_UInt ) + otl_device_table_get_start( OTL_Bytes table ); + + + /* return a device table's last size */ + OTL_LOCALDEF( OTL_UInt ) + otl_device_table_get_end( OTL_Bytes table ); + + + /* return pixel adjustment for a given size */ + OTL_LOCALDEF( OTL_Int ) + otl_device_table_get_delta( OTL_Bytes table, + OTL_UInt size ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** LOOKUPS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* validate lookup table */ + OTL_LOCALDEF( void ) + otl_lookup_validate( OTL_Bytes table, + OTL_Validator valid ); + + /* return number of sub-tables in a lookup */ + OTL_LOCALDEF( OTL_UInt ) + otl_lookup_get_count( OTL_Bytes table ); + + + /* return lookup sub-table */ + OTL_LOCALDEF( OTL_Bytes ) + otl_lookup_get_table( OTL_Bytes table, + OTL_UInt index ); + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** LOOKUP LISTS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* validate lookup list */ + OTL_LOCALDEF( void ) + otl_lookup_list_validate( OTL_Bytes table, + OTL_Validator valid ); + + /* return number of lookups in list */ + OTL_LOCALDEF( OTL_UInt ) + otl_lookup_list_get_count( OTL_Bytes table ); + + + /* return a given lookup from a list */ + OTL_LOCALDEF( OTL_Bytes ) + otl_lookup_list_get_lookup( OTL_Bytes table, + OTL_UInt index ); + + + /* return lookup sub-table from a list */ + OTL_LOCALDEF( OTL_Bytes ) + otl_lookup_list_get_table( OTL_Bytes table, + OTL_UInt lookup_index, + OTL_UInt table_index ); + + /* iterate over lookup list */ + OTL_LOCALDEF( void ) + otl_lookup_list_foreach( OTL_Bytes table, + OTL_ForeachFunc func, + OTL_Pointer func_data ); + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FEATURES *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* validate feature table */ + OTL_LOCALDEF( void ) + otl_feature_validate( OTL_Bytes table, + OTL_Validator valid ); + + /* return feature's lookup count */ + OTL_LOCALDEF( OTL_UInt ) + otl_feature_get_count( OTL_Bytes table ); + + /* get several lookups from a feature. returns the number of lookups grabbed */ + OTL_LOCALDEF( OTL_UInt ) + otl_feature_get_lookups( OTL_Bytes table, + OTL_UInt start, + OTL_UInt count, + OTL_UInt *lookups ); + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** FEATURE LIST *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + /* validate a feature list */ + OTL_LOCALDEF( void ) + otl_feature_list_validate( OTL_Bytes table, + OTL_Validator valid ); + + /* return number of features in list */ + OTL_LOCALDEF( OTL_UInt ) + otl_feature_list_get_count( OTL_Bytes table ); + + + /* return a given feature from a list */ + OTL_LOCALDEF( OTL_Bytes ) + otl_feature_list_get_feature( OTL_Bytes table, + OTL_UInt index ); + + /* iterate over all features in a list */ + OTL_LOCALDEF( void ) + otl_feature_list_foreach( OTL_Bytes table, + OTL_ForeachFunc func, + OTL_Pointer func_data ); + + /* */ + +OTL_END_HEADER + +#endif /* __OTLAYOUT_COMMON_H__ */ diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c index 119fc35cb..d0432a29f 100644 --- a/src/sfnt/sfobjs.c +++ b/src/sfnt/sfobjs.c @@ -300,6 +300,7 @@ /* fonts within PDF documents, so don't check for them. */ (void)LOAD_( max_profile ); (void)LOAD_( charmaps ); + /* the following tables are optional in PCL fonts -- */ /* don't check for errors */ diff --git a/src/sfnt/ttcmap0.c b/src/sfnt/ttcmap0.c index 9f3b6c42f..d416b1b3a 100644 --- a/src/sfnt/ttcmap0.c +++ b/src/sfnt/ttcmap0.c @@ -34,8 +34,8 @@ -#define TT_PEEK_Short FT_PEEK_SHORT -#define TT_PEEK_UShort FT_PEEK16_UBE +#define TT_PEEK_SHORT FT_PEEK_SHORT +#define TT_PEEK_USHORT FT_PEEK16_UBE #define TT_PEEK_Long FT_PEEK32_BE #define TT_PEEK_ULong FT_PEEK32_UBE @@ -72,7 +72,7 @@ tt_cmap0_validate( FT_Byte* table, FT_Validator valid ) { - FT_Byte* p = table + 2; /* skip format */ + FT_Byte* p = table + 2; FT_UInt length = TT_NEXT_USHORT(p); if ( table + length > valid->limit || length < 262 ) @@ -83,6 +83,7 @@ { FT_UInt n, index; + p = table + 6; for ( n = 0; n < 256; n++ ) { index = *p++; @@ -183,7 +184,7 @@ * keys 6 USHORT[256] sub-header keys * subs 518 SUBHEAD[NSUBS] sub-headers array * glyph_ids 518+NSUB*8 USHORT[] glyph id array - * + * * the 'keys' table is used to map charcode high-bytes to sub-headers. * the value of 'NSUBS' is the number of sub-headers defined in the * table and is computed by finding the maximum of the 'keys' table. @@ -228,7 +229,7 @@ FT_Validator valid ) { FT_Byte* p = table + 2; /* skip format */ - FT_UInt length = PEEK_UShort(p); + FT_UInt length = TT_PEEK_USHORT(p); FT_UInt n, max_subs; FT_Byte* keys; /* keys table */ FT_Byte* subs; /* sub-headers */ @@ -256,6 +257,8 @@ max_subs = index; } + FT_ASSERT( p == table + 518 ); + subs = p; glyph_ids = subs + (max_subs + 1)*8; if ( glyph_ids > valid->limit ) @@ -322,8 +325,8 @@ { FT_UInt char_lo = (FT_UInt)( char_code & 0xFF ); FT_UInt char_hi = (FT_UInt)( char_code >> 8 ); - FT_Byte* p = table + 6; /* keys table */ - FT_Byte* subs = p + 512; /* subheaders table */ + FT_Byte* p = table + 6; /* keys table */ + FT_Byte* subs = table + 518; /* subheaders table */ FT_Byte* sub; @@ -339,14 +342,14 @@ /* Otherwise, return 0 */ /* */ p += char_lo*2; - if ( PEEK_UShort(p) != 0 ) + if ( TT_PEEK_USHORT(p) != 0 ) goto Exit; } else { /* a 16-bit character code */ - p += char_hi*2; /* jump to key entry */ - sub = subs + PEEK_UShort(p); /* jump to sub-header */ + p += char_hi*2; /* jump to key entry */ + sub = subs + ( TT_PEEK_USHORT(p) & -8 ); /* jump to sub-header */ /* check that the hi byte isn't a valid one-byte value */ if ( sub == subs ) @@ -378,75 +381,22 @@ start = TT_NEXT_USHORT(p); count = TT_NEXT_USHORT(p); delta = TT_NEXT_SHORT(p); - offset = PEEK_UShort(p); + offset = TT_PEEK_USHORT(p); index -= start; if ( index < count && offset != 0 ) { p += offset + 2*index; - index = PEEK_UShort(p); + index = TT_PEEK_USHORT(p); - if ( index == 0 ) - goto Exit; - - result = (FT_UInt)( index + delta ) & 0xFFFFU; + if ( index != 0 ) + result = (FT_UInt)( index + delta ) & 0xFFFFU; } } - - Exit: return result; } - /* return first valid charcode in a format 2 sub-header */ - static FT_ULong - tt_cmap2_subheader_first( FT_Byte* subheader, - FT_UInt char_hi, - FT_UInt *agindex ) - { - FT_ULong result = 0; - FT_UInt n, gindex = 0; - FT_Byte* p = subheader; - - FT_UInt start = TT_NEXT_USHORT(p); - FT_UInt count = TT_NEXT_USHORT(p); - - if ( count > 0 ) - { - FT_Int delta = TT_NEXT_SHORT(p); - FT_UInt offset = TT_NEXT_USHORT(p); - - if ( offset == 0 ) - { - /* simple difference, compute directly */ - result = char_hi*256 + start; - gindex = (FT_UInt)( start + delta ) & 0xFFFFU; - } - else - { - FT_UInt i, index; - - /* parse glyph id table for non-0 indices */ - p += offset - 2; - for (; i < count; i++ ) - { - index = TT_NEXT_USHORT(p); - if ( index != 0 ) - { - result = char_hi*256 + start + i; - gindex = (FT_UInt)(index + delta) & 0xFFFFU; - break; - } - } - } - } - - if ( agindex ) - *agindex = gindex; - - return result; - } - static FT_UInt tt_cmap2_char_next( FT_Byte* table, @@ -459,7 +409,7 @@ FT_Byte* p; ++char_code; - for (;;) + while ( char_code < 0x10000U ) { subheader = tt_cmap2_get_subheader( table, char_code ); if ( subheader ) @@ -468,7 +418,7 @@ FT_UInt start = TT_NEXT_USHORT(p); FT_UInt count = TT_NEXT_USHORT(p); FT_Int delta = TT_NEXT_SHORT(p); - FT_UInt offset = PEEK_UShort(p); + FT_UInt offset = TT_PEEK_USHORT(p); FT_UInt char_lo = (FT_UInt)( char_code & 0xFF ); FT_UInt pos, index; @@ -505,8 +455,6 @@ /* jump to next sub-header, i.e. higher byte value */ Next_SubHeader: char_code = (char_code & -256) + 256; - if ( char_code >= 0x10000U ) - break; } Exit: @@ -534,6 +482,58 @@ /************************************************************************/ /************************************************************************/ + /************************************************************************* + * + * TABLE OVERVIEW: + * --------------- + * + * NAME OFFSET TYPE DESCRIPTION + * + * format 0 USHORT must be 4 + * length 2 USHORT table length in bytes + * language 4 USHORT Mac language code + * + * segCountX2 6 USHORT 2*NUM_SEGS + * searchRange 8 USHORT 2*(1 << LOG_SEGS) + * entrySelector 10 USHORT LOG_SEGS + * rangeShift 12 USHORT segCountX2 - searchRange + * + * endCount 14 USHORT[NUM_SEGS] end charcode for each + * segment. last is 0xFFFF + * + * pad 14+NUM_SEGS*2 USHORT padding + * + * startCount 16+NUM_SEGS*2 USHORT[NUM_SEGS] first charcode for each + * segment + * + * idDelta 16+NUM_SEGS*4 SHORT[NUM_SEGS] delta for each segment + * + * idOffset 16+NUM_SEGS*6 SHORT[NUM_SEGS] range offset for each + * segment. can be 0 + * + * glyphIds 16+NUM_SEGS*8 USHORT[] array og glyph id ranges + * + * + * Charcodes are modelled by a series of ordered (increasing) intervals + * called segments. Each segment has start and end codes, provided by + * the 'startCount' and 'endCount' arrays. Segments must not be over-lapping + * and the last segment should always contain the '0xFFFF' endCount. + * + * The fields 'searchRange', 'entrySelector' and 'rangeShift' are better + * ignored (they're traces of over-engineering in the TT specification) + * + * Each segment also has a signed 'delta', as well as an optional offset + * within the 'glyphIds' table. + * + * if a segment's idOffset is 0, then the glyph index corresponding to + * any charcode within the segment is obtained by adding the value of + * 'idDelta' directly to the charcode, modulo 65536 + * + * otherwise, a glyph index is taken from the glyph ids sub-array for the + * segment, and the value of 'idDelta' is added to it.. + */ + + #ifdef TT_CONFIG_CMAP_FORMAT_4 static void @@ -542,14 +542,13 @@ { FT_Byte* p = table + 2; /* skip format */ FT_UInt length = TT_NEXT_USHORT(p); - FT_Byte *ends, *starts, *offsets, *glyph_ids; + FT_Byte *ends, *starts, *offsets, *deltas, *glyph_ids; FT_UInt n, num_segs; if ( table + length > valid->limit || length < 16 ) TOO_SHORT; - p += 2; /* skip language */ - + p = table + 6; num_segs = TT_NEXT_USHORT(p); /* read segCountX2 */ if ( valid->level >= FT_VALIDATE_PARANOID ) @@ -584,12 +583,11 @@ search_range != (1 << entry_selector) ) INVALID_DATA; } - else - p += 6; - ends = p; - starts = ends + num_segs*2 + 2; - offsets = starts + num_segs*4; + ends = table + 14; + starts = table + 16 + num_segs*2; + deltas = starts + num_segs*2; + offsets = deltas + num_segs*2; glyph_ids = offsets + num_segs*2; if ( glyph_ids >= table + length ) @@ -599,7 +597,7 @@ if ( valid->level >= FT_VALIDATE_PARANOID ) { p = ends + (num_segs-1)*2; - if ( PEEK_UShort(p) != 0xFFFFU ) + if ( TT_PEEK_USHORT(p) != 0xFFFFU ) INVALID_DATA; } @@ -607,12 +605,14 @@ /* check also the offsets.. */ { FT_UInt start, end, last = 0,offset, n; + FT_Int delta; for ( n = 0; n < num_segs; n++ ) { - p = starts + n*2; start = PEEK_UShort(p); - p = ends + n*2; end = PEEK_UShort(p); - p = offsets + n*2; offset = PEEK_UShort(p); + p = starts + n*2; start = TT_PEEK_USHORT(p); + p = ends + n*2; end = TT_PEEK_USHORT(p); + p = deltas + n*2; delta = TT_PEEK_SHORT(p); + p = offsets + n*2; offset = TT_PEEK_USHORT(p); if ( end > start ) INVALID_DATA; @@ -625,10 +625,26 @@ p += offset; /* start of glyph id array */ /* check that we point within the glyph ids table only */ - if ( p < glyph_ids || p + (end - start + 1) > table + length ) + if ( p < glyph_ids || p + (end - start + 1)*2 > table + length ) INVALID_DATA; - /* XXXX: check glyph ids !! */ + /* check glyph indices within the segment range */ + if ( valid->level >= FT_VALIDATE_TIGHT ) + { + FT_UInt index; + + for ( ; start < end; ) + { + index = NEXT_USHORT(p); + if ( index != 0 ) + { + index = (FT_UInt)(index + delta) & 0xFFFFU; + + if ( index >= valid->num_glyphs ) + INVALID_GLYPH_ID; + } + } + } } last = end; } @@ -651,7 +667,7 @@ FT_UInt code = (FT_UInt)char_code; p = table + 6; - num_segs2 = PEEK_UShort(p); + num_segs2 = TT_PEEK_USHORT(p) & -2; /* be paranoid !! */ p = table + 14; /* ends table */ q = table + 16 + num_segs2; /* starts table */ @@ -668,13 +684,13 @@ { index = (FT_UInt)( char_code - start ); - p = q + num_segs2 - 2; delta = PEEK_Short(p); - p += num_segs2; offset = PEEK_UShort(p); + p = q + num_segs2 - 2; delta = TT_PEEK_SHORT(p); + p += num_segs2; offset = TT_PEEK_USHORT(p); if ( offset != 0 ) { p += offset + 2*index; - index = PEEK_UShort(p); + index = TT_PEEK_USHORT(p); } if ( index != 0 ) @@ -703,7 +719,7 @@ code = (FT_UInt)char_code; p = table + 6; - num_segs2 = PEEK_UShort(p) & -2; /* ensure even-ness */ + num_segs2 = TT_PEEK_USHORT(p) & -2; /* ensure even-ness */ for (;;) { @@ -723,8 +739,8 @@ if ( code <= end ) { - p = q + num_segs2 - 2; delta = PEEK_Short(p); - p += num_segs2; offset = PEEK_UShort(p); + p = q + num_segs2 - 2; delta = TT_PEEK_SHORT(p); + p += num_segs2; offset = TT_PEEK_USHORT(p); if ( offset != 0 ) { @@ -785,20 +801,41 @@ /************************************************************************/ /************************************************************************/ + /************************************************************************* + * + * TABLE OVERVIEW: + * --------------- + * + * NAME OFFSET TYPE DESCRIPTION + * + * format 0 USHORT must be 4 + * length 2 USHORT table length in bytes + * language 4 USHORT Mac language code + * + * first 6 USHORT first segment code + * count 8 USHORT segment size in chars + * glyphIds 10 USHORT[count] glyph ids + * + * + * A very simplified segment mapping + */ + #ifdef TT_CONFIG_CMAP_FORMAT_6 static void tt_cmap6_validate( FT_Byte* table, FT_Validator valid ) { - FT_Byte* p = table + 2; + FT_Byte* p; FT_UInt length, start, count; if ( table + 10 > valid->limit ) INVALID_TOO_SHORT; + p = table + 2; length = TT_NEXT_USHORT(p); - p += 2; /* skip language */ + + p = table + 6; /* skip language */ start = TT_NEXT_USHORT(p); count = TT_NEXT_USHORT(p); @@ -833,7 +870,7 @@ if ( index < count ) { p += 2*index; - result = PEEK_UShort(p); + result = TT_PEEK_USHORT(p); } return result; } @@ -924,6 +961,28 @@ /************************************************************************/ /************************************************************************/ + /************************************************************************* + * + * TABLE OVERVIEW: + * --------------- + * + * NAME OFFSET TYPE DESCRIPTION + * + * format 0 USHORT must be 8 + * reseved 2 USHORT reserved + * length 4 ULONG length in bytes + * language 8 ULONG Mac language code + * is32 12 BYTE[8192] 32-bitness bitmap + * count 8204 ULONG number of groups + * + * this header is followed by 'count' groups of the following format: + * + * start 0 ULONG first charcode + * end 4 ULONG last charcode + * startId 8 ULONG start glyph id for + * the group + */ + #ifdef TT_CONFIG_CMAP_FORMAT_8 static void @@ -939,11 +998,11 @@ INVALID_TOO_SHORT; length = TT_NEXT_ULONG(p); - if ( table + length > valid->limit || length < 16 + 8192 ) + if ( table + length > valid->limit || length < 8208 ) INVALID_TOO_SHORT; - is32 = p + 4; /* skip language */ - p = is32 + 8192; /* skip 'is32' array */ + is32 = table + 12; + p = is32 + 8192; /* skip 'is32' array */ num_groups = TT_NEXT_ULONG(p); if ( p + num_groups*12 > valid->limit ) @@ -983,10 +1042,10 @@ { hi = (FT_UInt)(start >> 16); lo = (FT_UInt)(start & 0xFFFFU); - + if ( is32[ hi >> 3 ] & (0x80 >> (hi & 7)) == 0 ) INVALID_DATA; - + if ( is32[ lo >> 3 ] & (0x80 >> (lo & 7)) == 0 ) INVALID_DATA; } @@ -995,20 +1054,22 @@ { /* start_hi == 0, check that is32[i] is 0 for each i in */ /* the range [start..end] */ - + /* end_hi cannot be != 0 !! */ if ( end & ~0xFFFFU ) INVALID_DATA; - + for ( ; count > 0; count--, start++ ) { lo = (FT_UInt)(start & 0xFFFFU); - + if ( is32[ lo >> 3 ] & (0x80 >> (lo & 7)) != 0 ) INVALID_DATA; } } } + + last = end; } } } @@ -1019,19 +1080,19 @@ FT_ULong char_code ) { FT_UInt result = 0; - FT_Byte* p = table + 12 + 8192; + FT_Byte* p = table + 8204; FT_ULong num_groups = TT_NEXT_ULONG(p); FT_ULong n, start, end, start_id; - + for ( ; num_groups > 0; num_groups-- ) { start = TT_NEXT_ULONG(p); end = TT_NEXT_ULONG(p); start_id = TT_NEXT_ULONG(p); - + if ( char_code < start ) break; - + if ( char_code <= end ) { result = start_id + char_code - start; @@ -1049,22 +1110,22 @@ { FT_ULong result = 0; FT_UInt gindex = 0; - FT_Byte* p = table + 12 + 8192; - FT_ULong num_groups = TT_NEXT_USHORT(p); + FT_Byte* p = table + 8204; + FT_ULong num_groups = TT_NEXT_ULONG(p); FT_ULong n, start, end, start_id; - + ++char_code; - p = table + 16 + 8192; - + p = table + 8208; + for ( n = 0; n < num_groups++; n++ ) { start = TT_NEXT_ULONG(p); end = TT_NEXT_ULONG(p); start_id = TT_NEXT_ULONG(p); - + if ( char_code < start ) char_code = start; - + if ( char_code <= end ) { gindex = (FT_UInt)(char_code - start + start_id); @@ -1075,13 +1136,13 @@ } } } - + Exit: if ( agindex ) *agindex = gindex; - + return result; - } + } static const TT_Cmap_ClassRec tt_cmap8_class_rec = @@ -1101,20 +1162,37 @@ /************************************************************************/ /************************************************************************/ + /************************************************************************* + * + * TABLE OVERVIEW: + * --------------- + * + * NAME OFFSET TYPE DESCRIPTION + * + * format 0 USHORT must be 10 + * reseved 2 USHORT reserved + * length 4 ULONG length in bytes + * language 8 ULONG Mac language code + * + * start 12 ULONG first char in range + * count 16 ULONG number of chars in range + * glyphIds 20 USHORT[count] glyph indices covered + */ + #ifdef TT_CONFIG_CMAP_FORMAT_10 static void tt_cmap10_validate( FT_Byte* table, FT_Validator valid ) { - FT_Byte* p = table + 2; + FT_Byte* p = table + 4; FT_ULong length, start, count; if ( table + 20 > valid->limit ) INVALID_TOO_SHORT; - length = TT_NEXT_USHORT(p); - p += 4; /* skip language */ + length = TT_NEXT_ULONG(p); + p = table + 12; start = TT_NEXT_ULONG(p); count = TT_NEXT_ULONG(p); @@ -1148,8 +1226,8 @@ if ( index < count ) { - p += 2*index; - result = PEEK_UShort(p); + p += 2*index; + result = TT_PEEK_USHORT(p); } return result; } @@ -1168,9 +1246,6 @@ FT_ULong index; char_code++; - if ( char_code >= 0x10000U ) - goto Exit; - if ( char_code < start ) char_code = start; @@ -1213,20 +1288,108 @@ /************************************************************************/ /************************************************************************/ + /************************************************************************* + * + * TABLE OVERVIEW: + * --------------- + * + * NAME OFFSET TYPE DESCRIPTION + * + * format 0 USHORT must be 12 + * reseved 2 USHORT reserved + * length 4 ULONG length in bytes + * language 8 ULONG Mac language code + * count 12 ULONG number of groups + * 16 + * + * this header is followed by 'count' groups of the following format: + * + * start 0 ULONG first charcode + * end 4 ULONG last charcode + * startId 8 ULONG start glyph id for + * the group + */ + #ifdef TT_CONFIG_CMAP_FORMAT_12 static void tt_cmap12_validate( FT_Byte* table, FT_Validator valid ) { - } + FT_Byte* p; + FT_ULong length; + FT_ULong num_groups; + + if ( table + 16 > valid->limit ) + INVALID_TOO_SHORT; + + p = table + 4; + length = TT_NEXT_ULONG(p); + + p = table + 12; + num_groups = TT_NEXT_ULONG(p); + + if ( table + length > valid->limit || length < 16 + 12*num_groups ) + INVALID_TOO_SHORT; + + /* check groups, they must be in increasing order */ + { + FT_ULong n, start, end, start_id, count, last = 0; + + for ( n = 0; n < num_groups; n++ ) + { + FT_Bytes* q; + FT_UInt hi, lo; + + start = TT_NEXT_ULONG(p); + end = TT_NEXT_ULONG(p); + start_id = TT_NEXT_ULONG(p); + + if ( start > end ) + INVALID_DATA; + + if ( n > 0 && start <= last ) + INVALID_DATA; + + if ( valid->level >= FT_VALIDATE_TIGHT ) + { + if ( start_id + end - start >= valid->num_glyphs ) + INVALID_GLYPH_ID; + } + + last = end; + } + } + } + static FT_UInt tt_cmap12_char_index( FT_Byte* table, FT_ULong char_code ) { - } + FT_UInt result = 0; + FT_Byte* p = table + 12; + FT_ULong num_groups = TT_NEXT_ULONG(p); + FT_ULong n, start, end, start_id; + + for ( ; num_groups > 0; num_groups-- ) + { + start = TT_NEXT_ULONG(p); + end = TT_NEXT_ULONG(p); + start_id = TT_NEXT_ULONG(p); + + if ( char_code < start ) + break; + + if ( char_code <= end ) + { + result = start_id + char_code - start; + break; + } + } + return result; + } static FT_ULong @@ -1234,7 +1397,41 @@ FT_ULong char_code, FT_UInt *agindex ) { - } + FT_ULong result = 0; + FT_UInt gindex = 0; + FT_Byte* p = table + 12; + FT_ULong num_groups = TT_NEXT_ULONG(p); + FT_ULong n, start, end, start_id; + + ++char_code; + p = table + 8208; + + for ( n = 0; n < num_groups++; n++ ) + { + start = TT_NEXT_ULONG(p); + end = TT_NEXT_ULONG(p); + start_id = TT_NEXT_ULONG(p); + + if ( char_code < start ) + char_code = start; + + if ( char_code <= end ) + { + gindex = (FT_UInt)(char_code - start + start_id); + if ( gindex != 0 ) + { + result = char_code; + goto Exit; + } + } + } + + Exit: + if ( agindex ) + *agindex = gindex; + + return result; + } static const TT_Cmap_ClassRec tt_cmap12_class_rec = @@ -1246,3 +1443,4 @@ #endif /* TT_CONFIG_CMAP_FORMAT_12 */ +/* END */ diff --git a/src/type1/t1cmap.c b/src/type1/t1cmap.c new file mode 100644 index 000000000..cd956f46b --- /dev/null +++ b/src/type1/t1cmap.c @@ -0,0 +1,414 @@ +#include "t1cmap.h" + + /***************************************************************************/ + /***************************************************************************/ + /***** *****/ + /***** TYPE1 STANDARD (AND EXPERT) ENCODING CMAPS *****/ + /***** *****/ + /***************************************************************************/ + /***************************************************************************/ + + static( void ) + t1_cmap_std_init( T1_CMapStd cmap, + FT_Int is_expert ) + { + T1_Face face = (T1_Face) FT_CMAP_FACE(cmap); + PSNames_Interface* psnames = face->psnames; + + cmap->num_glyphs = face->type1.num_glyphs; + cmap->glyph_names = face->type1.glyph_names; + cmap->sid_strings = sid_strings; + cmap->code_to_sid = is_expert ? psnames->adobe_expert_encoding + : psnames->adobe_std_encoding; + + FT_ASSERT( cmap->code_to_sid != NULL ); + } + + + FT_CALLBACK_DEF( void ) + t1_cmap_std_done( T1_CMapStd cmap ) + { + cmap->num_glyphs = 0; + cmap->glyph_names = NULL; + cmap->sid_strings = NULL; + cmap->code_to_sid = NULL; + } + + + FT_CALLBACK_DEF( FT_UInt ) + t1_cmap_std_char_index( T1_CMapStd cmap, + FT_UInt32 char_code ) + { + FT_UInt result = 0; + + if ( char_code < 256 ) + { + FT_UInt code; + const char* glyph_name; + FT_Int n; + + /* conver character code to Adobe SID string */ + code = cmap->code_to_sid[ char_code ]; + glyph_name = cmap->adobe_sid_strings[ code ]; + + /* look for the corresponding glyph name */ + for ( n = 0; n < cmap->num_glyphs; n++ ) + { + const char* gname = cmap->glyph_names[n]; + + if ( gname && gname[0] == glyph_name[0] && + strcmp( gname, glyph_name ) == 0 ) + { + result = n; + break; + } + } + } + return result; + } + + + FT_CALLBACK_DEF( FT_UInt ) + t1_cmap_std_char_next( T1_CMapStd cmap, + FT_UInt32 *pchar_code ) + { + FT_UInt result = 0; + FT_UInt32 char_code = *pchar_code; + + ++char_code; + while ( char_code < 256 ) + { + result = t1_cmap_standard_char_index( cmap, char_code ); + if ( result != 0 ) + goto Exit; + + char_code++; + } + char_code = 0; + + Exit: + *pchar_code = char_code; + return result; + } + + + FT_CALLBACK_DEF( FT_Error ) + t1_cmap_standard_init( T1_CMapStd cmap ) + { + t1_cmap_std_init( cmap, 0 ); + return 0; + } + + + FT_CALLBACK_TABLE const T1_CMap_ClassRec + t1_cmap_standard_class_rec = + { + sizeof( T1_CMapStdRec ), + + t1_cmap_standard_init, + t1_cmap_std_done, + t1_cmap_std_char_index, + t1_cmap_std_char_next + }; + + + FT_LOCAL_DEF T1_CMap_Class + t1_cmap_standard_class = &t1_cmap_standard_class_rec; + + + + + + FT_CALLBACK_DEF( void ) + t1_cmap_expert_init( T1_CMapStd cmap ) + { + t1_cmap_std_init( cmap, 1 ); + return 0; + } + + FT_CALLBACK_TABLE const T1_CMap_ClassRec + t1_cmap_expert_class_rec = + { + sizeof( T1_CMapStdRec ), + + t1_cmap_expert_init, + t1_cmap_std_done, + t1_cmap_std_char_index, + t1_cmap_std_char_next + }; + + + FT_LOCAL_DEF T1_CMap_Class + t1_cmap_expert_class = &t1_cmap_expert_class_rec; + + + /***************************************************************************/ + /***************************************************************************/ + /***** *****/ + /***** TYPE1 CUSTOM ENCODING CMAP *****/ + /***** *****/ + /***************************************************************************/ + /***************************************************************************/ + + + FT_CALLBACK_DEF( FT_Error ) + t1_cmap_custom_init( T1_CMapCustom cmap ) + { + T1_Face face = (T1_Face) FT_CMAP_FACE(cmap); + T1_Encoding* encoding = face->type1.encoding; + + cmap->first = encoding->code_first; + cmap->count = (FT_UInt)(encoding->code_last - cmap->first + 1); + cmap->indices = encoding->char_index; + + FT_ASSERT( cmap->indices != NULL ); + FT_ASSERT( encoding->code_first <= encoding->code_last ); + + return 0; + } + + + FT_CALLBACK_DEF( void ) + t1_cmap_custom_done( T1_CMapCustom cmap ) + { + cmap->indices = NULL; + cmap->first = 0; + cmap->count = 0; + } + + + FT_CALLBACK_DEF( FT_UInt ) + t1_cmap_custom_char_index( T1_CMapCustom cmap, + FT_UInt32 char_code ) + { + FT_UInt result = 0; + FT_UInt32 index; + + index = (FT_UInt32)( char_code - cmap->first ); + if ( index < cmap->count ) + result = cmap->indices[ index ]; + + return result; + } + + + FT_CALLBACK_DEF( FT_UInt ) + t1_cmap_custom_char_next( T1_CMapCustion cmap, + FT_UInt32 *pchar_code ) + { + FT_UInt result = 0; + FT_UInt32 char_code = *pchar_code; + FT_UInt32 index; + + ++char_code; + + if ( char_code < cmap->first ) + char_code = cmap->first; + + index = (FT_UInt32)( char_code - cmap->first ); + while ( index < cmap->count; index++, char_code++ ) + { + result = cmap->indices[index]; + if ( result != 0 ) + goto Exit; + } + + char_code = 0; + + Exit: + *pchar_code = char_code; + return result; + } + + + FT_CALLBACK_TABLE const FT_CMap_ClassRec + t1_cmap_custom_class_rec = + { + sizeof( T1_CMapCustomRec ), + t1_cmap_custom_init, + t1_cmap_custom_done, + t1_cmap_custom_char_index, + t1_cmap_custom_char_next + }; + + + FT_LOCAL_DEF FT_CMap_Class + t1_cmap_custom_class = &t1_cmap_custom_class_rec; + + + /***************************************************************************/ + /***************************************************************************/ + /***** *****/ + /***** TYPE1 SYNTHETIC UNICODE ENCODING CMAP *****/ + /***** *****/ + /***************************************************************************/ + /***************************************************************************/ + + + FT_CALLBACK_DEF( FT_Error ) + t1_cmap_unicode_init( T1_CMapUnicode cmap ) + { + FT_Error error; + FT_UInt count; + T1_Face face = (T1_Face) FT_CMAP_FACE(cmap); + FT_Memory memory = FT_FACE_MEMORY(face); + + cmap->num_pairs = 0; + cmap->pairs = NULL; + + count = face->type1.num_glyphs; + + if ( !ALLOC_ARRAY( cmap->pairs, count, T1_CMapUniPairRec ) ) + { + FT_UInt n, new_count; + T1_CMapUniPair pair; + FT_UInt32 uni_code; + + + pair = cmap->pairs; + for ( n = 0; n < count; n++ ) + { + const char* gname = face->type1.glyph_names[n]; + + /* build unsorted pair table by matching glyph names */ + if ( gname ) + { + uni_code = PS_Unicode_Value( gname ); + + if ( uni_code != 0 ) + { + pair->unicode = uni_code; + pair->gindex = n; + pair++; + } + } + + if ( new_count == 0 ) + { + /* there are no unicode characters in here !! */ + FREE( cmap->pairs ); + error = FT_Err_Invalid_Argument; + } + else + { + /* re-allocate if the new array is much smaller than the original */ + /* one.. */ + if ( new_count != count && new_count < count/2 ) + REALLOC_ARRAY( cmap->pairs, count, new_count, T1_CMapUniPairRec ) + + /* sort the pairs table to allow efficient binary searches */ + qsort( cmap->pairs, + new_count, + sizeof(T1_CMapUniPairRec), + t1_cmap_uni_pair_compare ); + + cmap->num_pairs = new_count; + } + } + } + + return error; + } + + + FT_CALLBACK_DEF( void ) + t1_cmap_unicode_done( T1_CMapUnicode cmap ) + { + FT_Face face = FT_CMAP_FACE(cmap); + FT_Memory memory = FT_FACE_MEMORY(face); + + FREE( cmap->pairs ); + cmap->num_pairs = 0; + } + + + FT_CALLBACK_DEF( FT_UInt ) + t1_cmap_unicode_char_index( T1_CMapUnicode cmap, + FT_UInt32 char_code ) + { + FT_UInt min = 0; + FT_UInt max = cmap->num_pairs; + FT_UInt mid; + T1_CMapUniPair pair; + + while ( min < max ) + { + mid = min + (max - min)/2; + pair = cmap->pairs + mid; + + if ( pair->unicode == char_code ) + return pair->gindex; + + if ( pair->unicode < char_code ) + min = mid+1; + else + max = mid; + } + return 0; + } + + + FT_CALLBACK_DEF( FT_UInt ) + t1_cmap_unicode_char_next( T1_CMapUnicode cmap, + FT_UInt32 *pchar_code ) + { + FT_UInt32 char_code = *pchar_code + 1; + + Restart: + { + FT_UInt min = 0; + FT_UInt max = cmap->num_pairs; + FT_UInt mid; + T1_CMapUniPair pair; + + while ( min < max ) + { + mid = min + (max - min)/2; + pair = cmap->pairs + mid; + + if ( pair->unicode == char_code ) + { + result = pair->gindex; + if ( result != 0 ) + goto Exit; + + char_code++; + goto Restart; + } + + if ( pair->unicode < char_code ) + min = mid+1; + else + max = mid; + } + + /* we didn't find it, but we have a pair just above it */ + char_code = 0; + + if ( min < cmap->num_pairs ) + { + pair = cmap->num_pairs + min; + result = pair->gindex; + if ( result != 0 ) + char_code = pair->unicode; + } + } + + Exit: + *pchar_code = char_code; + return result; + } + + + FT_CALLBACK_TABLE const FT_CMap_ClassRec + t1_cmap_unicode_class_rec = + { + sizeof( T1_CMapUnicodeRec ), + t1_cmap_unicode_init, + t1_cmap_unicode_done, + t1_cmap_unicode_char_index, + t1_cmap_unicode_char_next + }; + + + \ No newline at end of file diff --git a/src/type1/t1cmap.h b/src/type1/t1cmap.h new file mode 100644 index 000000000..228505614 --- /dev/null +++ b/src/type1/t1cmap.h @@ -0,0 +1,92 @@ +#ifndef __FT_TYPE1_CMAP_H__ +#define __FT_TYPE1_CMAP_H__ + +FT_BEGIN_HEADER + + /***************************************************************************/ + /***************************************************************************/ + /***** *****/ + /***** TYPE1 STANDARD (AND EXPERT) ENCODING CMAPS *****/ + /***** *****/ + /***************************************************************************/ + /***************************************************************************/ + + typedef struct T1_CMapStrRec_* T1_CMapStd; + + typedef struct T1_CMapUnicodeRec_* T1_CMapUnicode; + + + typedef struct T1_CMapStdRec_ + { + FT_CMapRec cmap; + + const FT_UShort* charcode_to_sid; + const char* const* adobe_sid_strings; + + FT_UInt num_glyphs; + const char** glyph_names; + + + } T1_CMapStdRec; + + + FT_LOCAL( FT_CMap_Class ) t1_cmap_standard_class; + + FT_LOCAL( FT_CMap_Class ) t1_cmap_expert_class; + + /***************************************************************************/ + /***************************************************************************/ + /***** *****/ + /***** TYPE1 CUSTOM ENCODING CMAP *****/ + /***** *****/ + /***************************************************************************/ + /***************************************************************************/ + + typedef struct T1_CMapCustomRec_* T1_CMapCustom; + + typedef struct T1_CMapCustomRec_ + { + FT_CMapRec cmap; + FT_UInt first; + FT_UInt count; + FT_UInt* indices; + + } T1_CMapCustomRec; + + + FT_LOCAL( FT_CMap_Class ) t1_cmap_custom_class; + + /***************************************************************************/ + /***************************************************************************/ + /***** *****/ + /***** TYPE1 SYNTHETIC UNICODE ENCODING CMAP *****/ + /***** *****/ + /***************************************************************************/ + /***************************************************************************/ + + typedef struct T1_CMapUniPairRec_* T1_CMapUniPair; + + typedef struct T1_CMapUniPairRec_ + { + FT_UInt32 unicode; + FT_UInt gindex; + + } T1_CMapUniPairRec; + + + typedef struct T1_CMapUnicodeRec_ + { + FT_CMapRec cmap; + FT_UInt num_pairs; + T1_CMapUniPair pairs; + + } T1_CMapUnicodeRec; + + + FT_LOCAL( FT_CMap_Class ) t1_cmap_unicode_class; + + /* */ + +FT_END_HEADER + +#endif /* __FT_TYPE1_CMAP_H__ */