diff --git a/src/autofit/afcjk.c b/src/autofit/afcjk.c index c15d343..2be157a 100644 --- a/src/autofit/afcjk.c +++ b/src/autofit/afcjk.c @@ -45,8 +45,456 @@ /*************************************************************************/ /*************************************************************************/ + /* Basically the Latin version with AF_CJKMetrics to replace AF_LatinMetrics */ + FT_LOCAL_DEF( void ) + af_cjk_metrics_init_widths( AF_CJKMetrics metrics, + FT_Face face, + FT_ULong charcode ) + { + /* scan the array of segments in each direction */ + AF_GlyphHintsRec hints[1]; + + + af_glyph_hints_init( hints, face->memory ); + + metrics->axis[AF_DIMENSION_HORZ].width_count = 0; + metrics->axis[AF_DIMENSION_VERT].width_count = 0; + + { + FT_Error error; + FT_UInt glyph_index; + int dim; + AF_CJKMetricsRec dummy[1]; + AF_Scaler scaler = &dummy->root.scaler; + + + glyph_index = FT_Get_Char_Index( face, charcode ); + if ( glyph_index == 0 ) + goto Exit; + + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); + if ( error || face->glyph->outline.n_points <= 0 ) + goto Exit; + + FT_ZERO( dummy ); + + dummy->units_per_em = metrics->units_per_em; + scaler->x_scale = scaler->y_scale = 0x10000L; + scaler->x_delta = scaler->y_delta = 0; + scaler->face = face; + scaler->render_mode = FT_RENDER_MODE_NORMAL; + scaler->flags = 0; + + af_glyph_hints_rescale( hints, (AF_ScriptMetrics)dummy ); + + error = af_glyph_hints_reload( hints, &face->glyph->outline ); + if ( error ) + goto Exit; + + for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) + { + AF_CJKAxis axis = &metrics->axis[dim]; + AF_AxisHints axhints = &hints->axis[dim]; + AF_Segment seg, limit, link; + FT_UInt num_widths = 0; + + + error = af_latin_hints_compute_segments( hints, + (AF_Dimension)dim ); + if ( error ) + goto Exit; + + af_latin_hints_link_segments( hints, + (AF_Dimension)dim ); + + seg = axhints->segments; + limit = seg + axhints->num_segments; + + for ( ; seg < limit; seg++ ) + { + link = seg->link; + + /* we only consider stem segments there! */ + if ( link && link->link == seg && link > seg ) + { + FT_Pos dist; + + + dist = seg->pos - link->pos; + if ( dist < 0 ) + dist = -dist; + + if ( num_widths < AF_CJK_MAX_WIDTHS ) + axis->widths[ num_widths++ ].org = dist; + } + } + + af_sort_widths( num_widths, axis->widths ); + axis->width_count = num_widths; + } + + Exit: + for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) + { + AF_CJKAxis axis = &metrics->axis[dim]; + FT_Pos stdw; + + + stdw = ( axis->width_count > 0 ) + ? axis->widths[0].org + : AF_LATIN_CONSTANT( metrics, 50 ); + + /* let's try 20% of the smallest width */ + axis->edge_distance_threshold = stdw / 5; + axis->standard_width = stdw; + axis->extra_light = 0; + } + } + + af_glyph_hints_done( hints ); + } + + + +#define AF_CJK_MAX_TEST_CHARACTERS 32 + + + /* Every blue zone has 2 types of fill and unfill, + * Which means fill the entire square or not. + * */ + enum + { + AF_CJK_BLUE_TYPE_FILL, + AF_CJK_BLUE_TYPE_UNFILL, + AF_CJK_BLUE_TYPE_MAX + }; + + /* Put some common and representative CJK characters here. */ + static const FT_ULong af_cjk_blue_chars[AF_CJK_BLUE_MAX] + [AF_CJK_BLUE_TYPE_MAX] + [AF_CJK_MAX_TEST_CHARACTERS] = + { + { + { + 0X4ED6, 0X4EEC, 0X4F60, 0X4F86, 0X5011, 0X5230, 0X548C, 0X5730, + 0X5BF9, 0X5C0D, 0X5C31, 0X5E2D, 0X6211, 0X65F6, 0X6642, 0X6703, + 0X6765, 0X70BA, 0X80FD, 0X8230, 0X8AAA, 0X8BF4, 0X8FD9, 0X9019, + 0X9F4A /* top fill */ + }, + { + 0X519B, 0X540C, 0X5DF2, 0X613F, 0X65E2, 0X661F, 0X662F, 0X666F, + 0X6C11, 0X7167, 0X73B0, 0X73FE, 0X7406, 0X7528, 0X7F6E, 0X8981, + 0X8ECD, 0X90A3, 0X914D, 0X91CC, 0X958B, 0X96F7, 0X9732, 0X9762, + 0X987E /* top unfill */ + } + }, + { + { + 0X4E2A, 0X4E3A, 0X4EBA, 0X4ED6, 0X4EE5, 0X4EEC, 0X4F60, 0X4F86, + 0X500B, 0X5011, 0X5230, 0X548C, 0X5927, 0X5BF9, 0X5C0D, 0X5C31, + 0X6211, 0X65F6, 0X6642, 0X6709, 0X6765, 0X70BA, 0X8981, 0X8AAA, + 0X8BF4 /* bottom fill */ + }, + { + 0X4E3B, 0X4E9B, 0X56E0, 0X5B83, 0X60F3, 0X610F, 0X7406, 0X751F, + 0X7576, 0X770B, 0X7740, 0X7F6E, 0X8005, 0X81EA, 0X8457, 0X88E1, + 0X8FC7, 0X8FD8, 0X8FDB, 0X9032, 0X904E, 0X9053, 0X9084, 0X91CC, + 0X9762 /* bottom unfill */ + } + }, + { + { + 0X4E9B, 0X4EEC, 0X4F60, 0X4F86, 0X5011, 0X5230, 0X548C, 0X5730, + 0X5979, 0X5C06, 0X5C07, 0X5C31, 0X5E74, 0X5F97, 0X60C5, 0X6700, + 0X6837, 0X6A23, 0X7406, 0X80FD, 0X8AAA, 0X8BF4, 0X8FD9, 0X9019, + 0X90A /* left fill */ + }, + { + 0X5373, 0X5417, 0X5427, 0X542C, 0X5462, 0X54C1, 0X54CD, 0X55CE, + 0X5E08, 0X5E2B, 0X6536, 0X65AD, 0X65B7, 0X660E, 0X773C, 0X9593, + 0X95F4, 0X9645, 0X9648, 0X9650, 0X9664, 0X9673, 0X968F, 0X969B, + 0X96A8 /* left unfill */ + } + }, + { + { + 0X4E8B, 0X524D, 0X5B78, 0X5C06, 0X5C07, 0X60C5, 0X60F3, 0X6216, + 0X653F, 0X65AF, 0X65B0, 0X6837, 0X6A23, 0X6C11, 0X6C92, 0X6CA1, + 0X7136, 0X7279, 0X73B0, 0X73FE, 0X7403, 0X7B2C, 0X7D93, 0X8C01, + 0X8D77 /* right fill */ + }, + { + 0X4F8B, 0X5225, 0X522B, 0X5236, 0X52A8, 0X52D5, 0X5417, 0X55CE, + 0X589E, 0X6307, 0X660E, 0X671D, 0X671F, 0X6784, 0X7269, 0X786E, + 0X79CD, 0X8ABF, 0X8C03, 0X8CBB, 0X8D39, 0X90A3, 0X90FD, 0X9593, + 0X95F4 /* right unfill */ + } + } + }; + + + /* Calculate blue zones for all the CJK_BLUE_XXX's */ + static void + af_cjk_metrics_init_blues( AF_CJKMetrics metrics, + FT_Face face, + const FT_ULong blue_chars[AF_CJK_BLUE_MAX] + [AF_CJK_BLUE_TYPE_MAX] + [AF_CJK_MAX_TEST_CHARACTERS] ) + { + FT_Pos fills[AF_CJK_MAX_TEST_CHARACTERS]; + FT_Pos flats [AF_CJK_MAX_TEST_CHARACTERS]; + FT_Int num_fills; + FT_Int num_flats; + FT_Int bb; + AF_CJKBlue blue; + FT_Error error; + AF_CJKAxis axis; + FT_GlyphSlot glyph = face->glyph; + + /* we compute the blues simply by loading each character from the */ + /* 'blue_chars[blues]' string, then compute its extreme */ + /* points (depending blue zone type etc.) */ + + FT_TRACE5(( "cjk blue zones computation\n" )); + FT_TRACE5(( "------------------------------------------------\n" )); + + for ( bb = 0; bb < AF_CJK_BLUE_MAX; bb++ ) + { + FT_Int fill_type; + FT_Pos* blue_ref; + FT_Pos* blue_shoot; + + + num_fills = 0; + num_flats = 0; + for ( fill_type = 0 ; fill_type < AF_CJK_BLUE_TYPE_MAX; fill_type++) + { + const FT_ULong* p = blue_chars[bb][fill_type]; + const FT_ULong* limit = p + AF_CJK_MAX_TEST_CHARACTERS; + FT_Bool fill = ( fill_type == AF_CJK_BLUE_TYPE_FILL ); + + + FT_TRACE5(( "cjk blue %3d/%d: ", bb, fill_type )); + + + for ( ; p < limit && *p; p++ ) + { + FT_UInt glyph_index; + FT_Pos best_pos; /* same as points.y */ + FT_Int best_point, best_first, best_last; + FT_Vector* points; + + FT_TRACE5(( "0X%lX", *p )); + + /* load the character in the face -- skip unknown or empty ones */ + glyph_index = FT_Get_Char_Index( face, *p ); + if ( glyph_index == 0 ) + continue; + + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); + if ( error || glyph->outline.n_points <= 0 ) + continue; + + /* now compute min or max point indices and coordinates */ + points = glyph->outline.points; + best_point = -1; + best_pos = 0; /* make compiler happy */ + best_first = 0; /* ditto */ + best_last = 0; /* ditto */ + + { + FT_Int nn; + FT_Int first = 0; + FT_Int last = -1; + + + for ( nn = 0; nn < glyph->outline.n_contours; first = last+1, nn++ ) + { + FT_Int old_best_point = best_point; + FT_Int pp; + + + last = glyph->outline.contours[nn]; + + /* 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. */ + if ( last <= first ) + continue; + + switch (bb) + { + case AF_CJK_BLUE_TOP: + for ( pp = first; pp <= last; pp++ ) + if ( best_point < 0 || points[pp].y > best_pos ) + { + best_point = pp; + best_pos = points[pp].y; + } + break; + case AF_CJK_BLUE_BOTTOM: + for ( pp = first; pp <= last; pp++ ) + if ( best_point < 0 || points[pp].y < best_pos ) + { + best_point = pp; + best_pos = points[pp].y; + } + break; + case AF_CJK_BLUE_LEFT: + for ( pp = first; pp <= last; pp++ ) + if ( best_point < 0 || points[pp].x < best_pos ) + { + best_point = pp; + best_pos = points[pp].x; + } + break; + case AF_CJK_BLUE_RIGHT: + for ( pp = first; pp <= last; pp++ ) + if ( best_point < 0 || points[pp].x > best_pos ) + { + best_point = pp; + best_pos = points[pp].x; + } + break; + default: + ; + } + + if ( best_point != old_best_point ) + { + best_first = first; + best_last = last; + } + } + FT_TRACE5(( "%5ld, ", best_pos )); + } + + if (fill) + fills[num_fills++] = best_pos; + else + flats[num_flats++] = best_pos; + } + + FT_TRACE5(( "\n" )); + } + + if ( num_flats == 0 && num_fills == 0) + { + /* + * we couldn't find a single glyph to compute this blue zone, + * we will simply ignore it then + */ + FT_TRACE5(( "empty\n" )); + continue; + } + + /* we have computed the contents of the `fill' and `flats' tables, */ + /* now determine the reference position of the blue -- */ + /* we simply take the median value after a simple sort */ + af_sort_pos( num_flats, flats ); + af_sort_pos( num_fills, fills ); + + if ( AF_CJK_BLUE_TOP == bb || AF_CJK_BLUE_BOTTOM == bb ) + axis = &metrics->axis[AF_DIMENSION_VERT]; + else + axis = &metrics->axis[AF_DIMENSION_HORZ]; + + blue = & axis->blues[axis->blue_count]; + blue_ref = & blue->ref.org; + blue_shoot = & blue->shoot.org; + + axis->blue_count++; + if ( num_flats == 0 ) + { + *blue_ref = + *blue_shoot = fills[num_fills/2]; + } + else if ( num_fills == 0 ) + { + *blue_ref = + *blue_shoot = flats[num_flats/2]; + } + else + { + *blue_ref = fills[num_fills / 2]; + *blue_shoot = flats[num_flats / 2]; + } + + /* make sure blue_ref >= blue_shoot for top/right or + * vis vesa for bottom/left. */ + if ( *blue_shoot != *blue_ref ) + { + FT_Pos ref = *blue_ref; + FT_Pos shoot = *blue_shoot; + FT_Bool under_ref = FT_BOOL( shoot < ref ); + + + if ( (AF_CJK_BLUE_TOP == bb || AF_CJK_BLUE_RIGHT == bb ) ^ + under_ref ) + *blue_shoot = *blue_ref = ( shoot + ref ) / 2; + } + + blue->flags = 0; + if ( AF_CJK_BLUE_TOP == bb ) + blue->flags |= AF_CJK_BLUE_IS_TOP; + else if ( AF_CJK_BLUE_RIGHT == bb ) + blue->flags |= AF_CJK_BLUE_IS_RIGHT; + + FT_TRACE5(( "-- cjk ref = %ld shoot = %ld\n", *blue_ref, *blue_shoot )); + } + return; + } + + + /* Basically the Latin version with type AF_CJKMetrics for metrics. */ + FT_LOCAL_DEF( void ) + af_cjk_metrics_check_digits( AF_CJKMetrics metrics, + FT_Face face ) + { + FT_UInt i; + FT_Bool started = 0, same_width = 1; + FT_Fixed advance, old_advance = 0; + + + /* check whether all ASCII digits have the same advance width; */ + /* digit `0' is 0x30 in all supported charmaps */ + for ( i = 0x30; i <= 0x39; i++ ) + { + FT_UInt glyph_index; + + + glyph_index = FT_Get_Char_Index( face, i ); + if ( glyph_index == 0 ) + continue; + + if ( FT_Get_Advance( face, glyph_index, + FT_LOAD_NO_SCALE | + FT_LOAD_NO_HINTING | + FT_LOAD_IGNORE_TRANSFORM, + &advance ) ) + continue; + + if ( started ) + { + if ( advance != old_advance ) + { + same_width = 0; + break; + } + } + else + { + old_advance = advance; + started = 1; + } + } + + metrics->root.digits_have_same_width = same_width; + } + + FT_LOCAL_DEF( FT_Error ) - af_cjk_metrics_init( AF_LatinMetrics metrics, + af_cjk_metrics_init( AF_CJKMetrics metrics, FT_Face face ) { FT_CharMap oldmap = face->charmap; @@ -54,15 +502,13 @@ metrics->units_per_em = face->units_per_EM; - /* TODO are there blues? */ - if ( FT_Select_Charmap( face, FT_ENCODING_UNICODE ) ) face->charmap = NULL; else { - /* latin's version would suffice */ - af_latin_metrics_init_widths( metrics, face, 0x7530 ); - af_latin_metrics_check_digits( metrics, face ); + af_cjk_metrics_init_widths( metrics, face, 0x7530 ); + af_cjk_metrics_init_blues( metrics, face, af_cjk_blue_chars ); + af_cjk_metrics_check_digits( metrics, face ); } FT_Set_Charmap( face, oldmap ); @@ -72,30 +518,98 @@ static void - af_cjk_metrics_scale_dim( AF_LatinMetrics metrics, + af_cjk_metrics_scale_dim( AF_CJKMetrics metrics, AF_Scaler scaler, AF_Dimension dim ) { - AF_LatinAxis axis; + FT_Fixed scale; + FT_Pos delta; + AF_CJKAxis axis; + FT_UInt nn; axis = &metrics->axis[dim]; if ( dim == AF_DIMENSION_HORZ ) { - axis->scale = scaler->x_scale; - axis->delta = scaler->x_delta; + scale = scaler->x_scale; + delta = scaler->x_delta; } else { - axis->scale = scaler->y_scale; - axis->delta = scaler->y_delta; + scale = scaler->y_scale; + delta = scaler->y_delta; + } + + if ( axis->org_scale == scale && axis->org_delta == delta ) + return; + + axis->org_scale = scale; + axis->org_delta = delta; + + axis->scale = scale; + axis->delta = delta; + + /* scale the blue zones */ + for ( nn = 0; nn < axis->blue_count; nn++ ) + { + AF_CJKBlue blue = &axis->blues[nn]; + FT_Pos dist; + + + blue->ref.cur = FT_MulFix( blue->ref.org, scale ) + delta; + blue->ref.fit = blue->ref.cur; + blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta; + blue->shoot.fit = blue->shoot.cur; + blue->flags &= ~AF_CJK_BLUE_ACTIVE; + + /* a blue zone is only active if it is less than 3/4 pixels tall */ + dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale ); + if ( dist <= 48 && dist >= -48 ) + { + FT_Pos delta1, delta2; + + blue->ref.fit = FT_PIX_ROUND( blue->ref.cur ); + + /* shoot is under shoot for cjk */ + delta1 = FT_DivFix(blue->ref.fit, scale) - blue->shoot.org; + delta2 = delta1; + if ( delta1 < 0 ) + delta2 = -delta2; + + delta2 = FT_MulFix( delta2, scale ); + + FT_TRACE5(( "delta: %d", delta1 )); + if ( delta2 < 32 ) + delta2 = 0; + /* + else if ( delta2 < 64 ) + delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 ); + */ + else + delta2 = FT_PIX_ROUND( delta2 ); + FT_TRACE5(( "/%d\n", delta2 )); + + if ( delta1 < 0 ) + delta2 = -delta2; + + blue->shoot.fit = blue->ref.fit - delta2; + + FT_TRACE5(( ">> active cjk blue zone %c%d[%ld/%ld]: " + "ref: cur=%.2f fit=%.2f shoot: cur=%.2f fit=%.2f\n", + ( dim == AF_DIMENSION_HORZ ) ? 'H':'V', + nn, blue->ref.org, blue->shoot.org, + blue->ref.cur/64.0, blue->ref.fit/64.0, + blue->shoot.cur/64.0, blue->shoot.fit/64.0 )); + + blue->flags |= AF_CJK_BLUE_ACTIVE; + } } } FT_LOCAL_DEF( void ) - af_cjk_metrics_scale( AF_LatinMetrics metrics, + af_cjk_metrics_scale( AF_CJKMetrics metrics, AF_Scaler scaler ) { metrics->root.scaler = *scaler; @@ -329,7 +843,7 @@ AF_AxisHints axis = &hints->axis[dim]; FT_Error error = AF_Err_Ok; FT_Memory memory = hints->memory; - AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim]; + AF_CJKAxis laxis = &((AF_CJKMetrics)hints->metrics)->axis[dim]; AF_Segment segments = axis->segments; AF_Segment segment_limit = segments + axis->num_segments; @@ -601,9 +1115,94 @@ } + FT_LOCAL_DEF( void ) + af_cjk_hints_compute_blue_edges( AF_GlyphHints hints, + AF_CJKMetrics metrics, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[ dim ]; + AF_Edge edge = axis->edges; + AF_Edge edge_limit = edge + axis->num_edges; + AF_CJKAxis cjk = &metrics->axis[ dim ]; + FT_Fixed scale = cjk->scale; + FT_Pos best_dist0; /* initial threshold */ + + + /* compute the initial threshold as a fraction of the EM size */ + best_dist0 = FT_MulFix( metrics->units_per_em / 40, scale ); + + if ( best_dist0 > 64 / 2 ) /* Maximum 1/2 pixel */ + best_dist0 = 64 / 2; + + /* compute which blue zones are active, i.e. have their scaled */ + /* size < 3/4 pixels */ + + /* If the distant between an edge and a blue zone is shorter than + * best_dist0, set the blue zone for the edge. Then search for + * the blue zone with the smallest best_dist to the edge. */ + for ( ; edge < edge_limit; edge++ ) + { + FT_UInt bb; + AF_Width best_blue = NULL; + FT_Pos best_dist = best_dist0; + + + for ( bb = 0; bb < cjk->blue_count; bb++ ) + { + AF_CJKBlue blue = cjk->blues + bb; + FT_Bool is_top_right_blue, is_major_dir; + + /* skip inactive blue zones (i.e., those that are too small) */ + if ( !( blue->flags & AF_CJK_BLUE_ACTIVE ) ) + continue; + + /* if it is a top zone, check for right edges -- if it is a bottom */ + /* zone, check for left edges */ + /* */ + /* of course, that's for TrueType */ + is_top_right_blue = FT_BOOL( + ( ( blue->flags & AF_CJK_BLUE_IS_TOP ) != 0 ) || + ( ( blue->flags & AF_CJK_BLUE_IS_RIGHT ) != 0 ) ); + is_major_dir = FT_BOOL( edge->dir == axis->major_dir ); + + /* if it is a top zone, the edge must be against the major */ + /* direction; if it is a bottom zone, it must be in the major */ + /* direction */ + if ( is_top_right_blue ^ is_major_dir ) + { + FT_Pos dist; + AF_Width compare; + + + /* Compare the edge to the closest blue zone type */ + if ( FT_ABS( edge->fpos - blue->ref.org ) > + FT_ABS( edge->fpos - blue->shoot.org ) ) + compare = &blue->shoot; + else + compare = &blue->ref; + + dist = edge->fpos - compare->org; + if (dist < 0) + dist = -dist; + + dist = FT_MulFix( dist, scale ); + if ( dist < best_dist ) + { + best_dist = dist; + best_blue = compare; + } + } + } + + if ( best_blue ) + edge->blue_edge = best_blue; + } + } + + FT_LOCAL_DEF( FT_Error ) af_cjk_hints_init( AF_GlyphHints hints, - AF_LatinMetrics metrics ) + AF_CJKMetrics metrics ) { FT_Render_Mode mode; FT_UInt32 scaler_flags, other_flags; @@ -728,8 +1327,8 @@ AF_Edge_Flags base_flags, AF_Edge_Flags stem_flags ) { - AF_LatinMetrics metrics = (AF_LatinMetrics) hints->metrics; - AF_LatinAxis axis = & metrics->axis[dim]; + AF_CJKMetrics metrics = (AF_CJKMetrics) hints->metrics; + AF_CJKAxis axis = & metrics->axis[dim]; FT_Pos dist = width; FT_Int sign = 0; FT_Int vertical = ( dim == AF_DIMENSION_VERT ); @@ -1029,6 +1628,58 @@ FT_Pos last_stem_pos = 0; + /* we begin by aligning all stems relative to the blue zone */ + FT_TRACE5(( "==== cjk hinting %s edges =====\n", + dim == AF_DIMENSION_HORZ ? "vertical" : "horizontal" )); + + if ( AF_HINTS_DO_BLUES( hints ) ) + { + for ( edge = edges; edge < edge_limit; edge++ ) + { + AF_Width blue; + AF_Edge edge1, edge2; + + + if ( edge->flags & AF_EDGE_DONE ) + continue; + + blue = edge->blue_edge; + edge1 = NULL; + edge2 = edge->link; + + if ( blue ) + { + edge1 = edge; + } + else if ( edge2 && edge2->blue_edge ) + { + blue = edge2->blue_edge; + edge1 = edge2; + edge2 = edge; + } + + if ( !edge1 ) + continue; + + FT_TRACE5(( "CJKBLUE: edge %d @%d (opos=%.2f) snapped to (%.2f), " + "was (%.2f)\n", + edge1-edges, edge1->fpos, edge1->opos / 64.0, blue->fit / 64.0, + edge1->pos / 64.0 )); + + edge1->pos = blue->fit; + edge1->flags |= AF_EDGE_DONE; + + if ( edge2 && !edge2->blue_edge ) + { + af_cjk_align_linked_edge( hints, dim, edge1, edge2 ); + edge2->flags |= AF_EDGE_DONE; + } + + if ( !anchor ) + anchor = edge; + } + } + /* now we align all stem edges. */ for ( edge = edges; edge < edge_limit; edge++ ) { @@ -1063,6 +1714,15 @@ } /* now align the stem */ + /* this should not happen, but it's better to be safe */ + if ( edge2->blue_edge ) + { + FT_TRACE5(( "ASSERTION FAILED for edge %d\n", edge2-edges )); + + af_cjk_align_linked_edge( hints, dim, edge2, edge ); + edge->flags |= AF_EDGE_DONE; + continue; + } if ( edge2 < edge ) { @@ -1389,7 +2049,7 @@ FT_LOCAL_DEF( FT_Error ) af_cjk_hints_apply( AF_GlyphHints hints, FT_Outline* outline, - AF_LatinMetrics metrics ) + AF_CJKMetrics metrics ) { FT_Error error; int dim; @@ -1407,6 +2067,8 @@ error = af_cjk_hints_detect_features( hints, AF_DIMENSION_HORZ ); if ( error ) goto Exit; + + af_cjk_hints_compute_blue_edges( hints, metrics, AF_DIMENSION_HORZ ); } if ( AF_HINTS_DO_VERTICAL( hints ) ) @@ -1414,6 +2076,8 @@ error = af_cjk_hints_detect_features( hints, AF_DIMENSION_VERT ); if ( error ) goto Exit; + + af_cjk_hints_compute_blue_edges( hints, metrics, AF_DIMENSION_VERT ); } /* grid-fit the outline */ @@ -1512,7 +2176,7 @@ AF_SCRIPT_CJK, af_cjk_uniranges, - sizeof( AF_LatinMetricsRec ), + sizeof( AF_CJKMetricsRec ), (AF_Script_InitMetricsFunc) af_cjk_metrics_init, (AF_Script_ScaleMetricsFunc)af_cjk_metrics_scale, @@ -1534,7 +2198,7 @@ AF_SCRIPT_CJK, af_cjk_uniranges, - sizeof( AF_LatinMetricsRec ), + sizeof( AF_CJKMetricsRec ), (AF_Script_InitMetricsFunc) NULL, (AF_Script_ScaleMetricsFunc)NULL, diff --git a/src/autofit/afcjk.h b/src/autofit/afcjk.h index 0b20d4a..20b7f80 100644 --- a/src/autofit/afcjk.h +++ b/src/autofit/afcjk.h @@ -20,6 +20,7 @@ #define __AFCJK_H__ #include "afhints.h" +#include "aflatin.h" FT_BEGIN_HEADER @@ -29,23 +30,92 @@ FT_BEGIN_HEADER AF_DECLARE_SCRIPT_CLASS(af_cjk_script_class) + /* CJK (global) metrics management */ + + /* + * CJK glyphs tend to fill the square. So we have both verticle and + * horizontal blue zones. But some glyphs have flat bounding stroke that + * leave some space between neighbour glyphs. + */ + enum + { + AF_CJK_BLUE_TOP, + AF_CJK_BLUE_BOTTOM, + AF_CJK_BLUE_LEFT, + AF_CJK_BLUE_RIGHT, + + AF_CJK_BLUE_MAX + }; + + +#define AF_CJK_MAX_WIDTHS 16 +#define AF_CJK_MAX_BLUES AF_CJK_BLUE_MAX + + + enum + { + AF_CJK_BLUE_ACTIVE = 1 << 0, + AF_CJK_BLUE_IS_TOP = 1 << 1, + AF_CJK_BLUE_IS_RIGHT = 1 << 2, + AF_CJK_BLUE_ADJUSTMENT = 1 << 3, /* used for scale adjustment */ + /* optimization */ + AF_CJK_BLUE_FLAG_MAX + }; + + typedef struct AF_CJKBlueRec_ + { + AF_WidthRec ref; + AF_WidthRec shoot; /* undershoot */ + FT_UInt flags; + + } AF_CJKBlueRec, *AF_CJKBlue; + + typedef struct AF_CJKAxisRec_ + { + FT_Fixed scale; + FT_Pos delta; + + FT_UInt width_count; + AF_WidthRec widths[AF_CJK_MAX_WIDTHS]; + FT_Pos edge_distance_threshold; + FT_Pos standard_width; + FT_Bool extra_light; + + /* used for horizontal metrics too for CJK */ + FT_Bool control_overshoot; + FT_UInt blue_count; + AF_CJKBlueRec blues[AF_CJK_BLUE_MAX]; + + FT_Fixed org_scale; + FT_Pos org_delta; + + } AF_CJKAxisRec, *AF_CJKAxis; + + + typedef struct AF_CJKMetricsRec_ + { + AF_ScriptMetricsRec root; + FT_UInt units_per_em; + AF_CJKAxisRec axis[AF_DIMENSION_MAX]; + + } AF_CJKMetricsRec, *AF_CJKMetrics; FT_LOCAL( FT_Error ) - af_cjk_metrics_init( AF_LatinMetrics metrics, + af_cjk_metrics_init( AF_CJKMetrics metrics, FT_Face face ); FT_LOCAL( void ) - af_cjk_metrics_scale( AF_LatinMetrics metrics, + af_cjk_metrics_scale( AF_CJKMetrics metrics, AF_Scaler scaler ); FT_LOCAL( FT_Error ) af_cjk_hints_init( AF_GlyphHints hints, - AF_LatinMetrics metrics ); + AF_CJKMetrics metrics ); FT_LOCAL( FT_Error ) af_cjk_hints_apply( AF_GlyphHints hints, FT_Outline* outline, - AF_LatinMetrics metrics ); + AF_CJKMetrics metrics ); /* */ diff --git a/src/autofit/afindic.c b/src/autofit/afindic.c index 057d851..7c2f4dd 100644 --- a/src/autofit/afindic.c +++ b/src/autofit/afindic.c @@ -33,16 +33,35 @@ static FT_Error - af_indic_metrics_init( AF_LatinMetrics metrics, + af_indic_metrics_init( AF_CJKMetrics metrics, FT_Face face ) { - /* use CJK routines */ - return af_cjk_metrics_init( metrics, face ); + /* skip blue zone init in CJK routines */ + FT_CharMap oldmap = face->charmap; + + + metrics->units_per_em = face->units_per_EM; + + if ( FT_Select_Charmap( face, FT_ENCODING_UNICODE ) ) + face->charmap = NULL; + else + { + af_cjk_metrics_init_widths( metrics, face, 0x7530 ); +#if 0 + /* either need indic specific blue_chars[] or just skip blue zones. */ + af_cjk_metrics_init_blues( metrics, face, af_cjk_blue_chars ); +#endif + af_cjk_metrics_check_digits( metrics, face ); + } + + FT_Set_Charmap( face, oldmap ); + + return AF_Err_Ok; } static void - af_indic_metrics_scale( AF_LatinMetrics metrics, + af_indic_metrics_scale( AF_CJKMetrics metrics, AF_Scaler scaler ) { /* use CJK routines */ @@ -52,7 +71,7 @@ static FT_Error af_indic_hints_init( AF_GlyphHints hints, - AF_LatinMetrics metrics ) + AF_CJKMetrics metrics ) { /* use CJK routines */ return af_cjk_hints_init( hints, metrics ); @@ -62,7 +81,7 @@ static FT_Error af_indic_hints_apply( AF_GlyphHints hints, FT_Outline* outline, - AF_LatinMetrics metrics) + AF_CJKMetrics metrics) { /* use CJK routines */ return af_cjk_hints_apply( hints, outline, metrics ); @@ -98,7 +117,7 @@ AF_SCRIPT_INDIC, af_indic_uniranges, - sizeof( AF_LatinMetricsRec ), + sizeof( AF_CJKMetricsRec ), (AF_Script_InitMetricsFunc) af_indic_metrics_init, (AF_Script_ScaleMetricsFunc)af_indic_metrics_scale, @@ -120,7 +139,7 @@ AF_SCRIPT_INDIC, af_indic_uniranges, - sizeof( AF_LatinMetricsRec ), + sizeof( AF_CJKMetricsRec ), (AF_Script_InitMetricsFunc) NULL, (AF_Script_ScaleMetricsFunc)NULL,