1
/***************************************************************************/
5
/* Auto-fitter hinting routines for latin script (body). */
7
/* Copyright 2003-2011 by */
8
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
10
/* This file is part of the FreeType project, and may only be used, */
11
/* modified, and distributed under the terms of the FreeType project */
12
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
13
/* this file you indicate that you have read the license and */
14
/* understand and accept it fully. */
16
/***************************************************************************/
20
#include FT_ADVANCES_H
21
#include FT_INTERNAL_DEBUG_H
27
#ifdef AF_CONFIG_OPTION_USE_WARPER
32
/*************************************************************************/
34
/* The macro FT_COMPONENT is used in trace mode. It is an implicit */
35
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
36
/* messages during execution. */
39
#define FT_COMPONENT trace_aflatin
42
/*************************************************************************/
43
/*************************************************************************/
45
/***** L A T I N G L O B A L M E T R I C S *****/
47
/*************************************************************************/
48
/*************************************************************************/
51
/* Find segments and links, compute all stem widths, and initialize */
52
/* standard width and height for the glyph with given charcode. */
55
af_latin_metrics_init_widths( AF_LatinMetrics metrics,
59
/* scan the array of segments in each direction */
60
AF_GlyphHintsRec hints[1];
63
af_glyph_hints_init( hints, face->memory );
65
metrics->axis[AF_DIMENSION_HORZ].width_count = 0;
66
metrics->axis[AF_DIMENSION_VERT].width_count = 0;
72
AF_LatinMetricsRec dummy[1];
73
AF_Scaler scaler = &dummy->root.scaler;
76
glyph_index = FT_Get_Char_Index( face, charcode );
77
if ( glyph_index == 0 )
80
error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
81
if ( error || face->glyph->outline.n_points <= 0 )
86
dummy->units_per_em = metrics->units_per_em;
88
scaler->x_scale = 0x10000L;
89
scaler->y_scale = 0x10000L;
94
scaler->render_mode = FT_RENDER_MODE_NORMAL;
97
af_glyph_hints_rescale( hints, (AF_ScriptMetrics)dummy );
99
error = af_glyph_hints_reload( hints, &face->glyph->outline );
103
for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
105
AF_LatinAxis axis = &metrics->axis[dim];
106
AF_AxisHints axhints = &hints->axis[dim];
107
AF_Segment seg, limit, link;
108
FT_UInt num_widths = 0;
111
error = af_latin_hints_compute_segments( hints,
116
af_latin_hints_link_segments( hints,
119
seg = axhints->segments;
120
limit = seg + axhints->num_segments;
122
for ( ; seg < limit; seg++ )
126
/* we only consider stem segments there! */
127
if ( link && link->link == seg && link > seg )
132
dist = seg->pos - link->pos;
136
if ( num_widths < AF_LATIN_MAX_WIDTHS )
137
axis->widths[num_widths++].org = dist;
141
af_sort_widths( num_widths, axis->widths );
142
axis->width_count = num_widths;
146
for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
148
AF_LatinAxis axis = &metrics->axis[dim];
152
stdw = ( axis->width_count > 0 )
153
? axis->widths[0].org
154
: AF_LATIN_CONSTANT( metrics, 50 );
156
/* let's try 20% of the smallest width */
157
axis->edge_distance_threshold = stdw / 5;
158
axis->standard_width = stdw;
159
axis->extra_light = 0;
163
af_glyph_hints_done( hints );
168
#define AF_LATIN_MAX_TEST_CHARACTERS 12
171
static const char af_latin_blue_chars[AF_LATIN_MAX_BLUES]
172
[AF_LATIN_MAX_TEST_CHARACTERS + 1] =
183
/* Find all blue zones. Flat segments give the reference points, */
184
/* round segments the overshoot positions. */
187
af_latin_metrics_init_blues( AF_LatinMetrics metrics,
190
FT_Pos flats [AF_LATIN_MAX_TEST_CHARACTERS];
191
FT_Pos rounds[AF_LATIN_MAX_TEST_CHARACTERS];
197
AF_LatinAxis axis = &metrics->axis[AF_DIMENSION_VERT];
198
FT_GlyphSlot glyph = face->glyph;
201
/* we compute the blues simply by loading each character from the */
202
/* `af_latin_blue_chars[blues]' string, then finding its top-most or */
203
/* bottom-most points (depending on `AF_IS_TOP_BLUE') */
205
FT_TRACE5(( "blue zones computation\n" ));
206
FT_TRACE5(( "------------------------------------------------\n" ));
208
for ( bb = 0; bb < AF_LATIN_BLUE_MAX; bb++ )
210
const char* p = af_latin_blue_chars[bb];
211
const char* limit = p + AF_LATIN_MAX_TEST_CHARACTERS;
216
FT_TRACE5(( "blue %3d: ", bb ));
221
for ( ; p < limit && *p; p++ )
224
FT_Pos best_y; /* same as points.y */
225
FT_Int best_point, best_first, best_last;
230
FT_TRACE5(( "'%c'", *p ));
232
/* load the character in the face -- skip unknown or empty ones */
233
glyph_index = FT_Get_Char_Index( face, (FT_UInt)*p );
234
if ( glyph_index == 0 )
237
error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
238
if ( error || glyph->outline.n_points <= 0 )
241
/* now compute min or max point indices and coordinates */
242
points = glyph->outline.points;
244
best_y = 0; /* make compiler happy */
245
best_first = 0; /* ditto */
246
best_last = 0; /* ditto */
255
nn < glyph->outline.n_contours;
256
first = last + 1, nn++ )
258
FT_Int old_best_point = best_point;
262
last = glyph->outline.contours[nn];
264
/* Avoid single-point contours since they are never rasterized. */
265
/* In some fonts, they correspond to mark attachment points */
266
/* which are way outside of the glyph's real outline. */
270
if ( AF_LATIN_IS_TOP_BLUE( bb ) )
272
for ( pp = first; pp <= last; pp++ )
273
if ( best_point < 0 || points[pp].y > best_y )
276
best_y = points[pp].y;
281
for ( pp = first; pp <= last; pp++ )
282
if ( best_point < 0 || points[pp].y < best_y )
285
best_y = points[pp].y;
289
if ( best_point != old_best_point )
295
FT_TRACE5(( "%5d", best_y ));
298
/* now check whether the point belongs to a straight or round */
299
/* segment; we first need to find in which contour the extremum */
300
/* lies, then inspect its previous and next points */
301
if ( best_point >= 0 )
307
/* now look for the previous and next points that are not on the */
308
/* same Y coordinate. Threshold the `closeness'... */
314
if ( prev > best_first )
319
dist = points[prev].y - best_y;
320
if ( dist < -5 || dist > 5 )
323
} while ( prev != best_point );
327
if ( next < best_last )
332
dist = points[next].y - best_y;
333
if ( dist < -5 || dist > 5 )
336
} while ( next != best_point );
338
/* now set the `round' flag depending on the segment's kind */
340
FT_CURVE_TAG( glyph->outline.tags[prev] ) != FT_CURVE_TAG_ON ||
341
FT_CURVE_TAG( glyph->outline.tags[next] ) != FT_CURVE_TAG_ON );
343
FT_TRACE5(( "%c ", round ? 'r' : 'f' ));
347
rounds[num_rounds++] = best_y;
349
flats[num_flats++] = best_y;
354
if ( num_flats == 0 && num_rounds == 0 )
357
* we couldn't find a single glyph to compute this blue zone,
358
* we will simply ignore it then
360
FT_TRACE5(( "empty\n" ));
364
/* we have computed the contents of the `rounds' and `flats' tables, */
365
/* now determine the reference and overshoot position of the blue -- */
366
/* we simply take the median value after a simple sort */
367
af_sort_pos( num_rounds, rounds );
368
af_sort_pos( num_flats, flats );
370
blue = &axis->blues[axis->blue_count];
371
blue_ref = &blue->ref.org;
372
blue_shoot = &blue->shoot.org;
376
if ( num_flats == 0 )
379
*blue_shoot = rounds[num_rounds / 2];
381
else if ( num_rounds == 0 )
384
*blue_shoot = flats[num_flats / 2];
388
*blue_ref = flats[num_flats / 2];
389
*blue_shoot = rounds[num_rounds / 2];
392
/* there are sometimes problems: if the overshoot position of top */
393
/* zones is under its reference position, or the opposite for bottom */
394
/* zones. We must thus check everything there and correct the errors */
395
if ( *blue_shoot != *blue_ref )
397
FT_Pos ref = *blue_ref;
398
FT_Pos shoot = *blue_shoot;
399
FT_Bool over_ref = FT_BOOL( shoot > ref );
402
if ( AF_LATIN_IS_TOP_BLUE( bb ) ^ over_ref )
404
*blue_shoot = ( shoot + ref ) / 2;
408
if ( AF_LATIN_IS_TOP_BLUE( bb ) )
409
blue->flags |= AF_LATIN_BLUE_TOP;
412
* The following flag is used later to adjust the y and x scales
413
* in order to optimize the pixel grid alignment of the top of small
416
if ( bb == AF_LATIN_BLUE_SMALL_TOP )
417
blue->flags |= AF_LATIN_BLUE_ADJUSTMENT;
419
FT_TRACE5(( "-- ref = %ld, shoot = %ld\n", *blue_ref, *blue_shoot ));
426
/* Check whether all ASCII digits have the same advance width. */
429
af_latin_metrics_check_digits( AF_LatinMetrics metrics,
433
FT_Bool started = 0, same_width = 1;
434
FT_Fixed advance, old_advance = 0;
437
/* digit `0' is 0x30 in all supported charmaps */
438
for ( i = 0x30; i <= 0x39; i++ )
443
glyph_index = FT_Get_Char_Index( face, i );
444
if ( glyph_index == 0 )
447
if ( FT_Get_Advance( face, glyph_index,
450
FT_LOAD_IGNORE_TRANSFORM,
456
if ( advance != old_advance )
464
old_advance = advance;
469
metrics->root.digits_have_same_width = same_width;
473
/* Initialize global metrics. */
475
FT_LOCAL_DEF( FT_Error )
476
af_latin_metrics_init( AF_LatinMetrics metrics,
479
FT_Error error = AF_Err_Ok;
480
FT_CharMap oldmap = face->charmap;
483
static const FT_Encoding latin_encodings[] =
486
FT_ENCODING_APPLE_ROMAN,
487
FT_ENCODING_ADOBE_STANDARD,
488
FT_ENCODING_ADOBE_LATIN_1,
490
FT_ENCODING_NONE /* end of list */
494
metrics->units_per_em = face->units_per_EM;
496
/* do we have a latin charmap in there? */
497
for ( ee = 0; latin_encodings[ee] != FT_ENCODING_NONE; ee++ )
499
error = FT_Select_Charmap( face, latin_encodings[ee] );
506
/* For now, compute the standard width and height from the `o'. */
507
af_latin_metrics_init_widths( metrics, face, 'o' );
508
af_latin_metrics_init_blues( metrics, face );
509
af_latin_metrics_check_digits( metrics, face );
512
FT_Set_Charmap( face, oldmap );
517
/* Adjust scaling value, then scale and shift widths */
518
/* and blue zones (if applicable) for given dimension. */
521
af_latin_metrics_scale_dim( AF_LatinMetrics metrics,
531
if ( dim == AF_DIMENSION_HORZ )
533
scale = scaler->x_scale;
534
delta = scaler->x_delta;
538
scale = scaler->y_scale;
539
delta = scaler->y_delta;
542
axis = &metrics->axis[dim];
544
if ( axis->org_scale == scale && axis->org_delta == delta )
547
axis->org_scale = scale;
548
axis->org_delta = delta;
551
* correct X and Y scale to optimize the alignment of the top of small
552
* letters to the pixel grid
555
AF_LatinAxis Axis = &metrics->axis[AF_DIMENSION_VERT];
556
AF_LatinBlue blue = NULL;
559
for ( nn = 0; nn < Axis->blue_count; nn++ )
561
if ( Axis->blues[nn].flags & AF_LATIN_BLUE_ADJUSTMENT )
563
blue = &Axis->blues[nn];
570
FT_Pos scaled = FT_MulFix( blue->shoot.org, scaler->y_scale );
571
FT_Pos fitted = ( scaled + 40 ) & ~63;
574
if ( scaled != fitted )
577
if ( dim == AF_DIMENSION_HORZ )
579
if ( fitted < scaled )
580
scale -= scale / 50; /* scale *= 0.98 */
584
if ( dim == AF_DIMENSION_VERT )
585
scale = FT_MulDiv( scale, fitted, scaled );
593
if ( dim == AF_DIMENSION_HORZ )
595
metrics->root.scaler.x_scale = scale;
596
metrics->root.scaler.x_delta = delta;
600
metrics->root.scaler.y_scale = scale;
601
metrics->root.scaler.y_delta = delta;
604
/* scale the widths */
605
for ( nn = 0; nn < axis->width_count; nn++ )
607
AF_Width width = axis->widths + nn;
610
width->cur = FT_MulFix( width->org, scale );
611
width->fit = width->cur;
614
/* an extra-light axis corresponds to a standard width that is */
615
/* smaller than 0.75 pixels */
617
(FT_Bool)( FT_MulFix( axis->standard_width, scale ) < 32 + 8 );
619
if ( dim == AF_DIMENSION_VERT )
621
/* scale the blue zones */
622
for ( nn = 0; nn < axis->blue_count; nn++ )
624
AF_LatinBlue blue = &axis->blues[nn];
628
blue->ref.cur = FT_MulFix( blue->ref.org, scale ) + delta;
629
blue->ref.fit = blue->ref.cur;
630
blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta;
631
blue->shoot.fit = blue->shoot.cur;
632
blue->flags &= ~AF_LATIN_BLUE_ACTIVE;
634
/* a blue zone is only active if it is less than 3/4 pixels tall */
635
dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale );
636
if ( dist <= 48 && dist >= -48 )
638
FT_Pos delta1, delta2;
641
delta1 = blue->shoot.org - blue->ref.org;
646
delta2 = FT_MulFix( delta2, scale );
650
else if ( delta2 < 64 )
651
delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 );
653
delta2 = FT_PIX_ROUND( delta2 );
658
blue->ref.fit = FT_PIX_ROUND( blue->ref.cur );
659
blue->shoot.fit = blue->ref.fit + delta2;
661
blue->flags |= AF_LATIN_BLUE_ACTIVE;
668
/* Scale global values in both directions. */
671
af_latin_metrics_scale( AF_LatinMetrics metrics,
674
metrics->root.scaler.render_mode = scaler->render_mode;
675
metrics->root.scaler.face = scaler->face;
677
af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ );
678
af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
682
/*************************************************************************/
683
/*************************************************************************/
685
/***** L A T I N G L Y P H A N A L Y S I S *****/
687
/*************************************************************************/
688
/*************************************************************************/
691
/* Walk over all contours and compute its segments. */
693
FT_LOCAL_DEF( FT_Error )
694
af_latin_hints_compute_segments( AF_GlyphHints hints,
697
AF_AxisHints axis = &hints->axis[dim];
698
FT_Memory memory = hints->memory;
699
FT_Error error = AF_Err_Ok;
700
AF_Segment segment = NULL;
702
AF_Point* contour = hints->contours;
703
AF_Point* contour_limit = contour + hints->num_contours;
704
AF_Direction major_dir, segment_dir;
709
seg0.flags = AF_EDGE_NORMAL;
711
major_dir = (AF_Direction)FT_ABS( axis->major_dir );
712
segment_dir = major_dir;
714
axis->num_segments = 0;
716
/* set up (u,v) in each point */
717
if ( dim == AF_DIMENSION_HORZ )
719
AF_Point point = hints->points;
720
AF_Point limit = point + hints->num_points;
723
for ( ; point < limit; point++ )
725
point->u = point->fx;
726
point->v = point->fy;
731
AF_Point point = hints->points;
732
AF_Point limit = point + hints->num_points;
735
for ( ; point < limit; point++ )
737
point->u = point->fy;
738
point->v = point->fx;
742
/* do each contour separately */
743
for ( ; contour < contour_limit; contour++ )
745
AF_Point point = contour[0];
746
AF_Point last = point->prev;
748
FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */
749
FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
753
if ( point == last ) /* skip singletons -- just in case */
756
if ( FT_ABS( last->out_dir ) == major_dir &&
757
FT_ABS( point->out_dir ) == major_dir )
759
/* we are already on an edge, try to locate its start */
765
if ( FT_ABS( point->out_dir ) != major_dir )
791
if ( point->out_dir != segment_dir || point == last )
793
/* we are just leaving an edge; record a new segment! */
794
segment->last = point;
795
segment->pos = (FT_Short)( ( min_pos + max_pos ) >> 1 );
797
/* a segment is round if either its first or last point */
798
/* is a control point */
799
if ( ( segment->first->flags | point->flags ) &
801
segment->flags |= AF_EDGE_ROUND;
803
/* compute segment size */
804
min_pos = max_pos = point->v;
806
v = segment->first->v;
812
segment->min_coord = (FT_Short)min_pos;
813
segment->max_coord = (FT_Short)max_pos;
814
segment->height = (FT_Short)( segment->max_coord -
815
segment->min_coord );
823
/* now exit if we are at the start/end point */
831
if ( !on_edge && FT_ABS( point->out_dir ) == major_dir )
833
/* this is the start of a new segment! */
834
segment_dir = (AF_Direction)point->out_dir;
836
/* clear all segment fields */
837
error = af_axis_hints_new_segment( axis, memory, &segment );
842
segment->dir = (FT_Char)segment_dir;
843
min_pos = max_pos = point->u;
844
segment->first = point;
845
segment->last = point;
846
segment->contour = contour;
856
/* now slightly increase the height of segments when this makes */
857
/* sense -- this is used to better detect and ignore serifs */
859
AF_Segment segments = axis->segments;
860
AF_Segment segments_end = segments + axis->num_segments;
863
for ( segment = segments; segment < segments_end; segment++ )
865
AF_Point first = segment->first;
866
AF_Point last = segment->last;
867
FT_Pos first_v = first->v;
868
FT_Pos last_v = last->v;
874
if ( first_v < last_v )
880
if ( p->v < first_v )
881
segment->height = (FT_Short)( segment->height +
882
( ( first_v - p->v ) >> 1 ) );
886
segment->height = (FT_Short)( segment->height +
887
( ( p->v - last_v ) >> 1 ) );
895
if ( p->v > first_v )
896
segment->height = (FT_Short)( segment->height +
897
( ( p->v - first_v ) >> 1 ) );
901
segment->height = (FT_Short)( segment->height +
902
( ( last_v - p->v ) >> 1 ) );
912
/* Link segments to form stems and serifs. */
915
af_latin_hints_link_segments( AF_GlyphHints hints,
918
AF_AxisHints axis = &hints->axis[dim];
919
AF_Segment segments = axis->segments;
920
AF_Segment segment_limit = segments + axis->num_segments;
921
FT_Pos len_threshold, len_score;
922
AF_Segment seg1, seg2;
925
len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 );
926
if ( len_threshold == 0 )
929
len_score = AF_LATIN_CONSTANT( hints->metrics, 6000 );
931
/* now compare each segment to the others */
932
for ( seg1 = segments; seg1 < segment_limit; seg1++ )
934
/* the fake segments are introduced to hint the metrics -- */
935
/* we must never link them to anything */
936
if ( seg1->dir != axis->major_dir || seg1->first == seg1->last )
939
/* search for stems having opposite directions, */
940
/* with seg1 to the `left' of seg2 */
941
for ( seg2 = segments; seg2 < segment_limit; seg2++ )
943
FT_Pos pos1 = seg1->pos;
944
FT_Pos pos2 = seg2->pos;
947
if ( seg1->dir + seg2->dir == 0 && pos2 > pos1 )
949
/* compute distance between the two segments */
950
FT_Pos dist = pos2 - pos1;
951
FT_Pos min = seg1->min_coord;
952
FT_Pos max = seg1->max_coord;
956
if ( min < seg2->min_coord )
957
min = seg2->min_coord;
959
if ( max > seg2->max_coord )
960
max = seg2->max_coord;
962
/* compute maximum coordinate difference of the two segments */
964
if ( len >= len_threshold )
966
/* small coordinate differences cause a higher score, and */
967
/* segments with a greater distance cause a higher score also */
968
score = dist + len_score / len;
970
/* and we search for the smallest score */
971
/* of the sum of the two values */
972
if ( score < seg1->score )
978
if ( score < seg2->score )
988
/* now compute the `serif' segments, cf. explanations in `afhints.h' */
989
for ( seg1 = segments; seg1 < segment_limit; seg1++ )
995
if ( seg2->link != seg1 )
998
seg1->serif = seg2->link;
1005
/* Link segments to edges, using feature analysis for selection. */
1007
FT_LOCAL_DEF( FT_Error )
1008
af_latin_hints_compute_edges( AF_GlyphHints hints,
1011
AF_AxisHints axis = &hints->axis[dim];
1012
FT_Error error = AF_Err_Ok;
1013
FT_Memory memory = hints->memory;
1014
AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim];
1016
AF_Segment segments = axis->segments;
1017
AF_Segment segment_limit = segments + axis->num_segments;
1020
AF_Direction up_dir;
1022
FT_Pos edge_distance_threshold;
1023
FT_Pos segment_length_threshold;
1026
axis->num_edges = 0;
1028
scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
1031
up_dir = ( dim == AF_DIMENSION_HORZ ) ? AF_DIR_UP
1035
* We ignore all segments that are less than 1 pixel in length
1036
* to avoid many problems with serif fonts. We compute the
1037
* corresponding threshold in font units.
1039
if ( dim == AF_DIMENSION_HORZ )
1040
segment_length_threshold = FT_DivFix( 64, hints->y_scale );
1042
segment_length_threshold = 0;
1044
/*********************************************************************/
1046
/* We begin by generating a sorted table of edges for the current */
1047
/* direction. To do so, we simply scan each segment and try to find */
1048
/* an edge in our table that corresponds to its position. */
1050
/* If no edge is found, we create and insert a new edge in the */
1051
/* sorted table. Otherwise, we simply add the segment to the edge's */
1052
/* list which gets processed in the second step to compute the */
1053
/* edge's properties. */
1055
/* Note that the table of edges is sorted along the segment/edge */
1058
/*********************************************************************/
1060
/* assure that edge distance threshold is at least 0.25px */
1061
edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
1063
if ( edge_distance_threshold > 64 / 4 )
1064
edge_distance_threshold = 64 / 4;
1066
edge_distance_threshold = FT_DivFix( edge_distance_threshold,
1069
for ( seg = segments; seg < segment_limit; seg++ )
1071
AF_Edge found = NULL;
1075
if ( seg->height < segment_length_threshold )
1078
/* A special case for serif edges: If they are smaller than */
1079
/* 1.5 pixels we ignore them. */
1081
2 * seg->height < 3 * segment_length_threshold )
1084
/* look for an edge corresponding to the segment */
1085
for ( ee = 0; ee < axis->num_edges; ee++ )
1087
AF_Edge edge = axis->edges + ee;
1091
dist = seg->pos - edge->fpos;
1095
if ( dist < edge_distance_threshold && edge->dir == seg->dir )
1107
/* insert a new edge in the list and */
1108
/* sort according to the position */
1109
error = af_axis_hints_new_edge( axis, seg->pos,
1110
(AF_Direction)seg->dir,
1115
/* add the segment to the new edge's list */
1120
edge->dir = seg->dir;
1121
edge->fpos = seg->pos;
1122
edge->opos = FT_MulFix( seg->pos, scale );
1123
edge->pos = edge->opos;
1124
seg->edge_next = seg;
1128
/* if an edge was found, simply add the segment to the edge's */
1130
seg->edge_next = found->first;
1131
found->last->edge_next = seg;
1137
/*********************************************************************/
1139
/* Good, we will now compute each edge's properties according to */
1140
/* the segments found on its position. Basically, these are */
1142
/* - the edge's main direction */
1143
/* - stem edge, serif edge or both (which defaults to stem then) */
1144
/* - rounded edge, straight or both (which defaults to straight) */
1145
/* - link for edge */
1147
/*********************************************************************/
1149
/* first of all, set the `edge' field in each segment -- this is */
1150
/* required in order to compute edge links */
1153
* Note that removing this loop and setting the `edge' field of each
1154
* segment directly in the code above slows down execution speed for
1155
* some reasons on platforms like the Sun.
1158
AF_Edge edges = axis->edges;
1159
AF_Edge edge_limit = edges + axis->num_edges;
1163
for ( edge = edges; edge < edge_limit; edge++ )
1170
seg = seg->edge_next;
1172
} while ( seg != edge->first );
1175
/* now compute each edge properties */
1176
for ( edge = edges; edge < edge_limit; edge++ )
1178
FT_Int is_round = 0; /* does it contain round segments? */
1179
FT_Int is_straight = 0; /* does it contain straight segments? */
1181
FT_Pos ups = 0; /* number of upwards segments */
1182
FT_Pos downs = 0; /* number of downwards segments */
1193
/* check for roundness of segment */
1194
if ( seg->flags & AF_EDGE_ROUND )
1200
/* check for segment direction */
1201
if ( seg->dir == up_dir )
1202
ups += seg->max_coord - seg->min_coord;
1204
downs += seg->max_coord - seg->min_coord;
1207
/* check for links -- if seg->serif is set, then seg->link must */
1209
is_serif = (FT_Bool)( seg->serif &&
1211
seg->serif->edge != edge );
1213
if ( ( seg->link && seg->link->edge != NULL ) || is_serif )
1225
edge2 = edge->serif;
1234
edge_delta = edge->fpos - edge2->fpos;
1235
if ( edge_delta < 0 )
1236
edge_delta = -edge_delta;
1238
seg_delta = seg->pos - seg2->pos;
1239
if ( seg_delta < 0 )
1240
seg_delta = -seg_delta;
1242
if ( seg_delta < edge_delta )
1250
edge->serif = edge2;
1251
edge2->flags |= AF_EDGE_SERIF;
1257
seg = seg->edge_next;
1259
} while ( seg != edge->first );
1261
/* set the round/straight flags */
1262
edge->flags = AF_EDGE_NORMAL;
1264
if ( is_round > 0 && is_round >= is_straight )
1265
edge->flags |= AF_EDGE_ROUND;
1268
/* set the edge's main direction */
1269
edge->dir = AF_DIR_NONE;
1272
edge->dir = (FT_Char)up_dir;
1274
else if ( ups < downs )
1275
edge->dir = (FT_Char)-up_dir;
1277
else if ( ups == downs )
1278
edge->dir = 0; /* both up and down! */
1281
/* get rid of serifs if link is set */
1282
/* XXX: This gets rid of many unpleasant artefacts! */
1283
/* Example: the `c' in cour.pfa at size 13 */
1285
if ( edge->serif && edge->link )
1295
/* Detect segments and edges for given dimension. */
1297
FT_LOCAL_DEF( FT_Error )
1298
af_latin_hints_detect_features( AF_GlyphHints hints,
1304
error = af_latin_hints_compute_segments( hints, dim );
1307
af_latin_hints_link_segments( hints, dim );
1309
error = af_latin_hints_compute_edges( hints, dim );
1316
/* Compute all edges which lie within blue zones. */
1318
FT_LOCAL_DEF( void )
1319
af_latin_hints_compute_blue_edges( AF_GlyphHints hints,
1320
AF_LatinMetrics metrics )
1322
AF_AxisHints axis = &hints->axis[AF_DIMENSION_VERT];
1323
AF_Edge edge = axis->edges;
1324
AF_Edge edge_limit = edge + axis->num_edges;
1325
AF_LatinAxis latin = &metrics->axis[AF_DIMENSION_VERT];
1326
FT_Fixed scale = latin->scale;
1329
/* compute which blue zones are active, i.e. have their scaled */
1330
/* size < 3/4 pixels */
1332
/* for each horizontal edge search the blue zone which is closest */
1333
for ( ; edge < edge_limit; edge++ )
1336
AF_Width best_blue = NULL;
1337
FT_Pos best_dist; /* initial threshold */
1340
/* compute the initial threshold as a fraction of the EM size */
1341
/* (the value 40 is heuristic) */
1342
best_dist = FT_MulFix( metrics->units_per_em / 40, scale );
1344
/* assure a minimum distance of 0.5px */
1345
if ( best_dist > 64 / 2 )
1348
for ( bb = 0; bb < AF_LATIN_BLUE_MAX; bb++ )
1350
AF_LatinBlue blue = latin->blues + bb;
1351
FT_Bool is_top_blue, is_major_dir;
1354
/* skip inactive blue zones (i.e., those that are too large) */
1355
if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) )
1358
/* if it is a top zone, check for right edges -- if it is a bottom */
1359
/* zone, check for left edges */
1361
/* of course, that's for TrueType */
1362
is_top_blue = (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_TOP ) != 0 );
1363
is_major_dir = FT_BOOL( edge->dir == axis->major_dir );
1365
/* if it is a top zone, the edge must be against the major */
1366
/* direction; if it is a bottom zone, it must be in the major */
1368
if ( is_top_blue ^ is_major_dir )
1373
/* first of all, compare it to the reference position */
1374
dist = edge->fpos - blue->ref.org;
1378
dist = FT_MulFix( dist, scale );
1379
if ( dist < best_dist )
1382
best_blue = &blue->ref;
1385
/* now compare it to the overshoot position and check whether */
1386
/* the edge is rounded, and whether the edge is over the */
1387
/* reference position of a top zone, or under the reference */
1388
/* position of a bottom zone */
1389
if ( edge->flags & AF_EDGE_ROUND && dist != 0 )
1391
FT_Bool is_under_ref = FT_BOOL( edge->fpos < blue->ref.org );
1394
if ( is_top_blue ^ is_under_ref )
1396
dist = edge->fpos - blue->shoot.org;
1400
dist = FT_MulFix( dist, scale );
1401
if ( dist < best_dist )
1404
best_blue = &blue->shoot;
1412
edge->blue_edge = best_blue;
1417
/* Initalize hinting engine. */
1420
af_latin_hints_init( AF_GlyphHints hints,
1421
AF_LatinMetrics metrics )
1423
FT_Render_Mode mode;
1424
FT_UInt32 scaler_flags, other_flags;
1425
FT_Face face = metrics->root.scaler.face;
1428
af_glyph_hints_rescale( hints, (AF_ScriptMetrics)metrics );
1431
* correct x_scale and y_scale if needed, since they may have
1432
* been modified by `af_latin_metrics_scale_dim' above
1434
hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale;
1435
hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta;
1436
hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale;
1437
hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta;
1439
/* compute flags depending on render mode, etc. */
1440
mode = metrics->root.scaler.render_mode;
1442
#if 0 /* #ifdef AF_CONFIG_OPTION_USE_WARPER */
1443
if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V )
1445
metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL;
1449
scaler_flags = hints->scaler_flags;
1453
* We snap the width of vertical stems for the monochrome and
1454
* horizontal LCD rendering targets only.
1456
if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD )
1457
other_flags |= AF_LATIN_HINTS_HORZ_SNAP;
1460
* We snap the width of horizontal stems for the monochrome and
1461
* vertical LCD rendering targets only.
1463
if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V )
1464
other_flags |= AF_LATIN_HINTS_VERT_SNAP;
1467
* We adjust stems to full pixels only if we don't use the `light' mode.
1469
if ( mode != FT_RENDER_MODE_LIGHT )
1470
other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
1472
if ( mode == FT_RENDER_MODE_MONO )
1473
other_flags |= AF_LATIN_HINTS_MONO;
1476
* In `light' hinting mode we disable horizontal hinting completely.
1477
* We also do it if the face is italic.
1479
if ( mode == FT_RENDER_MODE_LIGHT ||
1480
( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0 )
1481
scaler_flags |= AF_SCALER_FLAG_NO_HORIZONTAL;
1483
hints->scaler_flags = scaler_flags;
1484
hints->other_flags = other_flags;
1490
/*************************************************************************/
1491
/*************************************************************************/
1493
/***** L A T I N G L Y P H G R I D - F I T T I N G *****/
1495
/*************************************************************************/
1496
/*************************************************************************/
1498
/* Snap a given width in scaled coordinates to one of the */
1499
/* current standard widths. */
1502
af_latin_snap_width( AF_Width widths,
1507
FT_Pos best = 64 + 32 + 2;
1508
FT_Pos reference = width;
1512
for ( n = 0; n < count; n++ )
1529
scaled = FT_PIX_ROUND( reference );
1531
if ( width >= reference )
1533
if ( width < scaled + 48 )
1538
if ( width > scaled - 48 )
1546
/* Compute the snapped width of a given stem, ignoring very thin ones. */
1547
/* There is a lot of voodoo in this function; changing the hard-coded */
1548
/* parameters influence the whole hinting process. */
1551
af_latin_compute_stem_width( AF_GlyphHints hints,
1554
AF_Edge_Flags base_flags,
1555
AF_Edge_Flags stem_flags )
1557
AF_LatinMetrics metrics = (AF_LatinMetrics) hints->metrics;
1558
AF_LatinAxis axis = & metrics->axis[dim];
1559
FT_Pos dist = width;
1561
FT_Int vertical = ( dim == AF_DIMENSION_VERT );
1564
if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) ||
1574
if ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ||
1575
( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) )
1577
/* smooth hinting process: very lightly quantize the stem width */
1579
/* leave the widths of serifs alone */
1580
if ( ( stem_flags & AF_EDGE_SERIF ) &&
1585
else if ( base_flags & AF_EDGE_ROUND )
1590
else if ( dist < 56 )
1593
if ( axis->width_count > 0 )
1598
/* compare to standard width */
1599
delta = dist - axis->widths[0].cur;
1606
dist = axis->widths[0].cur;
1613
if ( dist < 3 * 64 )
1621
else if ( delta < 32 )
1624
else if ( delta < 54 )
1631
dist = ( dist + 32 ) & ~63;
1636
/* strong hinting process: snap the stem width to integer pixels */
1638
FT_Pos org_dist = dist;
1641
dist = af_latin_snap_width( axis->widths, axis->width_count, dist );
1645
/* in the case of vertical hinting, always round */
1646
/* the stem heights to integer pixels */
1649
dist = ( dist + 16 ) & ~63;
1655
if ( AF_LATIN_HINTS_DO_MONO( hints ) )
1657
/* monochrome horizontal hinting: snap widths to integer pixels */
1658
/* with a different threshold */
1663
dist = ( dist + 32 ) & ~63;
1667
/* for horizontal anti-aliased hinting, we adopt a more subtle */
1668
/* approach: we strengthen small stems, round stems whose size */
1669
/* is between 1 and 2 pixels to an integer, otherwise nothing */
1672
dist = ( dist + 64 ) >> 1;
1674
else if ( dist < 128 )
1676
/* We only round to an integer width if the corresponding */
1677
/* distortion is less than 1/4 pixel. Otherwise this */
1678
/* makes everything worse since the diagonals, which are */
1679
/* not hinted, appear a lot bolder or thinner than the */
1680
/* vertical stems. */
1685
dist = ( dist + 22 ) & ~63;
1686
delta = dist - org_dist;
1694
dist = ( dist + 64 ) >> 1;
1698
/* round otherwise to prevent color fringes in LCD mode */
1699
dist = ( dist + 32 ) & ~63;
1712
/* Align one stem edge relative to the previous stem edge. */
1715
af_latin_align_linked_edge( AF_GlyphHints hints,
1720
FT_Pos dist = stem_edge->opos - base_edge->opos;
1722
FT_Pos fitted_width = af_latin_compute_stem_width(
1724
(AF_Edge_Flags)base_edge->flags,
1725
(AF_Edge_Flags)stem_edge->flags );
1728
stem_edge->pos = base_edge->pos + fitted_width;
1730
FT_TRACE5(( "LINK: edge %d (opos=%.2f) linked to (%.2f),"
1731
" dist was %.2f, now %.2f\n",
1732
stem_edge-hints->axis[dim].edges, stem_edge->opos / 64.0,
1733
stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0 ));
1737
/* Shift the coordinates of the `serif' edge by the same amount */
1738
/* as the corresponding `base' edge has been moved already. */
1741
af_latin_align_serif_edge( AF_GlyphHints hints,
1747
serif->pos = base->pos + ( serif->opos - base->opos );
1751
/*************************************************************************/
1752
/*************************************************************************/
1753
/*************************************************************************/
1755
/**** E D G E H I N T I N G ****/
1757
/*************************************************************************/
1758
/*************************************************************************/
1759
/*************************************************************************/
1762
/* The main grid-fitting routine. */
1764
FT_LOCAL_DEF( void )
1765
af_latin_hint_edges( AF_GlyphHints hints,
1768
AF_AxisHints axis = &hints->axis[dim];
1769
AF_Edge edges = axis->edges;
1770
AF_Edge edge_limit = edges + axis->num_edges;
1773
AF_Edge anchor = NULL;
1774
FT_Int has_serifs = 0;
1777
/* we begin by aligning all stems relative to the blue zone */
1778
/* if needed -- that's only for horizontal edges */
1780
if ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_BLUES( hints ) )
1782
for ( edge = edges; edge < edge_limit; edge++ )
1785
AF_Edge edge1, edge2;
1788
if ( edge->flags & AF_EDGE_DONE )
1791
blue = edge->blue_edge;
1798
else if ( edge2 && edge2->blue_edge )
1800
blue = edge2->blue_edge;
1808
FT_TRACE5(( "BLUE: edge %d (opos=%.2f) snapped to (%.2f),"
1810
edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
1811
edge1->pos / 64.0 ));
1813
edge1->pos = blue->fit;
1814
edge1->flags |= AF_EDGE_DONE;
1816
if ( edge2 && !edge2->blue_edge )
1818
af_latin_align_linked_edge( hints, dim, edge1, edge2 );
1819
edge2->flags |= AF_EDGE_DONE;
1827
/* now we align all stem edges, trying to maintain the */
1828
/* relative order of stems in the glyph */
1829
for ( edge = edges; edge < edge_limit; edge++ )
1834
if ( edge->flags & AF_EDGE_DONE )
1837
/* skip all non-stem edges */
1845
/* now align the stem */
1847
/* this should not happen, but it's better to be safe */
1848
if ( edge2->blue_edge )
1850
FT_TRACE5(( "ASSERTION FAILED for edge %d\n", edge2-edges ));
1852
af_latin_align_linked_edge( hints, dim, edge2, edge );
1853
edge->flags |= AF_EDGE_DONE;
1859
FT_Pos org_len, org_center, cur_len;
1860
FT_Pos cur_pos1, error1, error2, u_off, d_off;
1863
org_len = edge2->opos - edge->opos;
1864
cur_len = af_latin_compute_stem_width(
1865
hints, dim, org_len,
1866
(AF_Edge_Flags)edge->flags,
1867
(AF_Edge_Flags)edge2->flags );
1869
/* some voodoo to specially round edges for small stem widths */
1870
if ( cur_len <= 64 )
1883
org_center = edge->opos + ( org_len >> 1 );
1884
cur_pos1 = FT_PIX_ROUND( org_center );
1886
error1 = org_center - ( cur_pos1 - u_off );
1890
error2 = org_center - ( cur_pos1 + d_off );
1894
if ( error1 < error2 )
1899
edge->pos = cur_pos1 - cur_len / 2;
1900
edge2->pos = edge->pos + cur_len;
1903
edge->pos = FT_PIX_ROUND( edge->opos );
1905
FT_TRACE5(( "ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
1906
" snapped to (%.2f) (%.2f)\n",
1907
edge - edges, edge->opos / 64.0,
1908
edge2 - edges, edge2->opos / 64.0,
1909
edge->pos / 64.0, edge2->pos / 64.0 ));
1912
edge->flags |= AF_EDGE_DONE;
1914
af_latin_align_linked_edge( hints, dim, edge, edge2 );
1918
FT_Pos org_pos, org_len, org_center, cur_len;
1919
FT_Pos cur_pos1, cur_pos2, delta1, delta2;
1922
org_pos = anchor->pos + ( edge->opos - anchor->opos );
1923
org_len = edge2->opos - edge->opos;
1924
org_center = org_pos + ( org_len >> 1 );
1926
cur_len = af_latin_compute_stem_width(
1927
hints, dim, org_len,
1928
(AF_Edge_Flags)edge->flags,
1929
(AF_Edge_Flags)edge2->flags );
1931
if ( edge2->flags & AF_EDGE_DONE )
1932
edge->pos = edge2->pos - cur_len;
1934
else if ( cur_len < 96 )
1936
FT_Pos u_off, d_off;
1939
cur_pos1 = FT_PIX_ROUND( org_center );
1952
delta1 = org_center - ( cur_pos1 - u_off );
1956
delta2 = org_center - ( cur_pos1 + d_off );
1960
if ( delta1 < delta2 )
1965
edge->pos = cur_pos1 - cur_len / 2;
1966
edge2->pos = cur_pos1 + cur_len / 2;
1968
FT_TRACE5(( "STEM: %d (opos=%.2f) to %d (opos=%.2f)"
1969
" snapped to (%.2f) and (%.2f)\n",
1970
edge - edges, edge->opos / 64.0,
1971
edge2 - edges, edge2->opos / 64.0,
1972
edge->pos / 64.0, edge2->pos / 64.0 ));
1976
org_pos = anchor->pos + ( edge->opos - anchor->opos );
1977
org_len = edge2->opos - edge->opos;
1978
org_center = org_pos + ( org_len >> 1 );
1980
cur_len = af_latin_compute_stem_width(
1981
hints, dim, org_len,
1982
(AF_Edge_Flags)edge->flags,
1983
(AF_Edge_Flags)edge2->flags );
1985
cur_pos1 = FT_PIX_ROUND( org_pos );
1986
delta1 = cur_pos1 + ( cur_len >> 1 ) - org_center;
1990
cur_pos2 = FT_PIX_ROUND( org_pos + org_len ) - cur_len;
1991
delta2 = cur_pos2 + ( cur_len >> 1 ) - org_center;
1995
edge->pos = ( delta1 < delta2 ) ? cur_pos1 : cur_pos2;
1996
edge2->pos = edge->pos + cur_len;
1998
FT_TRACE5(( "STEM: %d (opos=%.2f) to %d (opos=%.2f)"
1999
" snapped to (%.2f) and (%.2f)\n",
2000
edge - edges, edge->opos / 64.0,
2001
edge2 - edges, edge2->opos / 64.0,
2002
edge->pos / 64.0, edge2->pos / 64.0 ));
2005
edge->flags |= AF_EDGE_DONE;
2006
edge2->flags |= AF_EDGE_DONE;
2008
if ( edge > edges && edge->pos < edge[-1].pos )
2010
FT_TRACE5(( "BOUND: %d (pos=%.2f) to (%.2f)\n",
2011
edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0 ));
2012
edge->pos = edge[-1].pos;
2017
/* make sure that lowercase m's maintain their symmetry */
2019
/* In general, lowercase m's have six vertical edges if they are sans */
2020
/* serif, or twelve if they are with serifs. This implementation is */
2021
/* based on that assumption, and seems to work very well with most */
2022
/* faces. However, if for a certain face this assumption is not */
2023
/* true, the m is just rendered like before. In addition, any stem */
2024
/* correction will only be applied to symmetrical glyphs (even if the */
2025
/* glyph is not an m), so the potential for unwanted distortion is */
2026
/* relatively low. */
2028
/* We don't handle horizontal edges since we can't easily assure that */
2029
/* the third (lowest) stem aligns with the base line; it might end up */
2030
/* one pixel higher or lower. */
2032
n_edges = edge_limit - edges;
2033
if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) )
2035
AF_Edge edge1, edge2, edge3;
2036
FT_Pos dist1, dist2, span, delta;
2052
dist1 = edge2->opos - edge1->opos;
2053
dist2 = edge3->opos - edge2->opos;
2055
span = dist1 - dist2;
2061
delta = edge3->pos - ( 2 * edge2->pos - edge1->pos );
2062
edge3->pos -= delta;
2064
edge3->link->pos -= delta;
2066
/* move the serifs along with the stem */
2067
if ( n_edges == 12 )
2069
( edges + 8 )->pos -= delta;
2070
( edges + 11 )->pos -= delta;
2073
edge3->flags |= AF_EDGE_DONE;
2075
edge3->link->flags |= AF_EDGE_DONE;
2079
if ( has_serifs || !anchor )
2082
* now hint the remaining edges (serifs and single) in order
2083
* to complete our processing
2085
for ( edge = edges; edge < edge_limit; edge++ )
2090
if ( edge->flags & AF_EDGE_DONE )
2097
delta = edge->serif->opos - edge->opos;
2102
if ( delta < 64 + 16 )
2104
af_latin_align_serif_edge( hints, edge->serif, edge );
2105
FT_TRACE5(( "SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
2106
" aligned to (%.2f)\n",
2107
edge - edges, edge->opos / 64.0,
2108
edge->serif - edges, edge->serif->opos / 64.0,
2109
edge->pos / 64.0 ));
2113
FT_TRACE5(( "SERIF_ANCHOR: edge %d (opos=%.2f)"
2114
" snapped to (%.2f)\n",
2115
edge-edges, edge->opos / 64.0, edge->pos / 64.0 ));
2116
edge->pos = FT_PIX_ROUND( edge->opos );
2121
AF_Edge before, after;
2124
for ( before = edge - 1; before >= edges; before-- )
2125
if ( before->flags & AF_EDGE_DONE )
2128
for ( after = edge + 1; after < edge_limit; after++ )
2129
if ( after->flags & AF_EDGE_DONE )
2132
if ( before >= edges && before < edge &&
2133
after < edge_limit && after > edge )
2135
if ( after->opos == before->opos )
2136
edge->pos = before->pos;
2138
edge->pos = before->pos +
2139
FT_MulDiv( edge->opos - before->opos,
2140
after->pos - before->pos,
2141
after->opos - before->opos );
2143
FT_TRACE5(( "SERIF_LINK1: edge %d (opos=%.2f) snapped to (%.2f)"
2144
" from %d (opos=%.2f)\n",
2145
edge - edges, edge->opos / 64.0,
2147
before - edges, before->opos / 64.0 ));
2151
edge->pos = anchor->pos +
2152
( ( edge->opos - anchor->opos + 16 ) & ~31 );
2154
FT_TRACE5(( "SERIF_LINK2: edge %d (opos=%.2f)"
2155
" snapped to (%.2f)\n",
2156
edge - edges, edge->opos / 64.0, edge->pos / 64.0 ));
2160
edge->flags |= AF_EDGE_DONE;
2162
if ( edge > edges && edge->pos < edge[-1].pos )
2163
edge->pos = edge[-1].pos;
2165
if ( edge + 1 < edge_limit &&
2166
edge[1].flags & AF_EDGE_DONE &&
2167
edge->pos > edge[1].pos )
2168
edge->pos = edge[1].pos;
2174
/* Apply the complete hinting algorithm to a latin glyph. */
2177
af_latin_hints_apply( AF_GlyphHints hints,
2178
FT_Outline* outline,
2179
AF_LatinMetrics metrics )
2185
error = af_glyph_hints_reload( hints, outline );
2189
/* analyze glyph outline */
2190
#ifdef AF_CONFIG_OPTION_USE_WARPER
2191
if ( metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT ||
2192
AF_HINTS_DO_HORIZONTAL( hints ) )
2194
if ( AF_HINTS_DO_HORIZONTAL( hints ) )
2197
error = af_latin_hints_detect_features( hints, AF_DIMENSION_HORZ );
2202
if ( AF_HINTS_DO_VERTICAL( hints ) )
2204
error = af_latin_hints_detect_features( hints, AF_DIMENSION_VERT );
2208
af_latin_hints_compute_blue_edges( hints, metrics );
2211
/* grid-fit the outline */
2212
for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
2214
#ifdef AF_CONFIG_OPTION_USE_WARPER
2215
if ( dim == AF_DIMENSION_HORZ &&
2216
metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT )
2218
AF_WarperRec warper;
2223
af_warper_compute( &warper, hints, (AF_Dimension)dim,
2225
af_glyph_hints_scale_dim( hints, (AF_Dimension)dim,
2231
if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) ||
2232
( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) )
2234
af_latin_hint_edges( hints, (AF_Dimension)dim );
2235
af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim );
2236
af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
2237
af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
2240
af_glyph_hints_save( hints, outline );
2247
/*************************************************************************/
2248
/*************************************************************************/
2250
/***** L A T I N S C R I P T C L A S S *****/
2252
/*************************************************************************/
2253
/*************************************************************************/
2256
/* XXX: this should probably fine tuned to differentiate better between */
2259
static const AF_Script_UniRangeRec af_latin_uniranges[] =
2261
AF_UNIRANGE_REC( 0x0020UL, 0x007FUL ), /* Basic Latin (no control chars) */
2262
AF_UNIRANGE_REC( 0x00A0UL, 0x00FFUL ), /* Latin-1 Supplement (no control chars) */
2263
AF_UNIRANGE_REC( 0x0100UL, 0x017FUL ), /* Latin Extended-A */
2264
AF_UNIRANGE_REC( 0x0180UL, 0x024FUL ), /* Latin Extended-B */
2265
AF_UNIRANGE_REC( 0x0250UL, 0x02AFUL ), /* IPA Extensions */
2266
AF_UNIRANGE_REC( 0x02B0UL, 0x02FFUL ), /* Spacing Modifier Letters */
2267
AF_UNIRANGE_REC( 0x0300UL, 0x036FUL ), /* Combining Diacritical Marks */
2268
AF_UNIRANGE_REC( 0x0370UL, 0x03FFUL ), /* Greek and Coptic */
2269
AF_UNIRANGE_REC( 0x0400UL, 0x04FFUL ), /* Cyrillic */
2270
AF_UNIRANGE_REC( 0x0500UL, 0x052FUL ), /* Cyrillic Supplement */
2271
AF_UNIRANGE_REC( 0x1D00UL, 0x1D7FUL ), /* Phonetic Extensions */
2272
AF_UNIRANGE_REC( 0x1D80UL, 0x1DBFUL ), /* Phonetic Extensions Supplement */
2273
AF_UNIRANGE_REC( 0x1DC0UL, 0x1DFFUL ), /* Combining Diacritical Marks Supplement */
2274
AF_UNIRANGE_REC( 0x1E00UL, 0x1EFFUL ), /* Latin Extended Additional */
2275
AF_UNIRANGE_REC( 0x1F00UL, 0x1FFFUL ), /* Greek Extended */
2276
AF_UNIRANGE_REC( 0x2000UL, 0x206FUL ), /* General Punctuation */
2277
AF_UNIRANGE_REC( 0x2070UL, 0x209FUL ), /* Superscripts and Subscripts */
2278
AF_UNIRANGE_REC( 0x20A0UL, 0x20CFUL ), /* Currency Symbols */
2279
AF_UNIRANGE_REC( 0x2150UL, 0x218FUL ), /* Number Forms */
2280
AF_UNIRANGE_REC( 0x2460UL, 0x24FFUL ), /* Enclosed Alphanumerics */
2281
AF_UNIRANGE_REC( 0x2C60UL, 0x2C7FUL ), /* Latin Extended-C */
2282
AF_UNIRANGE_REC( 0x2DE0UL, 0x2DFFUL ), /* Cyrillic Extended-A */
2283
AF_UNIRANGE_REC( 0xA640UL, 0xA69FUL ), /* Cyrillic Extended-B */
2284
AF_UNIRANGE_REC( 0xA720UL, 0xA7FFUL ), /* Latin Extended-D */
2285
AF_UNIRANGE_REC( 0xFB00UL, 0xFB06UL ), /* Alphab. Present. Forms (Latin Ligs) */
2286
AF_UNIRANGE_REC( 0x1D400UL, 0x1D7FFUL ), /* Mathematical Alphanumeric Symbols */
2287
AF_UNIRANGE_REC( 0UL, 0UL )
2291
AF_DEFINE_SCRIPT_CLASS(af_latin_script_class,
2295
sizeof( AF_LatinMetricsRec ),
2297
(AF_Script_InitMetricsFunc) af_latin_metrics_init,
2298
(AF_Script_ScaleMetricsFunc)af_latin_metrics_scale,
2299
(AF_Script_DoneMetricsFunc) NULL,
2301
(AF_Script_InitHintsFunc) af_latin_hints_init,
2302
(AF_Script_ApplyHintsFunc) af_latin_hints_apply