diff --git a/ChangeLog b/ChangeLog index 3ec7f0957..79d1e37bd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2013-09-11 Werner Lemberg + + [autofit] Improve Hebrew rendering. + + This change introduces a new blue zone property + `AF_BLUE_PROPERTY_LATIN_LONG' to make the auto-hinter ignore short + top segments. + + * src/autofit/afblue.dat: Fix Hebrew blue strings. + Use AF_BLUE_PROPERTY_LATIN_LONG for AF_BLUE_STRING_HEBREW_TOP. + + * src/autofit/afblue.hin (AF_BLUE_PROPERTY_LATIN_LONG): New macro. + + * src/autofit/afblue.c, src/autofit/afblue.h: Updated. + + * src/autofit/aflatin.c (af_latin_metrics_init_blues): Handle + `AF_LATIN_IS_LONG_BLUE'. + + * src/autofit/aflatin.h (AF_LATIN_IS_LONG_BLUE): New macro. + 2013-08-28 Behdad Esfahbod [sfnt] Fix frame access while reading WOFF table directory. diff --git a/src/autofit/afblue.c b/src/autofit/afblue.c index 0947d2af9..f025e1b06 100644 --- a/src/autofit/afblue.c +++ b/src/autofit/afblue.c @@ -36,9 +36,9 @@ '\0', 'p', 'q', 'g', 'j', 'y', /* pqgjy */ '\0', - '\xD7', '\x90', '\xD7', '\x91', '\xD7', '\x9D', '\xD7', '\xA4', '\xD7', '\xA9', '\xD7', '\x93', '\xD7', '\x92', /* אבםפשדג */ + '\xD7', '\x91', '\xD7', '\x93', '\xD7', '\x94', '\xD7', '\x97', '\xD7', '\x9A', '\xD7', '\x9B', '\xD7', '\x9D', '\xD7', '\xA1', /* בדהחךכםס */ '\0', - '\xD7', '\x90', '\xD7', '\x94', '\xD7', '\x97', '\xD7', '\x9B', '\xD7', '\x9E', '\xD7', '\xA1', /* אהחכמס */ + '\xD7', '\x91', '\xD7', '\x98', '\xD7', '\x9B', '\xD7', '\x9D', '\xD7', '\xA1', '\xD7', '\xA6', /* בטכםסצ */ '\0', '\xD7', '\xA7', '\xD7', '\x9A', '\xD7', '\x9F', '\xD7', '\xA3', '\xD7', '\xA5', /* קךןףץ */ #ifdef AF_CONFIG_OPTION_CJK @@ -103,10 +103,11 @@ { AF_BLUE_STRING_LATIN_SMALL, 0 }, { AF_BLUE_STRING_LATIN_SMALL_MINOR, 0 }, { AF_BLUE_STRING_MAX, 0 }, - { AF_BLUE_STRING_HEBREW_TOP, AF_BLUE_PROPERTY_LATIN_TOP }, - { AF_BLUE_STRING_HEBREW_BOTTOM, 0 }, - { AF_BLUE_STRING_HEBREW_DESCENDER, 0 }, - { AF_BLUE_STRING_MAX, 0 }, + { AF_BLUE_STRING_HEBREW_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_LONG }, + { AF_BLUE_STRING_HEBREW_BOTTOM, 0 }, + { AF_BLUE_STRING_HEBREW_DESCENDER, 0 }, + { AF_BLUE_STRING_MAX, 0 }, #ifdef AF_CONFIG_OPTION_CJK { AF_BLUE_STRING_CJK_TOP_FILL, AF_BLUE_PROPERTY_CJK_TOP | AF_BLUE_PROPERTY_CJK_FILL }, diff --git a/src/autofit/afblue.dat b/src/autofit/afblue.dat index 5527b2c9a..af85fc383 100644 --- a/src/autofit/afblue.dat +++ b/src/autofit/afblue.dat @@ -77,9 +77,9 @@ AF_BLUE_STRING_ENUM AF_BLUE_STRINGS_ARRAY AF_BLUE_STRING_MAX_LEN: "pqgjy" AF_BLUE_STRING_HEBREW_TOP - "אבםפשדג" + "בדהחךכםס" AF_BLUE_STRING_HEBREW_BOTTOM - "אהחכמס" + "בטכםסצ" AF_BLUE_STRING_HEBREW_DESCENDER "קךןףץ" @@ -147,10 +147,11 @@ AF_BLUE_STRINGSET_ENUM AF_BLUE_STRINGSETS_ARRAY AF_BLUE_STRINGSET_MAX_LEN: { AF_BLUE_STRING_MAX, 0 } AF_BLUE_STRINGSET_HEBR - { AF_BLUE_STRING_HEBREW_TOP, AF_BLUE_PROPERTY_LATIN_TOP } - { AF_BLUE_STRING_HEBREW_BOTTOM, 0 } - { AF_BLUE_STRING_HEBREW_DESCENDER, 0 } - { AF_BLUE_STRING_MAX, 0 } + { AF_BLUE_STRING_HEBREW_TOP, AF_BLUE_PROPERTY_LATIN_TOP | + AF_BLUE_PROPERTY_LATIN_LONG } + { AF_BLUE_STRING_HEBREW_BOTTOM, 0 } + { AF_BLUE_STRING_HEBREW_DESCENDER, 0 } + { AF_BLUE_STRING_MAX, 0 } #ifdef AF_CONFIG_OPTION_CJK diff --git a/src/autofit/afblue.h b/src/autofit/afblue.h index 67f677722..cbe46b1bc 100644 --- a/src/autofit/afblue.h +++ b/src/autofit/afblue.h @@ -80,9 +80,9 @@ FT_BEGIN_HEADER AF_BLUE_STRING_LATIN_SMALL = 26, AF_BLUE_STRING_LATIN_SMALL_MINOR = 34, AF_BLUE_STRING_HEBREW_TOP = 40, - AF_BLUE_STRING_HEBREW_BOTTOM = 55, - AF_BLUE_STRING_HEBREW_DESCENDER = 68, - af_blue_1_1 = 78, + AF_BLUE_STRING_HEBREW_BOTTOM = 57, + AF_BLUE_STRING_HEBREW_DESCENDER = 70, + af_blue_1_1 = 80, #ifdef AF_CONFIG_OPTION_CJK AF_BLUE_STRING_CJK_TOP_FILL = af_blue_1_1 + 1, AF_BLUE_STRING_CJK_TOP_UNFILL = af_blue_1_1 + 77, @@ -129,6 +129,7 @@ FT_BEGIN_HEADER /* is a safe bet. */ #define AF_BLUE_PROPERTY_LATIN_TOP ( 1 << 0 ) #define AF_BLUE_PROPERTY_LATIN_SMALL_TOP ( 1 << 1 ) +#define AF_BLUE_PROPERTY_LATIN_LONG ( 1 << 2 ) #define AF_BLUE_PROPERTY_CJK_HORIZ ( 1 << 0 ) #define AF_BLUE_PROPERTY_CJK_TOP ( 1 << 1 ) diff --git a/src/autofit/afblue.hin b/src/autofit/afblue.hin index 424ca7e93..a9f1ae133 100644 --- a/src/autofit/afblue.hin +++ b/src/autofit/afblue.hin @@ -98,6 +98,7 @@ FT_BEGIN_HEADER /* is a safe bet. */ #define AF_BLUE_PROPERTY_LATIN_TOP ( 1 << 0 ) #define AF_BLUE_PROPERTY_LATIN_SMALL_TOP ( 1 << 1 ) +#define AF_BLUE_PROPERTY_LATIN_LONG ( 1 << 2 ) #define AF_BLUE_PROPERTY_CJK_HORIZ ( 1 << 0 ) #define AF_BLUE_PROPERTY_CJK_TOP ( 1 << 1 ) diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c index 93c9f3135..7243fb991 100644 --- a/src/autofit/aflatin.c +++ b/src/autofit/aflatin.c @@ -219,9 +219,7 @@ /* we walk over the blue character strings as specified in the */ - /* script's entry in the `af_blue_stringset' array, finding its */ - /* top-most or bottom-most points (depending on the string */ - /* properties) */ + /* script's entry in the `af_blue_stringset' array */ FT_TRACE5(( "latin blue zones computation\n" "============================\n" @@ -290,7 +288,7 @@ /* Avoid single-point contours since they are never rasterized. */ /* In some fonts, they correspond to mark attachment points */ - /* which are way outside of the glyph's real outline. */ + /* that are way outside of the glyph's real outline. */ if ( last <= first ) continue; @@ -319,8 +317,6 @@ best_contour_last = last; } } - - FT_TRACE5(( " U+%04lX: best_y = %5ld", ch, best_y )); } /* now check whether the point belongs to a straight or round */ @@ -403,6 +399,178 @@ } while ( next != best_point ); + if ( AF_LATIN_IS_LONG_BLUE( bs ) ) + { + /* If this flag is set, we have an additional constraint to */ + /* get the blue zone distance: Find a segment of the topmost */ + /* (or bottommost) contour that is longer than a heuristic */ + /* threshold. This ensures that small bumps in the outline */ + /* are ignored (for example, the `vertical serifs' found in */ + /* many Hebrew glyph designs). */ + + /* If this segment is long enough, we are done. Otherwise, */ + /* search the segment next to the extremum that is long */ + /* enough, has the same direction, and a not too large */ + /* vertical distance from the extremum. Note that the */ + /* algorithm doesn't check whether the found segment is */ + /* actually the one (vertically) nearest to the extremum. */ + + /* heuristic threshold value */ + FT_Pos length_threshold = metrics->units_per_em / 25; + + + dist = FT_ABS( points[best_segment_last].x - + points[best_segment_first].x ); + + if ( dist < length_threshold && + best_segment_last - best_segment_first + 2 <= + best_contour_last - best_contour_first ) + { + /* heuristic threshold value */ + FT_Pos height_threshold = metrics->units_per_em / 4; + + FT_Int first; + FT_Int last; + FT_Bool hit; + + FT_Bool left2right; + + + /* compute direction */ + prev = best_point; + + do + { + if ( prev > best_contour_first ) + prev--; + else + prev = best_contour_last; + + if ( points[prev].x != best_x ) + break; + + } while ( prev != best_point ); + + /* skip glyph for the degenerate case */ + if ( prev == best_point ) + continue; + + left2right = FT_BOOL( points[prev].x < points[best_point].x ); + + first = best_segment_last; + last = first; + hit = 0; + + do + { + FT_Bool l2r; + FT_Pos d; + FT_Int p_first, p_last; + + + if ( !hit ) + { + /* no hit; adjust first point */ + first = last; + + /* also adjust first and last on point */ + if ( FT_CURVE_TAG( outline.tags[first] ) == + FT_CURVE_TAG_ON ) + { + p_first = first; + p_last = first; + } + else + { + p_first = -1; + p_last = -1; + } + + hit = 1; + } + + if ( last < best_contour_last ) + last++; + else + last = best_contour_first; + + if ( FT_ABS( best_y - points[first].y ) > height_threshold ) + { + /* vertical distance too large */ + hit = 0; + continue; + } + + /* same test as above */ + dist = FT_ABS( points[last].y - points[first].y ); + if ( dist > 5 ) + if ( FT_ABS( points[last].x - points[first].x ) <= + 20 * dist ) + { + hit = 0; + continue; + } + + if ( FT_CURVE_TAG( outline.tags[last] ) == FT_CURVE_TAG_ON ) + { + p_last = last; + if ( p_first < 0 ) + p_first = last; + } + + l2r = FT_BOOL( points[first].x < points[last].x ); + d = FT_ABS( points[last].x - points[first].x ); + + if ( l2r == left2right && + d >= length_threshold ) + { + /* all constraints are met; update segment after finding */ + /* its end */ + do + { + if ( last < best_contour_last ) + last++; + else + last = best_contour_first; + + d = FT_ABS( points[last].y - points[first].y ); + if ( d > 5 ) + if ( FT_ABS( points[next].x - points[first].x ) <= + 20 * dist ) + { + last--; + break; + } + + p_last = last; + + if ( FT_CURVE_TAG( outline.tags[last] ) == + FT_CURVE_TAG_ON ) + { + p_last = last; + if ( p_first < 0 ) + p_first = last; + } + + } while ( last != best_segment_first ); + + best_y = points[first].y; + + best_segment_first = first; + best_segment_last = last; + + best_on_point_first = p_first; + best_on_point_last = p_last; + + break; + } + + } while ( last != best_segment_first ); + } + } + + FT_TRACE5(( " U+%04lX: best_y = %5ld", ch, best_y )); + /* now set the `round' flag depending on the segment's kind: */ /* */ /* - if the horizontal distance between the first and last */ diff --git a/src/autofit/aflatin.h b/src/autofit/aflatin.h index 8581fe568..7d294aad6 100644 --- a/src/autofit/aflatin.h +++ b/src/autofit/aflatin.h @@ -65,6 +65,8 @@ FT_BEGIN_HEADER ( (b)->properties & AF_BLUE_PROPERTY_LATIN_TOP ) #define AF_LATIN_IS_SMALL_TOP_BLUE( b ) \ ( (b)->properties & AF_BLUE_PROPERTY_LATIN_SMALL_TOP ) +#define AF_LATIN_IS_LONG_BLUE( b ) \ + ( (b)->properties & AF_BLUE_PROPERTY_LATIN_LONG ) #define AF_LATIN_MAX_WIDTHS 16