1 /***************************************************************************/
2 /* */
3 /* aflatin.c */
4 /* */
5 /* Auto-fitter hinting routines for latin writing system (body). */
6 /* */
7 /* Copyright 2003-2018 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
9 /* */
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. */
15 /* */
16 /***************************************************************************/
17
18
19 #include <ft2build.h>
20 #include FT_ADVANCES_H
21 #include FT_INTERNAL_DEBUG_H
22
23 #include "afglobal.h"
24 #include "afpic.h"
25 #include "aflatin.h"
26 #include "aferrors.h"
27
28
29 #ifdef AF_CONFIG_OPTION_USE_WARPER
30 #include "afwarp.h"
31 #endif
32
33
34 /*************************************************************************/
35 /* */
36 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
37 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
38 /* messages during execution. */
39 /* */
40 #undef FT_COMPONENT
41 #define FT_COMPONENT trace_aflatin
42
43
44 /* needed for computation of round vs. flat segments */
45 #define FLAT_THRESHOLD( x ) ( x / 14 )
46
47
48 /*************************************************************************/
49 /*************************************************************************/
50 /***** *****/
51 /***** L A T I N G L O B A L M E T R I C S *****/
52 /***** *****/
53 /*************************************************************************/
54 /*************************************************************************/
55
56
57 /* Find segments and links, compute all stem widths, and initialize */
58 /* standard width and height for the glyph with given charcode. */
59
60 FT_LOCAL_DEF( void )
61 af_latin_metrics_init_widths( AF_LatinMetrics metrics,
66
67
68 FT_TRACE5(( "\n"
69 "latin standard widths computation (style `%s')\n"
70 "=====================================================\n"
71 "\n",
72 af_style_names[metrics->root.style_class->style] ));
73
74 af_glyph_hints_init( hints, face->memory );
75
76 metrics->axis[AF_DIMENSION_HORZ].width_count = 0;
77 metrics->axis[AF_DIMENSION_VERT].width_count = 0;
78
79 {
80 FT_Error error;
81 FT_ULong glyph_index;
82 int dim;
83 AF_LatinMetricsRec dummy[1];
84 AF_Scaler scaler = &dummy->root.scaler;
85
86 #ifdef FT_CONFIG_OPTION_PIC
87 AF_FaceGlobals globals = metrics->root.globals;
88 #endif
89
90 AF_StyleClass style_class = metrics->root.style_class;
91 AF_ScriptClass script_class = AF_SCRIPT_CLASSES_GET
92 [style_class->script];
93
94 void* shaper_buf;
95 const char* p;
96
97 #ifdef FT_DEBUG_LEVEL_TRACE
98 FT_ULong ch = 0;
99 #endif
100
101 p = script_class->standard_charstring;
102 shaper_buf = af_shaper_buf_create( face );
103
104 /*
105 * We check a list of standard characters to catch features like
106 * `c2sc' (small caps from caps) that don't contain lowercase letters
107 * by definition, or other features that mainly operate on numerals.
108 * The first match wins.
109 */
110
111 glyph_index = 0;
112 while ( *p )
113 {
114 unsigned int num_idx;
115
116 #ifdef FT_DEBUG_LEVEL_TRACE
117 const char* p_old;
118 #endif
119
120
121 while ( *p == ' ' )
122 p++;
123
312 FT_Face face )
313 {
314 FT_Pos flats [AF_BLUE_STRING_MAX_LEN];
315 FT_Pos rounds[AF_BLUE_STRING_MAX_LEN];
316
317 FT_UInt num_flats;
318 FT_UInt num_rounds;
319
320 AF_LatinBlue blue;
321 FT_Error error;
322 AF_LatinAxis axis = &metrics->axis[AF_DIMENSION_VERT];
323 FT_Outline outline;
324
325 AF_StyleClass sc = metrics->root.style_class;
326
327 AF_Blue_Stringset bss = sc->blue_stringset;
328 const AF_Blue_StringRec* bs = &af_blue_stringsets[bss];
329
330 FT_Pos flat_threshold = FLAT_THRESHOLD( metrics->units_per_em );
331
332 void* shaper_buf;
333
334
335 /* we walk over the blue character strings as specified in the */
336 /* style's entry in the `af_blue_stringset' array */
337
338 FT_TRACE5(( "latin blue zones computation\n"
339 "============================\n"
340 "\n" ));
341
342 shaper_buf = af_shaper_buf_create( face );
343
344 for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
345 {
346 const char* p = &af_blue_strings[bs->string];
347 FT_Pos* blue_ref;
348 FT_Pos* blue_shoot;
349 FT_Pos ascender;
350 FT_Pos descender;
351
352
353 #ifdef FT_DEBUG_LEVEL_TRACE
354 {
355 FT_Bool have_flag = 0;
356
357
358 FT_TRACE5(( "blue zone %d", axis->blue_count ));
359
360 if ( bs->properties )
361 {
362 FT_TRACE5(( " (" ));
1019 *a ));
1020 }
1021 }
1022 }
1023
1024 FT_TRACE5(( "\n" ));
1025
1026 return;
1027 }
1028
1029
1030 /* Check whether all ASCII digits have the same advance width. */
1031
1032 FT_LOCAL_DEF( void )
1033 af_latin_metrics_check_digits( AF_LatinMetrics metrics,
1034 FT_Face face )
1035 {
1036 FT_Bool started = 0, same_width = 1;
1037 FT_Fixed advance = 0, old_advance = 0;
1038
1039 void* shaper_buf;
1040
1041 /* in all supported charmaps, digits have character codes 0x30-0x39 */
1042 const char digits[] = "0 1 2 3 4 5 6 7 8 9";
1043 const char* p;
1044
1045
1046 p = digits;
1047 shaper_buf = af_shaper_buf_create( face );
1048
1049 while ( *p )
1050 {
1051 FT_ULong glyph_index;
1052 unsigned int num_idx;
1053
1054
1055 /* reject input that maps to more than a single glyph */
1056 p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
1057 if ( num_idx > 1 )
1058 continue;
1059
1060 glyph_index = af_shaper_get_elem( &metrics->root,
1061 shaper_buf,
1062 0,
1063 &advance,
1064 NULL );
1065 if ( !glyph_index )
1066 continue;
1067
1266
1267 /* scale the widths */
1268 for ( nn = 0; nn < axis->width_count; nn++ )
1269 {
1270 AF_Width width = axis->widths + nn;
1271
1272
1273 width->cur = FT_MulFix( width->org, scale );
1274 width->fit = width->cur;
1275
1276 FT_TRACE5(( " %d scaled to %.2f\n",
1277 width->org,
1278 width->cur / 64.0 ));
1279 }
1280
1281 FT_TRACE5(( "\n" ));
1282
1283 /* an extra-light axis corresponds to a standard width that is */
1284 /* smaller than 5/8 pixels */
1285 axis->extra_light =
1286 (FT_Bool)( FT_MulFix( axis->standard_width, scale ) < 32 + 8 );
1287
1288 #ifdef FT_DEBUG_LEVEL_TRACE
1289 if ( axis->extra_light )
1290 FT_TRACE5(( "`%s' style is extra light (at current resolution)\n"
1291 "\n",
1292 af_style_names[metrics->root.style_class->style] ));
1293 #endif
1294
1295 if ( dim == AF_DIMENSION_VERT )
1296 {
1297 #ifdef FT_DEBUG_LEVEL_TRACE
1298 if ( axis->blue_count )
1299 FT_TRACE5(( "blue zones (style `%s')\n",
1300 af_style_names[metrics->root.style_class->style] ));
1301 #endif
1302
1303 /* scale the blue zones */
1304 for ( nn = 0; nn < axis->blue_count; nn++ )
1305 {
1306 AF_LatinBlue blue = &axis->blues[nn];
1954 FT_Pos max = seg1->max_coord;
1955 FT_Pos len;
1956
1957
1958 if ( min < seg2->min_coord )
1959 min = seg2->min_coord;
1960
1961 if ( max > seg2->max_coord )
1962 max = seg2->max_coord;
1963
1964 /* compute maximum coordinate difference of the two segments */
1965 /* (this is, how much they overlap) */
1966 len = max - min;
1967 if ( len >= len_threshold )
1968 {
1969 /*
1970 * The score is the sum of two demerits indicating the
1971 * `badness' of a fit, measured along the segments' main axis
1972 * and orthogonal to it, respectively.
1973 *
1974 * o The less overlapping along the main axis, the worse it
1975 * is, causing a larger demerit.
1976 *
1977 * o The nearer the orthogonal distance to a stem width, the
1978 * better it is, causing a smaller demerit. For simplicity,
1979 * however, we only increase the demerit for values that
1980 * exceed the largest stem width.
1981 */
1982
1983 FT_Pos dist = pos2 - pos1;
1984
1985 FT_Pos dist_demerit, score;
1986
1987
1988 if ( max_width )
1989 {
1990 /* distance demerits are based on multiples of `max_width'; */
1991 /* we scale by 1024 for getting more precision */
1992 FT_Pos delta = ( dist << 10 ) / max_width - ( 1 << 10 );
1993
1994
1995 if ( delta > 10000 )
1996 dist_demerit = 32000;
1997 else if ( delta > 0 )
2032 {
2033 seg1->link = 0;
2034 seg1->serif = seg2->link;
2035 }
2036 }
2037 }
2038 }
2039
2040
2041 /* Link segments to edges, using feature analysis for selection. */
2042
2043 FT_LOCAL_DEF( FT_Error )
2044 af_latin_hints_compute_edges( AF_GlyphHints hints,
2045 AF_Dimension dim )
2046 {
2047 AF_AxisHints axis = &hints->axis[dim];
2048 FT_Error error = FT_Err_Ok;
2049 FT_Memory memory = hints->memory;
2050 AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim];
2051
2052 #ifdef FT_CONFIG_OPTION_PIC
2053 AF_FaceGlobals globals = hints->metrics->globals;
2054 #endif
2055
2056 AF_StyleClass style_class = hints->metrics->style_class;
2057 AF_ScriptClass script_class = AF_SCRIPT_CLASSES_GET
2058 [style_class->script];
2059
2060 FT_Bool top_to_bottom_hinting = 0;
2061
2062 AF_Segment segments = axis->segments;
2063 AF_Segment segment_limit = segments + axis->num_segments;
2064 AF_Segment seg;
2065
2066 #if 0
2067 AF_Direction up_dir;
2068 #endif
2069 FT_Fixed scale;
2070 FT_Pos edge_distance_threshold;
2071 FT_Pos segment_length_threshold;
2072 FT_Pos segment_width_threshold;
2073
2074
2075 axis->num_edges = 0;
2076
2077 scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
2078 : hints->y_scale;
2084
2085 if ( dim == AF_DIMENSION_VERT )
2086 top_to_bottom_hinting = script_class->top_to_bottom_hinting;
2087
2088 /*
2089 * We ignore all segments that are less than 1 pixel in length
2090 * to avoid many problems with serif fonts. We compute the
2091 * corresponding threshold in font units.
2092 */
2093 if ( dim == AF_DIMENSION_HORZ )
2094 segment_length_threshold = FT_DivFix( 64, hints->y_scale );
2095 else
2096 segment_length_threshold = 0;
2097
2098 /*
2099 * Similarly, we ignore segments that have a width delta
2100 * larger than 0.5px (i.e., a width larger than 1px).
2101 */
2102 segment_width_threshold = FT_DivFix( 32, scale );
2103
2104 /*********************************************************************/
2105 /* */
2106 /* We begin by generating a sorted table of edges for the current */
2107 /* direction. To do so, we simply scan each segment and try to find */
2108 /* an edge in our table that corresponds to its position. */
2109 /* */
2110 /* If no edge is found, we create and insert a new edge in the */
2111 /* sorted table. Otherwise, we simply add the segment to the edge's */
2112 /* list which gets processed in the second step to compute the */
2113 /* edge's properties. */
2114 /* */
2115 /* Note that the table of edges is sorted along the segment/edge */
2116 /* position. */
2117 /* */
2118 /*********************************************************************/
2119
2120 /* assure that edge distance threshold is at most 0.25px */
2121 edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
2122 scale );
2123 if ( edge_distance_threshold > 64 / 4 )
2124 edge_distance_threshold = 64 / 4;
2125
2126 edge_distance_threshold = FT_DivFix( edge_distance_threshold,
2127 scale );
2128
2129 for ( seg = segments; seg < segment_limit; seg++ )
2130 {
2131 AF_Edge found = NULL;
2132 FT_Int ee;
2133
2134
2135 /* ignore too short segments, too wide ones, and, in this loop, */
2136 /* one-point segments without a direction */
2137 if ( seg->height < segment_length_threshold ||
2138 seg->delta > segment_width_threshold ||
2220 if ( dist < 0 )
2221 dist = -dist;
2222
2223 if ( dist < edge_distance_threshold )
2224 {
2225 found = edge;
2226 break;
2227 }
2228 }
2229
2230 /* one-point segments without a match are ignored */
2231 if ( found )
2232 {
2233 seg->edge_next = found->first;
2234 found->last->edge_next = seg;
2235 found->last = seg;
2236 }
2237 }
2238
2239
2240 /******************************************************************/
2241 /* */
2242 /* Good, we now compute each edge's properties according to the */
2243 /* segments found on its position. Basically, these are */
2244 /* */
2245 /* - the edge's main direction */
2246 /* - stem edge, serif edge or both (which defaults to stem then) */
2247 /* - rounded edge, straight or both (which defaults to straight) */
2248 /* - link for edge */
2249 /* */
2250 /******************************************************************/
2251
2252 /* first of all, set the `edge' field in each segment -- this is */
2253 /* required in order to compute edge links */
2254
2255 /*
2256 * Note that removing this loop and setting the `edge' field of each
2257 * segment directly in the code above slows down execution speed for
2258 * some reasons on platforms like the Sun.
2259 */
2260 {
2261 AF_Edge edges = axis->edges;
2262 AF_Edge edge_limit = edges + axis->num_edges;
2263 AF_Edge edge;
2264
2265
2266 for ( edge = edges; edge < edge_limit; edge++ )
2267 {
2268 seg = edge->first;
2269 if ( seg )
2270 do
2292 {
2293 FT_Bool is_serif;
2294
2295
2296 /* check for roundness of segment */
2297 if ( seg->flags & AF_EDGE_ROUND )
2298 is_round++;
2299 else
2300 is_straight++;
2301
2302 #if 0
2303 /* check for segment direction */
2304 if ( seg->dir == up_dir )
2305 ups += seg->max_coord - seg->min_coord;
2306 else
2307 downs += seg->max_coord - seg->min_coord;
2308 #endif
2309
2310 /* check for links -- if seg->serif is set, then seg->link must */
2311 /* be ignored */
2312 is_serif = (FT_Bool)( seg->serif &&
2313 seg->serif->edge &&
2314 seg->serif->edge != edge );
2315
2316 if ( ( seg->link && seg->link->edge ) || is_serif )
2317 {
2318 AF_Edge edge2;
2319 AF_Segment seg2;
2320
2321
2322 edge2 = edge->link;
2323 seg2 = seg->link;
2324
2325 if ( is_serif )
2326 {
2327 seg2 = seg->serif;
2328 edge2 = edge->serif;
2329 }
2330
2331 if ( edge2 )
2332 {
2919 /**** ****/
2920 /*************************************************************************/
2921 /*************************************************************************/
2922 /*************************************************************************/
2923
2924
2925 /* The main grid-fitting routine. */
2926
2927 static void
2928 af_latin_hint_edges( AF_GlyphHints hints,
2929 AF_Dimension dim )
2930 {
2931 AF_AxisHints axis = &hints->axis[dim];
2932 AF_Edge edges = axis->edges;
2933 AF_Edge edge_limit = edges + axis->num_edges;
2934 FT_PtrDist n_edges;
2935 AF_Edge edge;
2936 AF_Edge anchor = NULL;
2937 FT_Int has_serifs = 0;
2938
2939 #ifdef FT_CONFIG_OPTION_PIC
2940 AF_FaceGlobals globals = hints->metrics->globals;
2941 #endif
2942
2943 AF_StyleClass style_class = hints->metrics->style_class;
2944 AF_ScriptClass script_class = AF_SCRIPT_CLASSES_GET
2945 [style_class->script];
2946
2947 FT_Bool top_to_bottom_hinting = 0;
2948
2949 #ifdef FT_DEBUG_LEVEL_TRACE
2950 FT_UInt num_actions = 0;
2951 #endif
2952
2953
2954 FT_TRACE5(( "latin %s edge hinting (style `%s')\n",
2955 dim == AF_DIMENSION_VERT ? "horizontal" : "vertical",
2956 af_style_names[hints->metrics->style_class->style] ));
2957
2958 if ( dim == AF_DIMENSION_VERT )
2959 top_to_bottom_hinting = script_class->top_to_bottom_hinting;
2960
2961 /* we begin by aligning all stems relative to the blue zone */
2962 /* if needed -- that's only for horizontal edges */
2963
2964 if ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_BLUES( hints ) )
2965 {
|
1 /****************************************************************************
2 *
3 * aflatin.c
4 *
5 * Auto-fitter hinting routines for latin writing system (body).
6 *
7 * Copyright (C) 2003-2019 by
8 * David Turner, Robert Wilhelm, and Werner Lemberg.
9 *
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.
15 *
16 */
17
18
19 #include <ft2build.h>
20 #include FT_ADVANCES_H
21 #include FT_INTERNAL_DEBUG_H
22
23 #include "afglobal.h"
24 #include "aflatin.h"
25 #include "aferrors.h"
26
27
28 #ifdef AF_CONFIG_OPTION_USE_WARPER
29 #include "afwarp.h"
30 #endif
31
32
33 /**************************************************************************
34 *
35 * The macro FT_COMPONENT is used in trace mode. It is an implicit
36 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
37 * messages during execution.
38 */
39 #undef FT_COMPONENT
40 #define FT_COMPONENT aflatin
41
42
43 /* needed for computation of round vs. flat segments */
44 #define FLAT_THRESHOLD( x ) ( x / 14 )
45
46
47 /*************************************************************************/
48 /*************************************************************************/
49 /***** *****/
50 /***** L A T I N G L O B A L M E T R I C S *****/
51 /***** *****/
52 /*************************************************************************/
53 /*************************************************************************/
54
55
56 /* Find segments and links, compute all stem widths, and initialize */
57 /* standard width and height for the glyph with given charcode. */
58
59 FT_LOCAL_DEF( void )
60 af_latin_metrics_init_widths( AF_LatinMetrics metrics,
65
66
67 FT_TRACE5(( "\n"
68 "latin standard widths computation (style `%s')\n"
69 "=====================================================\n"
70 "\n",
71 af_style_names[metrics->root.style_class->style] ));
72
73 af_glyph_hints_init( hints, face->memory );
74
75 metrics->axis[AF_DIMENSION_HORZ].width_count = 0;
76 metrics->axis[AF_DIMENSION_VERT].width_count = 0;
77
78 {
79 FT_Error error;
80 FT_ULong glyph_index;
81 int dim;
82 AF_LatinMetricsRec dummy[1];
83 AF_Scaler scaler = &dummy->root.scaler;
84
85 AF_StyleClass style_class = metrics->root.style_class;
86 AF_ScriptClass script_class = af_script_classes[style_class->script];
87
88 /* If HarfBuzz is not available, we need a pointer to a single */
89 /* unsigned long value. */
90 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
91 void* shaper_buf;
92 #else
93 FT_ULong shaper_buf_;
94 void* shaper_buf = &shaper_buf_;
95 #endif
96
97 const char* p;
98
99 #ifdef FT_DEBUG_LEVEL_TRACE
100 FT_ULong ch = 0;
101 #endif
102
103
104 p = script_class->standard_charstring;
105
106 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
107 shaper_buf = af_shaper_buf_create( face );
108 #endif
109 /*
110 * We check a list of standard characters to catch features like
111 * `c2sc' (small caps from caps) that don't contain lowercase letters
112 * by definition, or other features that mainly operate on numerals.
113 * The first match wins.
114 */
115
116 glyph_index = 0;
117 while ( *p )
118 {
119 unsigned int num_idx;
120
121 #ifdef FT_DEBUG_LEVEL_TRACE
122 const char* p_old;
123 #endif
124
125
126 while ( *p == ' ' )
127 p++;
128
317 FT_Face face )
318 {
319 FT_Pos flats [AF_BLUE_STRING_MAX_LEN];
320 FT_Pos rounds[AF_BLUE_STRING_MAX_LEN];
321
322 FT_UInt num_flats;
323 FT_UInt num_rounds;
324
325 AF_LatinBlue blue;
326 FT_Error error;
327 AF_LatinAxis axis = &metrics->axis[AF_DIMENSION_VERT];
328 FT_Outline outline;
329
330 AF_StyleClass sc = metrics->root.style_class;
331
332 AF_Blue_Stringset bss = sc->blue_stringset;
333 const AF_Blue_StringRec* bs = &af_blue_stringsets[bss];
334
335 FT_Pos flat_threshold = FLAT_THRESHOLD( metrics->units_per_em );
336
337 /* If HarfBuzz is not available, we need a pointer to a single */
338 /* unsigned long value. */
339 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
340 void* shaper_buf;
341 #else
342 FT_ULong shaper_buf_;
343 void* shaper_buf = &shaper_buf_;
344 #endif
345
346
347 /* we walk over the blue character strings as specified in the */
348 /* style's entry in the `af_blue_stringset' array */
349
350 FT_TRACE5(( "latin blue zones computation\n"
351 "============================\n"
352 "\n" ));
353
354 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
355 shaper_buf = af_shaper_buf_create( face );
356 #endif
357
358 for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
359 {
360 const char* p = &af_blue_strings[bs->string];
361 FT_Pos* blue_ref;
362 FT_Pos* blue_shoot;
363 FT_Pos ascender;
364 FT_Pos descender;
365
366
367 #ifdef FT_DEBUG_LEVEL_TRACE
368 {
369 FT_Bool have_flag = 0;
370
371
372 FT_TRACE5(( "blue zone %d", axis->blue_count ));
373
374 if ( bs->properties )
375 {
376 FT_TRACE5(( " (" ));
1033 *a ));
1034 }
1035 }
1036 }
1037
1038 FT_TRACE5(( "\n" ));
1039
1040 return;
1041 }
1042
1043
1044 /* Check whether all ASCII digits have the same advance width. */
1045
1046 FT_LOCAL_DEF( void )
1047 af_latin_metrics_check_digits( AF_LatinMetrics metrics,
1048 FT_Face face )
1049 {
1050 FT_Bool started = 0, same_width = 1;
1051 FT_Fixed advance = 0, old_advance = 0;
1052
1053 /* If HarfBuzz is not available, we need a pointer to a single */
1054 /* unsigned long value. */
1055 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
1056 void* shaper_buf;
1057 #else
1058 FT_ULong shaper_buf_;
1059 void* shaper_buf = &shaper_buf_;
1060 #endif
1061
1062 /* in all supported charmaps, digits have character codes 0x30-0x39 */
1063 const char digits[] = "0 1 2 3 4 5 6 7 8 9";
1064 const char* p;
1065
1066
1067 p = digits;
1068
1069 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
1070 shaper_buf = af_shaper_buf_create( face );
1071 #endif
1072
1073 while ( *p )
1074 {
1075 FT_ULong glyph_index;
1076 unsigned int num_idx;
1077
1078
1079 /* reject input that maps to more than a single glyph */
1080 p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
1081 if ( num_idx > 1 )
1082 continue;
1083
1084 glyph_index = af_shaper_get_elem( &metrics->root,
1085 shaper_buf,
1086 0,
1087 &advance,
1088 NULL );
1089 if ( !glyph_index )
1090 continue;
1091
1290
1291 /* scale the widths */
1292 for ( nn = 0; nn < axis->width_count; nn++ )
1293 {
1294 AF_Width width = axis->widths + nn;
1295
1296
1297 width->cur = FT_MulFix( width->org, scale );
1298 width->fit = width->cur;
1299
1300 FT_TRACE5(( " %d scaled to %.2f\n",
1301 width->org,
1302 width->cur / 64.0 ));
1303 }
1304
1305 FT_TRACE5(( "\n" ));
1306
1307 /* an extra-light axis corresponds to a standard width that is */
1308 /* smaller than 5/8 pixels */
1309 axis->extra_light =
1310 FT_BOOL( FT_MulFix( axis->standard_width, scale ) < 32 + 8 );
1311
1312 #ifdef FT_DEBUG_LEVEL_TRACE
1313 if ( axis->extra_light )
1314 FT_TRACE5(( "`%s' style is extra light (at current resolution)\n"
1315 "\n",
1316 af_style_names[metrics->root.style_class->style] ));
1317 #endif
1318
1319 if ( dim == AF_DIMENSION_VERT )
1320 {
1321 #ifdef FT_DEBUG_LEVEL_TRACE
1322 if ( axis->blue_count )
1323 FT_TRACE5(( "blue zones (style `%s')\n",
1324 af_style_names[metrics->root.style_class->style] ));
1325 #endif
1326
1327 /* scale the blue zones */
1328 for ( nn = 0; nn < axis->blue_count; nn++ )
1329 {
1330 AF_LatinBlue blue = &axis->blues[nn];
1978 FT_Pos max = seg1->max_coord;
1979 FT_Pos len;
1980
1981
1982 if ( min < seg2->min_coord )
1983 min = seg2->min_coord;
1984
1985 if ( max > seg2->max_coord )
1986 max = seg2->max_coord;
1987
1988 /* compute maximum coordinate difference of the two segments */
1989 /* (this is, how much they overlap) */
1990 len = max - min;
1991 if ( len >= len_threshold )
1992 {
1993 /*
1994 * The score is the sum of two demerits indicating the
1995 * `badness' of a fit, measured along the segments' main axis
1996 * and orthogonal to it, respectively.
1997 *
1998 * - The less overlapping along the main axis, the worse it
1999 * is, causing a larger demerit.
2000 *
2001 * - The nearer the orthogonal distance to a stem width, the
2002 * better it is, causing a smaller demerit. For simplicity,
2003 * however, we only increase the demerit for values that
2004 * exceed the largest stem width.
2005 */
2006
2007 FT_Pos dist = pos2 - pos1;
2008
2009 FT_Pos dist_demerit, score;
2010
2011
2012 if ( max_width )
2013 {
2014 /* distance demerits are based on multiples of `max_width'; */
2015 /* we scale by 1024 for getting more precision */
2016 FT_Pos delta = ( dist << 10 ) / max_width - ( 1 << 10 );
2017
2018
2019 if ( delta > 10000 )
2020 dist_demerit = 32000;
2021 else if ( delta > 0 )
2056 {
2057 seg1->link = 0;
2058 seg1->serif = seg2->link;
2059 }
2060 }
2061 }
2062 }
2063
2064
2065 /* Link segments to edges, using feature analysis for selection. */
2066
2067 FT_LOCAL_DEF( FT_Error )
2068 af_latin_hints_compute_edges( AF_GlyphHints hints,
2069 AF_Dimension dim )
2070 {
2071 AF_AxisHints axis = &hints->axis[dim];
2072 FT_Error error = FT_Err_Ok;
2073 FT_Memory memory = hints->memory;
2074 AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim];
2075
2076 AF_StyleClass style_class = hints->metrics->style_class;
2077 AF_ScriptClass script_class = af_script_classes[style_class->script];
2078
2079 FT_Bool top_to_bottom_hinting = 0;
2080
2081 AF_Segment segments = axis->segments;
2082 AF_Segment segment_limit = segments + axis->num_segments;
2083 AF_Segment seg;
2084
2085 #if 0
2086 AF_Direction up_dir;
2087 #endif
2088 FT_Fixed scale;
2089 FT_Pos edge_distance_threshold;
2090 FT_Pos segment_length_threshold;
2091 FT_Pos segment_width_threshold;
2092
2093
2094 axis->num_edges = 0;
2095
2096 scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
2097 : hints->y_scale;
2103
2104 if ( dim == AF_DIMENSION_VERT )
2105 top_to_bottom_hinting = script_class->top_to_bottom_hinting;
2106
2107 /*
2108 * We ignore all segments that are less than 1 pixel in length
2109 * to avoid many problems with serif fonts. We compute the
2110 * corresponding threshold in font units.
2111 */
2112 if ( dim == AF_DIMENSION_HORZ )
2113 segment_length_threshold = FT_DivFix( 64, hints->y_scale );
2114 else
2115 segment_length_threshold = 0;
2116
2117 /*
2118 * Similarly, we ignore segments that have a width delta
2119 * larger than 0.5px (i.e., a width larger than 1px).
2120 */
2121 segment_width_threshold = FT_DivFix( 32, scale );
2122
2123 /**********************************************************************
2124 *
2125 * We begin by generating a sorted table of edges for the current
2126 * direction. To do so, we simply scan each segment and try to find
2127 * an edge in our table that corresponds to its position.
2128 *
2129 * If no edge is found, we create and insert a new edge in the
2130 * sorted table. Otherwise, we simply add the segment to the edge's
2131 * list which gets processed in the second step to compute the
2132 * edge's properties.
2133 *
2134 * Note that the table of edges is sorted along the segment/edge
2135 * position.
2136 *
2137 */
2138
2139 /* assure that edge distance threshold is at most 0.25px */
2140 edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
2141 scale );
2142 if ( edge_distance_threshold > 64 / 4 )
2143 edge_distance_threshold = 64 / 4;
2144
2145 edge_distance_threshold = FT_DivFix( edge_distance_threshold,
2146 scale );
2147
2148 for ( seg = segments; seg < segment_limit; seg++ )
2149 {
2150 AF_Edge found = NULL;
2151 FT_Int ee;
2152
2153
2154 /* ignore too short segments, too wide ones, and, in this loop, */
2155 /* one-point segments without a direction */
2156 if ( seg->height < segment_length_threshold ||
2157 seg->delta > segment_width_threshold ||
2239 if ( dist < 0 )
2240 dist = -dist;
2241
2242 if ( dist < edge_distance_threshold )
2243 {
2244 found = edge;
2245 break;
2246 }
2247 }
2248
2249 /* one-point segments without a match are ignored */
2250 if ( found )
2251 {
2252 seg->edge_next = found->first;
2253 found->last->edge_next = seg;
2254 found->last = seg;
2255 }
2256 }
2257
2258
2259 /*******************************************************************
2260 *
2261 * Good, we now compute each edge's properties according to the
2262 * segments found on its position. Basically, these are
2263 *
2264 * - the edge's main direction
2265 * - stem edge, serif edge or both (which defaults to stem then)
2266 * - rounded edge, straight or both (which defaults to straight)
2267 * - link for edge
2268 *
2269 */
2270
2271 /* first of all, set the `edge' field in each segment -- this is */
2272 /* required in order to compute edge links */
2273
2274 /*
2275 * Note that removing this loop and setting the `edge' field of each
2276 * segment directly in the code above slows down execution speed for
2277 * some reasons on platforms like the Sun.
2278 */
2279 {
2280 AF_Edge edges = axis->edges;
2281 AF_Edge edge_limit = edges + axis->num_edges;
2282 AF_Edge edge;
2283
2284
2285 for ( edge = edges; edge < edge_limit; edge++ )
2286 {
2287 seg = edge->first;
2288 if ( seg )
2289 do
2311 {
2312 FT_Bool is_serif;
2313
2314
2315 /* check for roundness of segment */
2316 if ( seg->flags & AF_EDGE_ROUND )
2317 is_round++;
2318 else
2319 is_straight++;
2320
2321 #if 0
2322 /* check for segment direction */
2323 if ( seg->dir == up_dir )
2324 ups += seg->max_coord - seg->min_coord;
2325 else
2326 downs += seg->max_coord - seg->min_coord;
2327 #endif
2328
2329 /* check for links -- if seg->serif is set, then seg->link must */
2330 /* be ignored */
2331 is_serif = FT_BOOL( seg->serif &&
2332 seg->serif->edge &&
2333 seg->serif->edge != edge );
2334
2335 if ( ( seg->link && seg->link->edge ) || is_serif )
2336 {
2337 AF_Edge edge2;
2338 AF_Segment seg2;
2339
2340
2341 edge2 = edge->link;
2342 seg2 = seg->link;
2343
2344 if ( is_serif )
2345 {
2346 seg2 = seg->serif;
2347 edge2 = edge->serif;
2348 }
2349
2350 if ( edge2 )
2351 {
2938 /**** ****/
2939 /*************************************************************************/
2940 /*************************************************************************/
2941 /*************************************************************************/
2942
2943
2944 /* The main grid-fitting routine. */
2945
2946 static void
2947 af_latin_hint_edges( AF_GlyphHints hints,
2948 AF_Dimension dim )
2949 {
2950 AF_AxisHints axis = &hints->axis[dim];
2951 AF_Edge edges = axis->edges;
2952 AF_Edge edge_limit = edges + axis->num_edges;
2953 FT_PtrDist n_edges;
2954 AF_Edge edge;
2955 AF_Edge anchor = NULL;
2956 FT_Int has_serifs = 0;
2957
2958 AF_StyleClass style_class = hints->metrics->style_class;
2959 AF_ScriptClass script_class = af_script_classes[style_class->script];
2960
2961 FT_Bool top_to_bottom_hinting = 0;
2962
2963 #ifdef FT_DEBUG_LEVEL_TRACE
2964 FT_UInt num_actions = 0;
2965 #endif
2966
2967
2968 FT_TRACE5(( "latin %s edge hinting (style `%s')\n",
2969 dim == AF_DIMENSION_VERT ? "horizontal" : "vertical",
2970 af_style_names[hints->metrics->style_class->style] ));
2971
2972 if ( dim == AF_DIMENSION_VERT )
2973 top_to_bottom_hinting = script_class->top_to_bottom_hinting;
2974
2975 /* we begin by aligning all stems relative to the blue zone */
2976 /* if needed -- that's only for horizontal edges */
2977
2978 if ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_BLUES( hints ) )
2979 {
|