diff options
Diffstat (limited to 'src/autofit/aflatin.c')
-rw-r--r-- | src/autofit/aflatin.c | 1038 |
1 files changed, 649 insertions, 389 deletions
diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c index 893e986..9c9f370 100644 --- a/src/autofit/aflatin.c +++ b/src/autofit/aflatin.c @@ -41,6 +41,10 @@ #define FT_COMPONENT trace_aflatin + /* needed for computation of round vs. flat segments */ +#define FLAT_THRESHOLD( x ) ( x / 14 ) + + /*************************************************************************/ /*************************************************************************/ /***** *****/ @@ -75,7 +79,6 @@ { FT_Error error; FT_ULong glyph_index; - FT_Long y_offset; int dim; AF_LatinMetricsRec dummy[1]; AF_Scaler scaler = &dummy->root.scaler; @@ -88,52 +91,63 @@ AF_ScriptClass script_class = AF_SCRIPT_CLASSES_GET [style_class->script]; - FT_UInt32 standard_char; + void* shaper_buf; + const char* p; + +#ifdef FT_DEBUG_LEVEL_TRACE + FT_ULong ch; +#endif + p = script_class->standard_charstring; + shaper_buf = af_shaper_buf_create( face ); /* - * We check more than a single standard character to catch features - * like `c2sc' (small caps from caps) that don't contain lowercase - * letters by definition, or other features that mainly operate on - * numerals. + * We check a list of standard characters to catch features like + * `c2sc' (small caps from caps) that don't contain lowercase letters + * by definition, or other features that mainly operate on numerals. + * The first match wins. */ - standard_char = script_class->standard_char1; - af_get_char_index( &metrics->root, - standard_char, - &glyph_index, - &y_offset ); - if ( !glyph_index ) + glyph_index = 0; + while ( *p ) { - if ( script_class->standard_char2 ) - { - standard_char = script_class->standard_char2; - af_get_char_index( &metrics->root, - standard_char, - &glyph_index, - &y_offset ); - if ( !glyph_index ) - { - if ( script_class->standard_char3 ) - { - standard_char = script_class->standard_char3; - af_get_char_index( &metrics->root, - standard_char, - &glyph_index, - &y_offset ); - if ( !glyph_index ) - goto Exit; - } - else - goto Exit; - } - } - else - goto Exit; + unsigned int num_idx; + +#ifdef FT_DEBUG_LEVEL_TRACE + const char* p_old; +#endif + + + while ( *p == ' ' ) + p++; + +#ifdef FT_DEBUG_LEVEL_TRACE + p_old = p; + GET_UTF8_CHAR( ch, p_old ); +#endif + + /* reject input that maps to more than a single glyph */ + p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx ); + if ( num_idx > 1 ) + continue; + + /* otherwise exit loop if we have a result */ + glyph_index = af_shaper_get_elem( &metrics->root, + shaper_buf, + 0, + NULL, + NULL ); + if ( glyph_index ) + break; } + af_shaper_buf_destroy( face, shaper_buf ); + + if ( !glyph_index ) + goto Exit; + FT_TRACE5(( "standard character: U+%04lX (glyph index %d)\n", - standard_char, glyph_index )); + ch, glyph_index )); error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); if ( error || face->glyph->outline.n_points <= 0 ) @@ -274,6 +288,10 @@ AF_Blue_Stringset bss = sc->blue_stringset; const AF_Blue_StringRec* bs = &af_blue_stringsets[bss]; + FT_Pos flat_threshold = FLAT_THRESHOLD( metrics->units_per_em ); + + void* shaper_buf; + /* we walk over the blue character strings as specified in the */ /* style's entry in the `af_blue_stringset' array */ @@ -282,11 +300,15 @@ "============================\n" "\n" )); + shaper_buf = af_shaper_buf_create( face ); + for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ ) { 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 @@ -305,6 +327,11 @@ FT_TRACE5(( "top" )); have_flag = 1; } + else if ( AF_LATIN_IS_SUB_TOP_BLUE( bs ) ) + { + FT_TRACE5(( "sub top" )); + have_flag = 1; + } if ( AF_LATIN_IS_NEUTRAL_BLUE( bs ) ) { @@ -338,394 +365,482 @@ num_flats = 0; num_rounds = 0; + ascender = 0; + descender = 0; while ( *p ) { - FT_ULong ch; FT_ULong glyph_index; FT_Long y_offset; - FT_Pos best_y; /* same as points.y */ FT_Int best_point, best_contour_first, best_contour_last; FT_Vector* points; - FT_Bool round = 0; + FT_Pos best_y_extremum; /* same as points.y */ + FT_Bool best_round = 0; - GET_UTF8_CHAR( ch, p ); + unsigned int i, num_idx; - /* load the character in the face -- skip unknown or empty ones */ - af_get_char_index( &metrics->root, ch, &glyph_index, &y_offset ); - if ( glyph_index == 0 ) - { - FT_TRACE5(( " U+%04lX unavailable\n", ch )); - continue; - } +#ifdef FT_DEBUG_LEVEL_TRACE + const char* p_old; + FT_ULong ch; +#endif + + + while ( *p == ' ' ) + p++; + +#ifdef FT_DEBUG_LEVEL_TRACE + p_old = p; + GET_UTF8_CHAR( ch, p_old ); +#endif - error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); - outline = face->glyph->outline; - /* reject glyphs that don't produce any rendering */ - if ( error || outline.n_points <= 2 ) + p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx ); + + if ( !num_idx ) { - FT_TRACE5(( " U+%04lX contains no (usable) outlines\n", ch )); + FT_TRACE5(( " U+%04lX unavailable\n", ch )); continue; } - /* now compute min or max point indices and coordinates */ - points = outline.points; - best_point = -1; - best_y = 0; /* make compiler happy */ - best_contour_first = 0; /* ditto */ - best_contour_last = 0; /* ditto */ + if ( AF_LATIN_IS_TOP_BLUE( bs ) ) + best_y_extremum = FT_INT_MIN; + else + best_y_extremum = FT_INT_MAX; + /* iterate over all glyph elements of the character cluster */ + /* and get the data of the `biggest' one */ + for ( i = 0; i < num_idx; i++ ) { - FT_Int nn; - FT_Int first = 0; - FT_Int last = -1; + FT_Pos best_y; + FT_Bool round = 0; - for ( nn = 0; nn < outline.n_contours; first = last + 1, nn++ ) + /* load the character in the face -- skip unknown or empty ones */ + glyph_index = af_shaper_get_elem( &metrics->root, + shaper_buf, + i, + NULL, + &y_offset ); + if ( glyph_index == 0 ) { - FT_Int old_best_point = best_point; - FT_Int pp; + FT_TRACE5(( " U+%04lX unavailable\n", ch )); + continue; + } + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); + outline = face->glyph->outline; + /* reject glyphs that don't produce any rendering */ + if ( error || outline.n_points <= 2 ) + { +#ifdef FT_DEBUG_LEVEL_TRACE + if ( num_idx == 1 ) + FT_TRACE5(( " U+%04lX contains no (usable) outlines\n", ch )); + else + FT_TRACE5(( " component %d of cluster starting with U+%04lX" + " contains no (usable) outlines\n", i, ch )); +#endif + continue; + } - last = outline.contours[nn]; + /* now compute min or max point indices and coordinates */ + points = outline.points; + best_point = -1; + best_y = 0; /* make compiler happy */ + best_contour_first = 0; /* ditto */ + best_contour_last = 0; /* ditto */ - /* Avoid single-point contours since they are never rasterized. */ - /* In some fonts, they correspond to mark attachment points */ - /* that are way outside of the glyph's real outline. */ - if ( last <= first ) - continue; + { + FT_Int nn; + FT_Int first = 0; + FT_Int last = -1; - if ( AF_LATIN_IS_TOP_BLUE( bs ) ) + + for ( nn = 0; nn < outline.n_contours; first = last + 1, nn++ ) { - for ( pp = first; pp <= last; pp++ ) - if ( best_point < 0 || points[pp].y > best_y ) + FT_Int old_best_point = best_point; + FT_Int pp; + + + last = outline.contours[nn]; + + /* Avoid single-point contours since they are never */ + /* rasterized. In some fonts, they correspond to mark */ + /* attachment points that are way outside of the glyph's */ + /* real outline. */ + if ( last <= first ) + continue; + + if ( AF_LATIN_IS_TOP_BLUE( bs ) || + AF_LATIN_IS_SUB_TOP_BLUE( bs ) ) + { + for ( pp = first; pp <= last; pp++ ) { - best_point = pp; - best_y = points[pp].y; + 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 ) + } + else + { + for ( pp = first; pp <= last; pp++ ) { - best_point = pp; - best_y = points[pp].y; + 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 ) - { - best_contour_first = first; - best_contour_last = last; + if ( best_point != old_best_point ) + { + best_contour_first = first; + best_contour_last = last; + } } } - } - - /* now check whether the point belongs to a straight or round */ - /* segment; we first need to find in which contour the extremum */ - /* lies, then inspect its previous and next points */ - if ( best_point >= 0 ) - { - FT_Pos best_x = points[best_point].x; - FT_Int prev, next; - FT_Int best_segment_first, best_segment_last; - FT_Int best_on_point_first, best_on_point_last; - FT_Pos dist; - - best_segment_first = best_point; - best_segment_last = best_point; - - if ( FT_CURVE_TAG( outline.tags[best_point] ) == FT_CURVE_TAG_ON ) + /* now check whether the point belongs to a straight or round */ + /* segment; we first need to find in which contour the extremum */ + /* lies, then inspect its previous and next points */ + if ( best_point >= 0 ) { - best_on_point_first = best_point; - best_on_point_last = best_point; - } - else - { - best_on_point_first = -1; - best_on_point_last = -1; - } + FT_Pos best_x = points[best_point].x; + FT_Int prev, next; + FT_Int best_segment_first, best_segment_last; + FT_Int best_on_point_first, best_on_point_last; + FT_Pos dist; - /* look for the previous and next points on the contour */ - /* that are not on the same Y coordinate, then threshold */ - /* the `closeness'... */ - prev = best_point; - next = prev; - do - { - if ( prev > best_contour_first ) - prev--; - else - prev = best_contour_last; + best_segment_first = best_point; + best_segment_last = best_point; - dist = FT_ABS( points[prev].y - best_y ); - /* accept a small distance or a small angle (both values are */ - /* heuristic; value 20 corresponds to approx. 2.9 degrees) */ - if ( dist > 5 ) - if ( FT_ABS( points[prev].x - best_x ) <= 20 * dist ) - break; + if ( FT_CURVE_TAG( outline.tags[best_point] ) == FT_CURVE_TAG_ON ) + { + best_on_point_first = best_point; + best_on_point_last = best_point; + } + else + { + best_on_point_first = -1; + best_on_point_last = -1; + } - best_segment_first = prev; + /* look for the previous and next points on the contour */ + /* that are not on the same Y coordinate, then threshold */ + /* the `closeness'... */ + prev = best_point; + next = prev; - if ( FT_CURVE_TAG( outline.tags[prev] ) == FT_CURVE_TAG_ON ) + do { - best_on_point_first = prev; - if ( best_on_point_last < 0 ) - best_on_point_last = prev; - } + if ( prev > best_contour_first ) + prev--; + else + prev = best_contour_last; - } while ( prev != best_point ); + dist = FT_ABS( points[prev].y - best_y ); + /* accept a small distance or a small angle (both values are */ + /* heuristic; value 20 corresponds to approx. 2.9 degrees) */ + if ( dist > 5 ) + if ( FT_ABS( points[prev].x - best_x ) <= 20 * dist ) + break; - do - { - if ( next < best_contour_last ) - next++; - else - next = best_contour_first; + best_segment_first = prev; - dist = FT_ABS( points[next].y - best_y ); - if ( dist > 5 ) - if ( FT_ABS( points[next].x - best_x ) <= 20 * dist ) - break; + if ( FT_CURVE_TAG( outline.tags[prev] ) == FT_CURVE_TAG_ON ) + { + best_on_point_first = prev; + if ( best_on_point_last < 0 ) + best_on_point_last = prev; + } - best_segment_last = next; + } while ( prev != best_point ); - if ( FT_CURVE_TAG( outline.tags[next] ) == FT_CURVE_TAG_ON ) + do { - best_on_point_last = next; - if ( best_on_point_first < 0 ) - best_on_point_first = next; - } + if ( next < best_contour_last ) + next++; + else + next = best_contour_first; - } while ( next != best_point ); + dist = FT_ABS( points[next].y - best_y ); + if ( dist > 5 ) + if ( FT_ABS( points[next].x - best_x ) <= 20 * dist ) + break; - 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 ) + best_segment_last = next; + + if ( FT_CURVE_TAG( outline.tags[next] ) == FT_CURVE_TAG_ON ) + { + best_on_point_last = next; + if ( best_on_point_first < 0 ) + best_on_point_first = next; + } + + } 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 height_threshold = metrics->units_per_em / 4; + FT_Pos length_threshold = metrics->units_per_em / 25; - FT_Int first; - FT_Int last; - FT_Bool hit; - /* we intentionally declare these two variables */ - /* outside of the loop since various compilers emit */ - /* incorrect warning messages otherwise, talking about */ - /* `possibly uninitialized variables' */ - FT_Int p_first = 0; /* make compiler happy */ - FT_Int p_last = 0; + dist = FT_ABS( points[best_segment_last].x - + points[best_segment_first].x ); - FT_Bool left2right; + 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; - /* compute direction */ - prev = best_point; + /* we intentionally declare these two variables */ + /* outside of the loop since various compilers emit */ + /* incorrect warning messages otherwise, talking about */ + /* `possibly uninitialized variables' */ + FT_Int p_first = 0; /* make compiler happy */ + FT_Int p_last = 0; - do - { - if ( prev > best_contour_first ) - prev--; - else - prev = best_contour_last; + FT_Bool left2right; - if ( points[prev].x != best_x ) - break; - } while ( prev != best_point ); + /* compute direction */ + prev = best_point; - /* skip glyph for the degenerate case */ - if ( prev == best_point ) - continue; + do + { + if ( prev > best_contour_first ) + prev--; + else + prev = best_contour_last; - left2right = FT_BOOL( points[prev].x < points[best_point].x ); + if ( points[prev].x != best_x ) + break; - first = best_segment_last; - last = first; - hit = 0; + } while ( prev != best_point ); - do - { - FT_Bool l2r; - FT_Pos d; + /* skip glyph for the degenerate case */ + if ( prev == best_point ) + continue; + left2right = FT_BOOL( points[prev].x < points[best_point].x ); - if ( !hit ) + first = best_segment_last; + last = first; + hit = 0; + + do { - /* no hit; adjust first point */ - first = last; + FT_Bool l2r; + FT_Pos d; - /* 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 + + if ( !hit ) { - p_first = -1; - p_last = -1; - } + /* no hit; adjust first point */ + first = last; - hit = 1; - } + /* 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; + } - if ( last < best_contour_last ) - last++; - else - last = best_contour_first; + hit = 1; + } - if ( FT_ABS( best_y - points[first].y ) > height_threshold ) - { - /* vertical distance too large */ - hit = 0; - continue; - } + if ( last < best_contour_last ) + last++; + else + last = best_contour_first; - /* 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 ) + if ( FT_ABS( best_y - points[first].y ) > height_threshold ) { + /* vertical distance too large */ hit = 0; continue; } - if ( FT_CURVE_TAG( outline.tags[last] ) == FT_CURVE_TAG_ON ) - { - p_last = last; - if ( p_first < 0 ) - p_first = last; - } + /* 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; + } - l2r = FT_BOOL( points[first].x < points[last].x ); - d = FT_ABS( points[last].x - points[first].x ); + if ( FT_CURVE_TAG( outline.tags[last] ) == FT_CURVE_TAG_ON ) + { + p_last = last; + if ( p_first < 0 ) + p_first = last; + } - if ( l2r == left2right && - d >= length_threshold ) - { - /* all constraints are met; update segment after finding */ - /* its end */ - do + l2r = FT_BOOL( points[first].x < points[last].x ); + d = FT_ABS( points[last].x - points[first].x ); + + if ( l2r == left2right && + d >= length_threshold ) { - if ( last < best_contour_last ) - last++; - else - last = best_contour_first; + /* 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 ) + { + if ( last > best_contour_first ) + last--; + else + last = best_contour_last; + break; + } - d = FT_ABS( points[last].y - points[first].y ); - if ( d > 5 ) - if ( FT_ABS( points[next].x - points[first].x ) <= - 20 * dist ) + p_last = last; + + if ( FT_CURVE_TAG( outline.tags[last] ) == + FT_CURVE_TAG_ON ) { - if ( last > best_contour_first ) - last--; - else - last = best_contour_last; - break; + p_last = last; + if ( p_first < 0 ) + p_first = last; } - p_last = last; + } while ( last != best_segment_first ); - if ( FT_CURVE_TAG( outline.tags[last] ) == - FT_CURVE_TAG_ON ) - { - p_last = last; - if ( p_first < 0 ) - p_first = last; - } + 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; - } while ( last != best_segment_first ); + break; + } - best_y = points[first].y; + } while ( last != best_segment_first ); + } + } - best_segment_first = first; - best_segment_last = last; + /* for computing blue zones, we add the y offset as returned */ + /* by the currently used OpenType feature -- for example, */ + /* superscript glyphs might be identical to subscript glyphs */ + /* with a vertical shift */ + best_y += y_offset; - best_on_point_first = p_first; - best_on_point_last = p_last; +#ifdef FT_DEBUG_LEVEL_TRACE + if ( num_idx == 1 ) + FT_TRACE5(( " U+%04lX: best_y = %5ld", ch, best_y )); + else + FT_TRACE5(( " component %d of cluster starting with U+%04lX:" + " best_y = %5ld", i, ch, best_y )); +#endif - break; - } + /* now set the `round' flag depending on the segment's kind: */ + /* */ + /* - if the horizontal distance between the first and last */ + /* `on' point is larger than a heuristic threshold */ + /* we have a flat segment */ + /* - if either the first or the last point of the segment is */ + /* an `off' point, the segment is round, otherwise it is */ + /* flat */ + if ( best_on_point_first >= 0 && + best_on_point_last >= 0 && + ( FT_ABS( points[best_on_point_last].x - + points[best_on_point_first].x ) ) > + flat_threshold ) + round = 0; + else + round = FT_BOOL( + FT_CURVE_TAG( outline.tags[best_segment_first] ) != + FT_CURVE_TAG_ON || + FT_CURVE_TAG( outline.tags[best_segment_last] ) != + FT_CURVE_TAG_ON ); - } while ( last != best_segment_first ); + if ( round && AF_LATIN_IS_NEUTRAL_BLUE( bs ) ) + { + /* only use flat segments for a neutral blue zone */ + FT_TRACE5(( " (round, skipped)\n" )); + continue; } + + FT_TRACE5(( " (%s)\n", round ? "round" : "flat" )); } - /* for computing blue zones, we add the y offset as returned */ - /* by the currently used OpenType feature -- for example, */ - /* superscript glyphs might be identical to subscript glyphs */ - /* with a vertical shift */ - best_y += y_offset; - - 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 */ - /* `on' point is larger than upem/8 (value 8 is heuristic) */ - /* we have a flat segment */ - /* - if either the first or the last point of the segment is */ - /* an `off' point, the segment is round, otherwise it is */ - /* flat */ - if ( best_on_point_first >= 0 && - best_on_point_last >= 0 && - (FT_UInt)( FT_ABS( points[best_on_point_last].x - - points[best_on_point_first].x ) ) > - metrics->units_per_em / 8 ) - round = 0; + if ( AF_LATIN_IS_TOP_BLUE( bs ) ) + { + if ( best_y > best_y_extremum ) + { + best_y_extremum = best_y; + best_round = round; + } + } else - round = FT_BOOL( - FT_CURVE_TAG( outline.tags[best_segment_first] ) != - FT_CURVE_TAG_ON || - FT_CURVE_TAG( outline.tags[best_segment_last] ) != - FT_CURVE_TAG_ON ); - - if ( round && AF_LATIN_IS_NEUTRAL_BLUE( bs ) ) { - /* only use flat segments for a neutral blue zone */ - FT_TRACE5(( " (round, skipped)\n" )); - continue; + if ( best_y < best_y_extremum ) + { + best_y_extremum = best_y; + best_round = round; + } } - FT_TRACE5(( " (%s)\n", round ? "round" : "flat" )); + } /* end for loop */ + + if ( !( best_y_extremum == FT_INT_MIN || + best_y_extremum == FT_INT_MAX ) ) + { + if ( best_round ) + rounds[num_rounds++] = best_y_extremum; + else + flats[num_flats++] = best_y_extremum; } - if ( round ) - rounds[num_rounds++] = best_y; - else - flats[num_flats++] = best_y; - } + } /* end while loop */ if ( num_flats == 0 && num_rounds == 0 ) { @@ -775,7 +890,8 @@ FT_Bool over_ref = FT_BOOL( shoot > ref ); - if ( AF_LATIN_IS_TOP_BLUE( bs ) ^ over_ref ) + if ( ( AF_LATIN_IS_TOP_BLUE( bs ) || + AF_LATIN_IS_SUB_TOP_BLUE( bs) ) ^ over_ref ) { *blue_ref = *blue_shoot = ( shoot + ref ) / 2; @@ -785,9 +901,14 @@ } } + blue->ascender = ascender; + blue->descender = descender; + blue->flags = 0; if ( AF_LATIN_IS_TOP_BLUE( bs ) ) blue->flags |= AF_LATIN_BLUE_TOP; + if ( AF_LATIN_IS_SUB_TOP_BLUE( bs ) ) + blue->flags |= AF_LATIN_BLUE_SUB_TOP; if ( AF_LATIN_IS_NEUTRAL_BLUE( bs ) ) blue->flags |= AF_LATIN_BLUE_NEUTRAL; @@ -802,7 +923,10 @@ FT_TRACE5(( " -> reference = %ld\n" " overshoot = %ld\n", *blue_ref, *blue_shoot )); - } + + } /* end for loop */ + + af_shaper_buf_destroy( face, shaper_buf ); FT_TRACE5(( "\n" )); @@ -816,27 +940,36 @@ af_latin_metrics_check_digits( AF_LatinMetrics metrics, FT_Face face ) { - FT_UInt i; FT_Bool started = 0, same_width = 1; FT_Fixed advance, old_advance = 0; + void* shaper_buf; - /* digit `0' is 0x30 in all supported charmaps */ - for ( i = 0x30; i <= 0x39; i++ ) + /* in all supported charmaps, digits have character codes 0x30-0x39 */ + const char digits[] = "0 1 2 3 4 5 6 7 8 9"; + const char* p; + + + p = digits; + shaper_buf = af_shaper_buf_create( face ); + + while ( *p ) { - FT_ULong glyph_index; - FT_Long y_offset; + FT_ULong glyph_index; + unsigned int num_idx; - af_get_char_index( &metrics->root, i, &glyph_index, &y_offset ); - if ( glyph_index == 0 ) + /* reject input that maps to more than a single glyph */ + p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx ); + if ( num_idx > 1 ) continue; - if ( FT_Get_Advance( face, glyph_index, - FT_LOAD_NO_SCALE | - FT_LOAD_NO_HINTING | - FT_LOAD_IGNORE_TRANSFORM, - &advance ) ) + glyph_index = af_shaper_get_elem( &metrics->root, + shaper_buf, + 0, + &advance, + NULL ); + if ( !glyph_index ) continue; if ( started ) @@ -854,6 +987,8 @@ } } + af_shaper_buf_destroy( face, shaper_buf ); + metrics->root.digits_have_same_width = same_width; } @@ -967,18 +1102,52 @@ #endif if ( dim == AF_DIMENSION_VERT ) { - scale = FT_MulDiv( scale, fitted, scaled ); - - 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 )); + FT_Pos max_height; + FT_Pos dist; + FT_Fixed new_scale; + + + 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 } } } @@ -1032,8 +1201,11 @@ if ( dim == AF_DIMENSION_VERT ) { - FT_TRACE5(( "blue zones (style `%s')\n", - af_style_names[metrics->root.style_class->style] )); +#ifdef FT_DEBUG_LEVEL_TRACE + if ( axis->blue_count ) + FT_TRACE5(( "blue zones (style `%s')\n", + af_style_names[metrics->root.style_class->style] )); +#endif /* scale the blue zones */ for ( nn = 0; nn < axis->blue_count; nn++ ) @@ -1106,21 +1278,63 @@ #endif blue->flags |= AF_LATIN_BLUE_ACTIVE; + } + } + + /* use sub-top blue zone only if it doesn't overlap with */ + /* another (non-sup-top) blue zone; otherwise, the */ + /* effect would be similar to a neutral blue zone, which */ + /* is not desired here */ + for ( nn = 0; nn < axis->blue_count; nn++ ) + { + AF_LatinBlue blue = &axis->blues[nn]; + FT_UInt i; + + + if ( !( blue->flags & AF_LATIN_BLUE_SUB_TOP ) ) + continue; + if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) ) + continue; - FT_TRACE5(( " reference %d: %d scaled to %.2f%s\n" - " overshoot %d: %d scaled to %.2f%s\n", - nn, - blue->ref.org, - blue->ref.fit / 64.0, - blue->flags & AF_LATIN_BLUE_ACTIVE ? "" - : " (inactive)", - nn, - blue->shoot.org, - blue->shoot.fit / 64.0, - blue->flags & AF_LATIN_BLUE_ACTIVE ? "" - : " (inactive)" )); + for ( i = 0; i < axis->blue_count; i++ ) + { + AF_LatinBlue b = &axis->blues[i]; + + + if ( b->flags & AF_LATIN_BLUE_SUB_TOP ) + continue; + if ( !( b->flags & AF_LATIN_BLUE_ACTIVE ) ) + continue; + + if ( b->ref.fit <= blue->shoot.fit && + b->shoot.fit >= blue->ref.fit ) + { + blue->flags &= ~AF_LATIN_BLUE_ACTIVE; + break; + } } } + +#ifdef FT_DEBUG_LEVEL_TRACE + for ( nn = 0; nn < axis->blue_count; nn++ ) + { + AF_LatinBlue blue = &axis->blues[nn]; + + + FT_TRACE5(( " reference %d: %d scaled to %.2f%s\n" + " overshoot %d: %d scaled to %.2f%s\n", + nn, + blue->ref.org, + blue->ref.fit / 64.0, + blue->flags & AF_LATIN_BLUE_ACTIVE ? "" + : " (inactive)", + nn, + blue->shoot.org, + blue->shoot.fit / 64.0, + blue->flags & AF_LATIN_BLUE_ACTIVE ? "" + : " (inactive)" )); + } +#endif } } @@ -1140,6 +1354,22 @@ } + /* Extract standard_width from writing system/script specific */ + /* metrics class. */ + + FT_LOCAL_DEF( void ) + af_latin_get_standard_widths( AF_LatinMetrics metrics, + FT_Pos* stdHW, + FT_Pos* stdVW ) + { + if ( stdHW ) + *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width; + + if ( stdVW ) + *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width; + } + + /*************************************************************************/ /*************************************************************************/ /***** *****/ @@ -1155,14 +1385,17 @@ af_latin_hints_compute_segments( AF_GlyphHints hints, AF_Dimension dim ) { - AF_AxisHints axis = &hints->axis[dim]; - FT_Memory memory = hints->memory; - FT_Error error = FT_Err_Ok; - AF_Segment segment = NULL; - AF_SegmentRec seg0; - AF_Point* contour = hints->contours; - AF_Point* contour_limit = contour + hints->num_contours; - AF_Direction major_dir, segment_dir; + AF_LatinMetrics metrics = (AF_LatinMetrics)hints->metrics; + AF_AxisHints axis = &hints->axis[dim]; + FT_Memory memory = hints->memory; + FT_Error error = FT_Err_Ok; + AF_Segment segment = NULL; + AF_SegmentRec seg0; + AF_Point* contour = hints->contours; + AF_Point* contour_limit = contour + hints->num_contours; + AF_Direction major_dir, segment_dir; + + FT_Pos flat_threshold = FLAT_THRESHOLD( metrics->units_per_em ); FT_ZERO( &seg0 ); @@ -1203,11 +1436,13 @@ /* do each contour separately */ for ( ; contour < contour_limit; contour++ ) { - AF_Point point = contour[0]; - AF_Point last = point->prev; - int on_edge = 0; - FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */ - FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */ + AF_Point point = contour[0]; + AF_Point last = point->prev; + int on_edge = 0; + FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */ + FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */ + FT_Pos min_on_pos = 32000; + FT_Pos max_on_pos = -32000; FT_Bool passed; @@ -1249,6 +1484,16 @@ if ( u > max_pos ) max_pos = u; + /* get minimum and maximum coordinate of on points */ + if ( !( point->flags & AF_FLAG_CONTROL ) ) + { + v = point->v; + if ( v < min_on_pos ) + min_on_pos = v; + if ( v > max_on_pos ) + max_on_pos = v; + } + if ( point->out_dir != segment_dir || point == last ) { /* we are just leaving an edge; record a new segment! */ @@ -1256,9 +1501,10 @@ segment->pos = (FT_Short)( ( min_pos + max_pos ) >> 1 ); /* a segment is round if either its first or last point */ - /* is a control point */ - if ( ( segment->first->flags | point->flags ) & - AF_FLAG_CONTROL ) + /* is a control point, and the length of the on points */ + /* inbetween doesn't exceed a heuristic limit */ + if ( ( segment->first->flags | point->flags ) & AF_FLAG_CONTROL && + ( max_on_pos - min_on_pos ) < flat_threshold ) segment->flags |= AF_EDGE_ROUND; /* compute segment size */ @@ -1301,10 +1547,19 @@ /* clear all segment fields */ segment[0] = seg0; - segment->dir = (FT_Char)segment_dir; + segment->dir = (FT_Char)segment_dir; + segment->first = point; + segment->last = point; + min_pos = max_pos = point->u; - segment->first = point; - segment->last = point; + + if ( point->flags & AF_FLAG_CONTROL ) + { + min_on_pos = 32000; + max_on_pos = -32000; + } + else + min_on_pos = max_on_pos = point->v; on_edge = 1; } @@ -1872,7 +2127,8 @@ /* the major direction) -- this assumes the TrueType convention */ /* for the orientation of contours */ is_top_blue = - (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_TOP ) != 0 ); + (FT_Byte)( ( blue->flags & ( AF_LATIN_BLUE_TOP | + AF_LATIN_BLUE_SUB_TOP ) ) != 0 ); is_neutral_blue = (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_NEUTRAL ) != 0); is_major_dir = @@ -2805,7 +3061,8 @@ /* Apply the complete hinting algorithm to a latin glyph. */ static FT_Error - af_latin_hints_apply( AF_GlyphHints hints, + af_latin_hints_apply( FT_UInt glyph_index, + AF_GlyphHints hints, FT_Outline* outline, AF_LatinMetrics metrics ) { @@ -2847,7 +3104,9 @@ if ( error ) goto Exit; - af_latin_hints_compute_blue_edges( hints, metrics ); + /* apply blue zones to base characters only */ + if ( !( metrics->root.globals->glyph_styles[glyph_index] & AF_NONBASE ) ) + af_latin_hints_compute_blue_edges( hints, metrics ); } /* grid-fit the outline */ @@ -2907,6 +3166,7 @@ (AF_WritingSystem_InitMetricsFunc) af_latin_metrics_init, (AF_WritingSystem_ScaleMetricsFunc)af_latin_metrics_scale, (AF_WritingSystem_DoneMetricsFunc) NULL, + (AF_WritingSystem_GetStdWidthsFunc)af_latin_get_standard_widths, (AF_WritingSystem_InitHintsFunc) af_latin_hints_init, (AF_WritingSystem_ApplyHintsFunc) af_latin_hints_apply |