static void af_cjk_hints_link_segments( AF_GlyphHints hints, AF_Dimension dim ) { AF_AxisHints axis = &hints->axis[dim]; AF_Segment segments = axis->segments; AF_Segment segment_limit = segments + axis->num_segments; AF_Direction major_dir = axis->major_dir; AF_Segment seg1, seg2; FT_Pos len_threshold; FT_Pos dist_threshold; len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 ); dist_threshold = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale : hints->y_scale; dist_threshold = FT_DivFix( 64 * 3, dist_threshold ); /* now compare each segment to the others */ for ( seg1 = segments; seg1 < segment_limit; seg1++ ) { /* the fake segments are for metrics hinting only */ if ( seg1->first == seg1->last ) continue; if ( seg1->dir != major_dir ) continue; for ( seg2 = segments; seg2 < segment_limit; seg2++ ) if ( seg2 != seg1 && seg1->dir + seg2->dir == 0 ) { FT_Pos dist = seg2->pos - seg1->pos; if ( dist < 0 ) continue; { FT_Pos min = seg1->min_coord; FT_Pos max = seg1->max_coord; FT_Pos len; if ( min < seg2->min_coord ) min = seg2->min_coord; if ( max > seg2->max_coord ) max = seg2->max_coord; len = max - min; if ( len >= len_threshold ) { if ( dist * 8 < seg1->score * 9 && ( dist * 8 < seg1->score * 7 || seg1->len < len ) ) { seg1->score = dist; seg1->len = len; seg1->link = seg2; } if ( dist * 8 < seg2->score * 9 && ( dist * 8 < seg2->score * 7 || seg2->len < len ) ) { seg2->score = dist; seg2->len = len; seg2->link = seg1; } } } } } /* * now compute the `serif' segments * * In Hanzi, some strokes are wider on one or both of the ends. * We either identify the stems on the ends as serifs or remove * the linkage, depending on the length of the stems. * */ { AF_Segment link1, link2; for ( seg1 = segments; seg1 < segment_limit; seg1++ ) { link1 = seg1->link; if ( !link1 || link1->link != seg1 || link1->pos <= seg1->pos ) continue; if ( seg1->score >= dist_threshold ) continue; for ( seg2 = segments; seg2 < segment_limit; seg2++ ) { if ( seg2->pos > seg1->pos || seg1 == seg2 ) continue; link2 = seg2->link; if ( !link2 || link2->link != seg2 || link2->pos < link1->pos ) continue; if ( seg1->pos == seg2->pos && link1->pos == link2->pos ) continue; if ( seg2->score <= seg1->score || seg1->score * 4 <= seg2->score ) continue; /* seg2 < seg1 < link1 < link2 */ if ( seg1->len >= seg2->len * 3 ) { AF_Segment seg; for ( seg = segments; seg < segment_limit; seg++ ) { AF_Segment link = seg->link; if ( link == seg2 ) { seg->link = 0; seg->serif = link1; } else if ( link == link2 ) { seg->link = 0; seg->serif = seg1; } } } else { seg1->link = link1->link = 0; break; } } } } for ( seg1 = segments; seg1 < segment_limit; seg1++ ) { seg2 = seg1->link; if ( seg2 ) { seg2->num_linked++; if ( seg2->link != seg1 ) { seg1->link = 0; if ( seg2->score < dist_threshold || seg1->score < seg2->score * 4 ) seg1->serif = seg2->link; else seg2->num_linked--; } } } }
af_latin_metrics_init_widths( AF_LatinMetrics 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_LatinMetricsRec 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, 0 ); if ( error ) goto Exit; for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) { AF_LatinAxis 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_LATIN_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_LatinAxis 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 ); }