diff --git a/ChangeLog b/ChangeLog index c7321f6de..a1bcfb0ae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2016-09-09 Werner Lemberg + + [sfnt] Don't provide (completely) broken strike data. + + FreeType tries to sanitize strike header data; we now reject + completely broken ones. + + * include/freetype/internal/tttypes.h (TT_FaceRec): New + `sbit_strike_map' array pointer. + + * src/base/ftobjs.c (FT_Match_Size): Reject matches where either + width or height would be zero. + Add tracing message in case of error. + + * src/sfnt/sfobjs.c (sfnt_load_face): Populate `sbit_strike_map', + only using (more or less) valid strike header data for + FT_Face's `available_sizes' array. + (sfnt_done_face): Updated. + + * src/sfnt/ttsbit.c (tt_face_set_sbit_strike): Use + `sbit_strike_map'. + (tt_face_load_strike_metrics): Improve tracing. + + * src/truetype/ttdriver.c (tt_size_select): Use `sbit_strike_map'. + 2016-09-08 Werner Lemberg * Version 2.7 released. diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h index 3a50734be..10503fdd0 100644 --- a/include/freetype/freetype.h +++ b/include/freetype/freetype.h @@ -951,6 +951,10 @@ FT_BEGIN_HEADER /* strikes in the face. It is set to NULL if */ /* there is no bitmap strike. */ /* */ + /* Note that FreeType tries to sanitize the */ + /* strike data since they are sometimes sloppy */ + /* or incorrect, but this can easily fail. */ + /* */ /* num_charmaps :: The number of charmaps in the face. */ /* */ /* charmaps :: An array of the charmaps of the face. */ diff --git a/include/freetype/internal/tttypes.h b/include/freetype/internal/tttypes.h index 4ed980be8..176713258 100644 --- a/include/freetype/internal/tttypes.h +++ b/include/freetype/internal/tttypes.h @@ -1371,6 +1371,7 @@ FT_BEGIN_HEADER FT_ULong sbit_table_size; TT_SbitTableType sbit_table_type; FT_UInt sbit_num_strikes; + FT_UInt* sbit_strike_map; FT_Byte* kern_table; FT_ULong kern_table_size; diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c index 0c9e4090c..9006b598b 100644 --- a/src/base/ftobjs.c +++ b/src/base/ftobjs.c @@ -2631,6 +2631,9 @@ w = FT_PIX_ROUND( w ); h = FT_PIX_ROUND( h ); + if ( !w || !h ) + return FT_THROW( Invalid_Pixel_Size ); + for ( i = 0; i < face->num_fixed_sizes; i++ ) { FT_Bitmap_Size* bsize = face->available_sizes + i; @@ -2650,6 +2653,8 @@ } } + FT_TRACE3(( "FT_Match_Size: no matching bitmap strike\n" )); + return FT_THROW( Invalid_Pixel_Size ); } diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c index 4413bbcf2..5d5a448d8 100644 --- a/src/sfnt/sfobjs.c +++ b/src/sfnt/sfobjs.c @@ -1411,7 +1411,7 @@ * depths in the FT_Bitmap_Size record. This is a design error. */ { - FT_UInt i, count; + FT_UInt count; count = face->sbit_num_strikes; @@ -1423,6 +1423,8 @@ FT_Short avgwidth = face->os2.xAvgCharWidth; FT_Size_Metrics metrics; + FT_UInt strike_idx, bsize_idx; + if ( em_size == 0 || face->os2.version == 0xFFFFU ) { @@ -1430,31 +1432,43 @@ em_size = 1; } - if ( FT_NEW_ARRAY( root->available_sizes, count ) ) + /* to avoid invalid strike data in the `available_sizes' field */ + /* of `FT_Face', we map `available_sizes' indices to strike */ + /* indices */ + if ( FT_NEW_ARRAY( root->available_sizes, count ) || + FT_NEW_ARRAY( face->sbit_strike_map, count ) ) goto Exit; - for ( i = 0; i < count; i++ ) + bsize_idx = 0; + for ( strike_idx = 0; strike_idx < count; strike_idx++ ) { - FT_Bitmap_Size* bsize = root->available_sizes + i; + FT_Bitmap_Size* bsize = root->available_sizes + bsize_idx; - error = sfnt->load_strike_metrics( face, i, &metrics ); + error = sfnt->load_strike_metrics( face, strike_idx, &metrics ); if ( error ) goto Exit; bsize->height = (FT_Short)( metrics.height >> 6 ); - bsize->width = (FT_Short)( - ( avgwidth * metrics.x_ppem + em_size / 2 ) / em_size ); + bsize->width = (FT_Short)( + ( avgwidth * metrics.x_ppem + em_size / 2 ) / em_size ); bsize->x_ppem = metrics.x_ppem << 6; bsize->y_ppem = metrics.y_ppem << 6; /* assume 72dpi */ bsize->size = metrics.y_ppem << 6; + + /* only use strikes with valid PPEM values */ + if ( bsize->x_ppem && bsize->y_ppem ) + face->sbit_strike_map[bsize_idx++] = strike_idx; } + /* reduce array size to the actually used elements */ + (void)FT_RENEW_ARRAY( face->sbit_strike_map, count, bsize_idx ); + root->face_flags |= FT_FACE_FLAG_FIXED_SIZES; - root->num_fixed_sizes = (FT_Int)count; + root->num_fixed_sizes = (FT_Int)bsize_idx; } } @@ -1648,6 +1662,7 @@ /* freeing sbit size table */ FT_FREE( face->root.available_sizes ); + FT_FREE( face->sbit_strike_map ); face->root.num_fixed_sizes = 0; FT_FREE( face->postscript_name ); diff --git a/src/sfnt/ttsbit.c b/src/sfnt/ttsbit.c index 36c261dcc..b4902be69 100644 --- a/src/sfnt/ttsbit.c +++ b/src/sfnt/ttsbit.c @@ -251,7 +251,14 @@ FT_Size_Request req, FT_ULong* astrike_index ) { - return FT_Match_Size( (FT_Face)face, req, 0, astrike_index ); + FT_Error error; + + + error = FT_Match_Size( (FT_Face)face, req, 0, astrike_index ); + if ( !error ) + *astrike_index = face->sbit_strike_map[*astrike_index]; + + return error; } @@ -305,7 +312,8 @@ FT_TRACE2(( "tt_face_load_strike_metrics:" " sanitizing invalid ascender and descender\n" " " - " values for strike (%d, %d)\n", + " values for strike %d (%dppem, %dppem)\n", + strike_index, metrics->x_ppem, metrics->y_ppem )); /* sanitize buggy ascender and descender values */ diff --git a/src/truetype/ttdriver.c b/src/truetype/ttdriver.c index 6520c93df..8c988670c 100644 --- a/src/truetype/ttdriver.c +++ b/src/truetype/ttdriver.c @@ -273,25 +273,27 @@ static FT_Error tt_size_select( FT_Size size, - FT_ULong strike_index ) + FT_ULong bsize_index ) { TT_Face ttface = (TT_Face)size->face; TT_Size ttsize = (TT_Size)size; FT_Error error = FT_Err_Ok; + FT_UInt strike_index = ttface->sbit_strike_map[bsize_index]; + ttsize->strike_index = strike_index; if ( FT_IS_SCALABLE( size->face ) ) { /* use the scaled metrics, even when tt_size_reset fails */ - FT_Select_Metrics( size->face, strike_index ); + FT_Select_Metrics( size->face, bsize_index ); tt_size_reset( ttsize ); /* ignore return value */ } else { - SFNT_Service sfnt = (SFNT_Service) ttface->sfnt; + SFNT_Service sfnt = (SFNT_Service)ttface->sfnt; FT_Size_Metrics* metrics = &size->metrics; @@ -319,7 +321,7 @@ if ( FT_HAS_FIXED_SIZES( size->face ) ) { TT_Face ttface = (TT_Face)size->face; - SFNT_Service sfnt = (SFNT_Service) ttface->sfnt; + SFNT_Service sfnt = (SFNT_Service)ttface->sfnt; FT_ULong strike_index;