From 551dd3c0a6d4d82a85797936edfe9147ca08af35 Mon Sep 17 00:00:00 2001 From: Werner Lemberg Date: Sun, 4 May 2008 13:37:38 +0000 Subject: [PATCH] First steps to fix the scaling bug of CID-keyed CFF subfonts, reported by Ding Li on 2008/03/28 on freetype-devel. * src/base/cff/cffparse.c (power_tens): New array. (cff_parse_real): Rewritten to introduce a fourth parameter which returns the `scaling' of the real number so that we have no precision loss. This is not used yet. Update all callers. (cff_parse_fixed_thousand): Replace with... (cff_parse_fixed_scaled): This function. Update all callers. --- ChangeLog | 13 +++ src/cff/cffparse.c | 232 +++++++++++++++++++++++++++++++-------------- 2 files changed, 173 insertions(+), 72 deletions(-) diff --git a/ChangeLog b/ChangeLog index b1e69f4ab..f1a978316 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2008-05-04 Werner Lemberg + + First steps to fix the scaling bug of CID-keyed CFF subfonts, + reported by Ding Li on 2008/03/28 on freetype-devel. + + * src/base/cff/cffparse.c (power_tens): New array. + (cff_parse_real): Rewritten to introduce a fourth parameter which + returns the `scaling' of the real number so that we have no + precision loss. This is not used yet. + Update all callers. + (cff_parse_fixed_thousand): Replace with... + (cff_parse_fixed_scaled): This function. Update all callers. + 2008-05-03 Werner Lemberg * src/base/ftobjs.c (FT_Load_Glyph): Call the auto-hinter without diff --git a/src/cff/cffparse.c b/src/cff/cffparse.c index af276f7f3..0d20ab509 100644 --- a/src/cff/cffparse.c +++ b/src/cff/cffparse.c @@ -136,24 +136,51 @@ } + static const FT_Long power_tens[] = + { + 1L, + 10L, + 100L, + 1000L, + 10000L, + 100000L, + 1000000L, + 10000000L, + 100000000L, + 1000000000L + }; + + /* read a real */ static FT_Fixed cff_parse_real( FT_Byte* start, FT_Byte* limit, - FT_Int power_ten ) + FT_Int power_ten, + FT_Int* scaling ) { - FT_Byte* p = start; - FT_Long num, divider, result, exponent; - FT_Int sign = 0, exponent_sign = 0; + FT_Byte* p = start; FT_UInt nib; FT_UInt phase; + FT_Long result, number, rest, exponent; + FT_Int sign = 0, exponent_sign = 0; + FT_Int exponent_add, integer_length, fraction_length; + - result = 0; - num = 0; - divider = 1; + if ( scaling ) + *scaling = 0; + + result = 0; - /* first of all, read the integer part */ + number = 0; + rest = 0; + exponent = 0; + + exponent_add = 0; + integer_length = 0; + fraction_length = 0; + + /* First of all, read the integer part. */ phase = 4; for (;;) @@ -166,7 +193,7 @@ /* Make sure we don't read past the end. */ if ( p >= limit ) - goto Bad; + goto Exit; } /* Get the nibble. */ @@ -178,10 +205,20 @@ else if ( nib > 9 ) break; else - result = result * 10 + nib; + { + /* Increase exponent if we can't add the digit. */ + if ( number >= 0xCCCCCCCL ) + exponent_add++; + /* Skip leading zeros. */ + else if ( nib || number ) + { + integer_length++; + number = number * 10 + nib; + } + } } - /* read decimal part, if any */ + /* Read fraction part, if any. */ if ( nib == 0xa ) for (;;) { @@ -193,7 +230,7 @@ /* Make sure we don't read past the end. */ if ( p >= limit ) - goto Bad; + goto Exit; } /* Get the nibble. */ @@ -202,24 +239,18 @@ if ( nib >= 10 ) break; - /* Increase precision if the integer part is zero */ - /* and we have to scale the real number. */ - if ( !result && power_ten ) - { - power_ten--; - num = num * 10 + nib; - } - else + /* Skip leading zeros if possible. */ + if ( !nib && !number ) + exponent_add--; + /* Only add digit if we don't overflow. */ + else if ( number < 0xCCCCCCCL ) { - if ( divider < 10000000L ) - { - num = num * 10 + nib; - divider *= 10; - } + fraction_length++; + number = number * 10 + nib; } } - /* read exponent, if any */ + /* Read exponent, if any. */ if ( nib == 12 ) { exponent_sign = 1; @@ -228,19 +259,17 @@ if ( nib == 11 ) { - exponent = 0; - for (;;) { - /* If we entered this iteration with phase == 4, we need */ - /* to read a new byte. */ + /* If we entered this iteration with phase == 4, */ + /* we need to read a new byte. */ if ( phase ) { p++; /* Make sure we don't read past the end. */ if ( p >= limit ) - goto Bad; + goto Exit; } /* Get the nibble. */ @@ -250,37 +279,98 @@ break; exponent = exponent * 10 + nib; + + /* Arbitrarily limit exponent. */ + if ( exponent > 1000 ) + goto Exit; } if ( exponent_sign ) exponent = -exponent; - - power_ten += (FT_Int)exponent; } - /* Move the integer part into the higher 16 bits. */ - result <<= 16; - - /* Place the decimal part into the lower 16 bits. */ - if ( num ) - result |= FT_DivFix( num, divider ); + /* We don't check `power_ten' and `exponent_add'. */ + exponent += power_ten + exponent_add; - /* apply power of 10 if needed */ - if ( power_ten > 0 ) + if ( scaling ) { - divider = 10; /* actually, this will be used as multiplier here */ - while ( --power_ten > 0 ) - divider = divider * 10; + /* Only use `fraction_length'. */ + fraction_length += integer_length; + exponent += integer_length; + + if ( fraction_length <= 5 ) + { + if ( number > 0x7FFFL ) + { + result = FT_DivFix( number, 10 ); + *scaling = exponent - fraction_length + 1; + } + else + { + if ( exponent > 0 ) + { + FT_Int new_fraction_length, shift; + - result = FT_MulFix( divider << 16, result ); + /* Make `scaling' as small as possible. */ + new_fraction_length = FT_MIN( exponent, 5 ); + exponent -= new_fraction_length; + shift = new_fraction_length - fraction_length; + + number *= power_tens[shift]; + if ( number > 0x7FFFL ) + { + number /= 10; + exponent += 1; + } + } + else + exponent -= fraction_length; + + result = number << 16; + *scaling = exponent; + } + } + else + { + if ( ( number / power_tens[fraction_length - 5] ) > 0x7FFFL ) + { + result = FT_DivFix( number, power_tens[fraction_length - 4] ); + *scaling = exponent - 4; + } + else + { + result = FT_DivFix( number, power_tens[fraction_length - 5] ); + *scaling = exponent - 5; + } + } } - else if ( power_ten < 0 ) + else { - divider = 10; - while ( ++power_ten < 0 ) - divider = divider * 10; + integer_length += exponent; + fraction_length -= exponent; + + /* Check for overflow and underflow. */ + if ( FT_ABS( integer_length ) > 5 ) + goto Exit; - result = FT_DivFix( result, divider << 16 ); + /* Convert into 16.16 format. */ + if ( fraction_length > 0 ) + { + if ( ( number / power_tens[fraction_length] ) > 0x7FFFL ) + goto Exit; + + result = FT_DivFix( number, power_tens[fraction_length] ); + } + else + { + number *= power_tens[-fraction_length]; + + if ( number > 0x7FFFL ) + goto Exit; + + result = number << 16; + } } if ( sign ) @@ -288,10 +378,6 @@ Exit: return result; - - Bad: - result = 0; - goto Exit; } @@ -299,8 +385,8 @@ static FT_Long cff_parse_num( FT_Byte** d ) { - return ( **d == 30 ? ( cff_parse_real ( d[0], d[1], 0 ) >> 16 ) - : cff_parse_integer( d[0], d[1] ) ); + return **d == 30 ? ( cff_parse_real( d[0], d[1], 0, NULL ) >> 16 ) + : cff_parse_integer( d[0], d[1] ); } @@ -308,20 +394,24 @@ static FT_Fixed cff_parse_fixed( FT_Byte** d ) { - return ( **d == 30 ? cff_parse_real ( d[0], d[1], 0 ) - : cff_parse_integer( d[0], d[1] ) << 16 ); + return **d == 30 ? cff_parse_real( d[0], d[1], 0, NULL ) + : cff_parse_integer( d[0], d[1] ) << 16; } + /* read a floating point number, either integer or real, */ - /* but return 1000 times the number read in. */ + /* but return `10^scaling' times the number read in */ static FT_Fixed - cff_parse_fixed_thousand( FT_Byte** d ) + cff_parse_fixed_scaled( FT_Byte** d, + FT_Int scaling ) { return **d == - 30 ? cff_parse_real ( d[0], d[1], 3 ) - : (FT_Fixed)FT_MulFix( cff_parse_integer( d[0], d[1] ) << 16, 1000 ); + 30 ? cff_parse_real( d[0], d[1], scaling, NULL ) + : (FT_Fixed)FT_MulFix( cff_parse_integer( d[0], d[1] ) << 16, + power_tens[scaling] ); } + static FT_Error cff_parse_font_matrix( CFF_Parser parser ) { @@ -330,20 +420,18 @@ FT_Vector* offset = &dict->font_offset; FT_UShort* upm = &dict->units_per_em; FT_Byte** data = parser->stack; - FT_Error error; + FT_Error error = CFF_Err_Stack_Underflow; FT_Fixed temp; - error = CFF_Err_Stack_Underflow; - if ( parser->top >= parser->stack + 6 ) { - matrix->xx = cff_parse_fixed_thousand( data++ ); - matrix->yx = cff_parse_fixed_thousand( data++ ); - matrix->xy = cff_parse_fixed_thousand( data++ ); - matrix->yy = cff_parse_fixed_thousand( data++ ); - offset->x = cff_parse_fixed_thousand( data++ ); - offset->y = cff_parse_fixed_thousand( data ); + matrix->xx = cff_parse_fixed_scaled( data++, 3 ); + matrix->yx = cff_parse_fixed_scaled( data++, 3 ); + matrix->xy = cff_parse_fixed_scaled( data++, 3 ); + matrix->yy = cff_parse_fixed_scaled( data++, 3 ); + offset->x = cff_parse_fixed_scaled( data++, 3 ); + offset->y = cff_parse_fixed_scaled( data, 3 ); temp = FT_ABS( matrix->yy ); @@ -599,7 +687,7 @@ goto Store_Number; case cff_kind_fixed_thousand: - val = cff_parse_fixed_thousand( parser->stack ); + val = cff_parse_fixed_scaled( parser->stack, 3 ); Store_Number: switch ( field->size )