diff --git a/ChangeLog b/ChangeLog index cc8ba8908..28cec3e70 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2015-11-06 Philipp Knechtges + + [autofit] Don't distort (latin) glyphs too much (#46195). + + * src/autofit/aflatin.h (AF_LatinBlueRec): Add `ascender' and + `descender' fields. + + * src/autofit/aflatin.c (af_latin_metrics_init_blues): Collect + ascender and descender data for blue zones. + (af_latin_metrics_scale_dim): Reject vertical scaling values that + change the result by more than two pixels. + 2015-11-05 Werner Lemberg [sfnt] Ignore embedded bitmaps with zero size (#46379). diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c index 5645aaf04..53851e7db 100644 --- a/src/autofit/aflatin.c +++ b/src/autofit/aflatin.c @@ -293,6 +293,8 @@ const char* p = &af_blue_strings[bs->string]; FT_Pos* blue_ref; FT_Pos* blue_shoot; + FT_Pos ascender; + FT_Pos descender; #ifdef FT_DEBUG_LEVEL_TRACE @@ -344,6 +346,8 @@ num_flats = 0; num_rounds = 0; + ascender = 0; + descender = 0; while ( *p ) { @@ -405,20 +409,30 @@ if ( AF_LATIN_IS_TOP_BLUE( bs ) ) { for ( pp = first; pp <= last; pp++ ) + { if ( best_point < 0 || points[pp].y > best_y ) { best_point = pp; best_y = points[pp].y; + ascender = FT_MAX( ascender, best_y + y_offset ); } + else + descender = FT_MIN( descender, points[pp].y + y_offset ); + } } else { for ( pp = first; pp <= last; pp++ ) + { if ( best_point < 0 || points[pp].y < best_y ) { best_point = pp; best_y = points[pp].y; + descender = FT_MIN( descender, best_y + y_offset ); } + else + ascender = FT_MAX( ascender, points[pp].y + y_offset ); + } } if ( best_point != old_best_point ) @@ -791,6 +805,9 @@ } } + blue->ascender = ascender; + blue->descender = descender; + blue->flags = 0; if ( AF_LATIN_IS_TOP_BLUE( bs ) ) blue->flags |= AF_LATIN_BLUE_TOP; @@ -973,18 +990,52 @@ #endif if ( dim == AF_DIMENSION_VERT ) { - scale = FT_MulDiv( scale, fitted, scaled ); + FT_Pos max_height; + FT_Pos dist; + FT_Fixed new_scale; - FT_TRACE5(( - "af_latin_metrics_scale_dim:" - " x height alignment (style `%s'):\n" - " " - " vertical scaling changed from %.4f to %.4f (by %d%%)\n" - "\n", - af_style_names[metrics->root.style_class->style], - axis->org_scale / 65536.0, - scale / 65536.0, - ( fitted - scaled ) * 100 / scaled )); + + new_scale = FT_MulDiv( scale, fitted, scaled ); + + /* the scaling should not change the result by more than two pixels */ + max_height = metrics->units_per_em; + + for ( nn = 0; nn < Axis->blue_count; nn++ ) + { + max_height = FT_MAX( max_height, Axis->blues[nn].ascender ); + max_height = FT_MAX( max_height, -Axis->blues[nn].descender ); + } + + dist = FT_ABS( FT_MulFix( max_height, new_scale - scale ) ); + dist &= ~127; + + if ( dist == 0 ) + { + scale = new_scale; + + FT_TRACE5(( + "af_latin_metrics_scale_dim:" + " x height alignment (style `%s'):\n" + " " + " vertical scaling changed from %.4f to %.4f (by %d%%)\n" + "\n", + af_style_names[metrics->root.style_class->style], + axis->org_scale / 65536.0, + scale / 65536.0, + ( fitted - scaled ) * 100 / scaled )); + } +#ifdef FT_DEBUG_LEVEL_TRACE + else + { + FT_TRACE5(( + "af_latin_metrics_scale_dim:" + " x height alignment (style `%s'):\n" + " " + " excessive vertical scaling abandoned\n" + "\n", + af_style_names[metrics->root.style_class->style] )); + } +#endif } } } diff --git a/src/autofit/aflatin.h b/src/autofit/aflatin.h index 6855492ca..dd75ef341 100644 --- a/src/autofit/aflatin.h +++ b/src/autofit/aflatin.h @@ -74,6 +74,8 @@ FT_BEGIN_HEADER { AF_WidthRec ref; AF_WidthRec shoot; + FT_Pos ascender; + FT_Pos descender; FT_UInt flags; } AF_LatinBlueRec, *AF_LatinBlue;