1 /***************************************************************************/
2 /* */
3 /* afcjk.c */
4 /* */
5 /* Auto-fitter hinting routines for CJK writing system (body). */
6 /* */
7 /* Copyright 2006-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 * The algorithm is based on akito's autohint patch, archived at
20 *
21 * https://web.archive.org/web/20051219160454/http://www.kde.gr.jp:80/~akito/patch/freetype2/2.1.7/
22 *
23 */
24
25 #include <ft2build.h>
26 #include FT_ADVANCES_H
27 #include FT_INTERNAL_DEBUG_H
28
29 #include "afglobal.h"
30 #include "afpic.h"
31 #include "aflatin.h"
32 #include "afcjk.h"
33
34
35 #ifdef AF_CONFIG_OPTION_CJK
36
37 #undef AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT
38
39 #include "aferrors.h"
40
41
42 #ifdef AF_CONFIG_OPTION_USE_WARPER
43 #include "afwarp.h"
44 #endif
45
46
47 /*************************************************************************/
48 /* */
49 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
50 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
51 /* messages during execution. */
52 /* */
53 #undef FT_COMPONENT
54 #define FT_COMPONENT trace_afcjk
55
56
57 /*************************************************************************/
58 /*************************************************************************/
59 /***** *****/
60 /***** C J K G L O B A L M E T R I C S *****/
61 /***** *****/
62 /*************************************************************************/
63 /*************************************************************************/
64
65
66 /* Basically the Latin version with AF_CJKMetrics */
67 /* to replace AF_LatinMetrics. */
68
69 FT_LOCAL_DEF( void )
70 af_cjk_metrics_init_widths( AF_CJKMetrics metrics,
71 FT_Face face )
72 {
73 /* scan the array of segments in each direction */
74 AF_GlyphHintsRec hints[1];
75
76
77 FT_TRACE5(( "\n"
78 "cjk standard widths computation (style `%s')\n"
79 "===================================================\n"
80 "\n",
81 af_style_names[metrics->root.style_class->style] ));
82
83 af_glyph_hints_init( hints, face->memory );
84
85 metrics->axis[AF_DIMENSION_HORZ].width_count = 0;
86 metrics->axis[AF_DIMENSION_VERT].width_count = 0;
87
88 {
89 FT_Error error;
90 FT_ULong glyph_index;
91 int dim;
92 AF_CJKMetricsRec dummy[1];
93 AF_Scaler scaler = &dummy->root.scaler;
94
95 #ifdef FT_CONFIG_OPTION_PIC
96 AF_FaceGlobals globals = metrics->root.globals;
97 #endif
98
99 AF_StyleClass style_class = metrics->root.style_class;
100 AF_ScriptClass script_class = AF_SCRIPT_CLASSES_GET
101 [style_class->script];
102
103 void* shaper_buf;
104 const char* p;
105
106 #ifdef FT_DEBUG_LEVEL_TRACE
107 FT_ULong ch = 0;
108 #endif
109
110 p = script_class->standard_charstring;
111 shaper_buf = af_shaper_buf_create( face );
112
113 /* We check a list of standard characters. The first match wins. */
114
115 glyph_index = 0;
116 while ( *p )
117 {
118 unsigned int num_idx;
119
120 #ifdef FT_DEBUG_LEVEL_TRACE
121 const char* p_old;
122 #endif
123
124
125 while ( *p == ' ' )
126 p++;
127
128 #ifdef FT_DEBUG_LEVEL_TRACE
129 p_old = p;
130 GET_UTF8_CHAR( ch, p_old );
131 #endif
279 FT_Face face )
280 {
281 FT_Pos fills[AF_BLUE_STRING_MAX_LEN];
282 FT_Pos flats[AF_BLUE_STRING_MAX_LEN];
283
284 FT_UInt num_fills;
285 FT_UInt num_flats;
286
287 FT_Bool fill;
288
289 AF_CJKBlue blue;
290 FT_Error error;
291 AF_CJKAxis axis;
292 FT_Outline outline;
293
294 AF_StyleClass sc = metrics->root.style_class;
295
296 AF_Blue_Stringset bss = sc->blue_stringset;
297 const AF_Blue_StringRec* bs = &af_blue_stringsets[bss];
298
299 void* shaper_buf;
300
301
302 /* we walk over the blue character strings as specified in the */
303 /* style's entry in the `af_blue_stringset' array, computing its */
304 /* extremum points (depending on the string properties) */
305
306 FT_TRACE5(( "cjk blue zones computation\n"
307 "==========================\n"
308 "\n" ));
309
310 shaper_buf = af_shaper_buf_create( face );
311
312 for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
313 {
314 const char* p = &af_blue_strings[bs->string];
315 FT_Pos* blue_ref;
316 FT_Pos* blue_shoot;
317
318
319 if ( AF_CJK_IS_HORIZ_BLUE( bs ) )
320 axis = &metrics->axis[AF_DIMENSION_HORZ];
321 else
322 axis = &metrics->axis[AF_DIMENSION_VERT];
323
324 #ifdef FT_DEBUG_LEVEL_TRACE
325 {
326 FT_String* cjk_blue_name[4] =
327 {
328 (FT_String*)"bottom", /* -- , -- */
329 (FT_String*)"top", /* -- , TOP */
330 (FT_String*)"left", /* HORIZ, -- */
548
549 } /* end for loop */
550
551 af_shaper_buf_destroy( face, shaper_buf );
552
553 FT_TRACE5(( "\n" ));
554
555 return;
556 }
557
558
559 /* Basically the Latin version with type AF_CJKMetrics for metrics. */
560
561 FT_LOCAL_DEF( void )
562 af_cjk_metrics_check_digits( AF_CJKMetrics metrics,
563 FT_Face face )
564 {
565 FT_Bool started = 0, same_width = 1;
566 FT_Fixed advance = 0, old_advance = 0;
567
568 void* shaper_buf;
569
570 /* in all supported charmaps, digits have character codes 0x30-0x39 */
571 const char digits[] = "0 1 2 3 4 5 6 7 8 9";
572 const char* p;
573
574
575 p = digits;
576 shaper_buf = af_shaper_buf_create( face );
577
578 while ( *p )
579 {
580 FT_ULong glyph_index;
581 unsigned int num_idx;
582
583
584 /* reject input that maps to more than a single glyph */
585 p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
586 if ( num_idx > 1 )
587 continue;
588
589 glyph_index = af_shaper_get_elem( &metrics->root,
590 shaper_buf,
591 0,
592 &advance,
593 NULL );
594 if ( !glyph_index )
595 continue;
596
983 AF_Dimension dim )
984 {
985 AF_AxisHints axis = &hints->axis[dim];
986 FT_Error error = FT_Err_Ok;
987 FT_Memory memory = hints->memory;
988 AF_CJKAxis laxis = &((AF_CJKMetrics)hints->metrics)->axis[dim];
989
990 AF_Segment segments = axis->segments;
991 AF_Segment segment_limit = segments + axis->num_segments;
992 AF_Segment seg;
993
994 FT_Fixed scale;
995 FT_Pos edge_distance_threshold;
996
997
998 axis->num_edges = 0;
999
1000 scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
1001 : hints->y_scale;
1002
1003 /*********************************************************************/
1004 /* */
1005 /* We begin by generating a sorted table of edges for the current */
1006 /* direction. To do so, we simply scan each segment and try to find */
1007 /* an edge in our table that corresponds to its position. */
1008 /* */
1009 /* If no edge is found, we create and insert a new edge in the */
1010 /* sorted table. Otherwise, we simply add the segment to the edge's */
1011 /* list which is then processed in the second step to compute the */
1012 /* edge's properties. */
1013 /* */
1014 /* Note that the edges table is sorted along the segment/edge */
1015 /* position. */
1016 /* */
1017 /*********************************************************************/
1018
1019 edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
1020 scale );
1021 if ( edge_distance_threshold > 64 / 4 )
1022 edge_distance_threshold = FT_DivFix( 64 / 4, scale );
1023 else
1024 edge_distance_threshold = laxis->edge_distance_threshold;
1025
1026 for ( seg = segments; seg < segment_limit; seg++ )
1027 {
1028 AF_Edge found = NULL;
1029 FT_Pos best = 0xFFFFU;
1030 FT_Int ee;
1031
1032
1033 /* look for an edge corresponding to the segment */
1034 for ( ee = 0; ee < axis->num_edges; ee++ )
1035 {
1036 AF_Edge edge = axis->edges + ee;
1037 FT_Pos dist;
1097 FT_ZERO( edge );
1098
1099 edge->first = seg;
1100 edge->last = seg;
1101 edge->dir = seg->dir;
1102 edge->fpos = seg->pos;
1103 edge->opos = FT_MulFix( seg->pos, scale );
1104 edge->pos = edge->opos;
1105 seg->edge_next = seg;
1106 }
1107 else
1108 {
1109 /* if an edge was found, simply add the segment to the edge's */
1110 /* list */
1111 seg->edge_next = found->first;
1112 found->last->edge_next = seg;
1113 found->last = seg;
1114 }
1115 }
1116
1117 /******************************************************************/
1118 /* */
1119 /* Good, we now compute each edge's properties according to the */
1120 /* segments found on its position. Basically, these are */
1121 /* */
1122 /* - the edge's main direction */
1123 /* - stem edge, serif edge or both (which defaults to stem then) */
1124 /* - rounded edge, straight or both (which defaults to straight) */
1125 /* - link for edge */
1126 /* */
1127 /******************************************************************/
1128
1129 /* first of all, set the `edge' field in each segment -- this is */
1130 /* required in order to compute edge links */
1131
1132 /*
1133 * Note that removing this loop and setting the `edge' field of each
1134 * segment directly in the code above slows down execution speed for
1135 * some reasons on platforms like the Sun.
1136 */
1137 {
1138 AF_Edge edges = axis->edges;
1139 AF_Edge edge_limit = edges + axis->num_edges;
1140 AF_Edge edge;
1141
1142
1143 for ( edge = edges; edge < edge_limit; edge++ )
1144 {
1145 seg = edge->first;
1146 if ( seg )
1147 do
1157 {
1158 FT_Int is_round = 0; /* does it contain round segments? */
1159 FT_Int is_straight = 0; /* does it contain straight segments? */
1160
1161
1162 seg = edge->first;
1163
1164 do
1165 {
1166 FT_Bool is_serif;
1167
1168
1169 /* check for roundness of segment */
1170 if ( seg->flags & AF_EDGE_ROUND )
1171 is_round++;
1172 else
1173 is_straight++;
1174
1175 /* check for links -- if seg->serif is set, then seg->link must */
1176 /* be ignored */
1177 is_serif = (FT_Bool)( seg->serif && seg->serif->edge != edge );
1178
1179 if ( seg->link || is_serif )
1180 {
1181 AF_Edge edge2;
1182 AF_Segment seg2;
1183
1184
1185 edge2 = edge->link;
1186 seg2 = seg->link;
1187
1188 if ( is_serif )
1189 {
1190 seg2 = seg->serif;
1191 edge2 = edge->serif;
1192 }
1193
1194 if ( edge2 )
1195 {
1196 FT_Pos edge_delta;
1197 FT_Pos seg_delta;
|
1 /****************************************************************************
2 *
3 * afcjk.c
4 *
5 * Auto-fitter hinting routines for CJK writing system (body).
6 *
7 * Copyright (C) 2006-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 * The algorithm is based on akito's autohint patch, archived at
20 *
21 * https://web.archive.org/web/20051219160454/http://www.kde.gr.jp:80/~akito/patch/freetype2/2.1.7/
22 *
23 */
24
25 #include <ft2build.h>
26 #include FT_ADVANCES_H
27 #include FT_INTERNAL_DEBUG_H
28
29 #include "afglobal.h"
30 #include "aflatin.h"
31 #include "afcjk.h"
32
33
34 #ifdef AF_CONFIG_OPTION_CJK
35
36 #undef AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT
37
38 #include "aferrors.h"
39
40
41 #ifdef AF_CONFIG_OPTION_USE_WARPER
42 #include "afwarp.h"
43 #endif
44
45
46 /**************************************************************************
47 *
48 * The macro FT_COMPONENT is used in trace mode. It is an implicit
49 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
50 * messages during execution.
51 */
52 #undef FT_COMPONENT
53 #define FT_COMPONENT afcjk
54
55
56 /*************************************************************************/
57 /*************************************************************************/
58 /***** *****/
59 /***** C J K G L O B A L M E T R I C S *****/
60 /***** *****/
61 /*************************************************************************/
62 /*************************************************************************/
63
64
65 /* Basically the Latin version with AF_CJKMetrics */
66 /* to replace AF_LatinMetrics. */
67
68 FT_LOCAL_DEF( void )
69 af_cjk_metrics_init_widths( AF_CJKMetrics metrics,
70 FT_Face face )
71 {
72 /* scan the array of segments in each direction */
73 AF_GlyphHintsRec hints[1];
74
75
76 FT_TRACE5(( "\n"
77 "cjk standard widths computation (style `%s')\n"
78 "===================================================\n"
79 "\n",
80 af_style_names[metrics->root.style_class->style] ));
81
82 af_glyph_hints_init( hints, face->memory );
83
84 metrics->axis[AF_DIMENSION_HORZ].width_count = 0;
85 metrics->axis[AF_DIMENSION_VERT].width_count = 0;
86
87 {
88 FT_Error error;
89 FT_ULong glyph_index;
90 int dim;
91 AF_CJKMetricsRec dummy[1];
92 AF_Scaler scaler = &dummy->root.scaler;
93
94 AF_StyleClass style_class = metrics->root.style_class;
95 AF_ScriptClass script_class = af_script_classes[style_class->script];
96
97 /* If HarfBuzz is not available, we need a pointer to a single */
98 /* unsigned long value. */
99 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
100 void* shaper_buf;
101 #else
102 FT_ULong shaper_buf_;
103 void* shaper_buf = &shaper_buf_;
104 #endif
105
106 const char* p;
107
108 #ifdef FT_DEBUG_LEVEL_TRACE
109 FT_ULong ch = 0;
110 #endif
111
112 p = script_class->standard_charstring;
113
114 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
115 shaper_buf = af_shaper_buf_create( face );
116 #endif
117
118 /* We check a list of standard characters. The first match wins. */
119
120 glyph_index = 0;
121 while ( *p )
122 {
123 unsigned int num_idx;
124
125 #ifdef FT_DEBUG_LEVEL_TRACE
126 const char* p_old;
127 #endif
128
129
130 while ( *p == ' ' )
131 p++;
132
133 #ifdef FT_DEBUG_LEVEL_TRACE
134 p_old = p;
135 GET_UTF8_CHAR( ch, p_old );
136 #endif
284 FT_Face face )
285 {
286 FT_Pos fills[AF_BLUE_STRING_MAX_LEN];
287 FT_Pos flats[AF_BLUE_STRING_MAX_LEN];
288
289 FT_UInt num_fills;
290 FT_UInt num_flats;
291
292 FT_Bool fill;
293
294 AF_CJKBlue blue;
295 FT_Error error;
296 AF_CJKAxis axis;
297 FT_Outline outline;
298
299 AF_StyleClass sc = metrics->root.style_class;
300
301 AF_Blue_Stringset bss = sc->blue_stringset;
302 const AF_Blue_StringRec* bs = &af_blue_stringsets[bss];
303
304 /* If HarfBuzz is not available, we need a pointer to a single */
305 /* unsigned long value. */
306 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
307 void* shaper_buf;
308 #else
309 FT_ULong shaper_buf_;
310 void* shaper_buf = &shaper_buf_;
311 #endif
312
313
314 /* we walk over the blue character strings as specified in the */
315 /* style's entry in the `af_blue_stringset' array, computing its */
316 /* extremum points (depending on the string properties) */
317
318 FT_TRACE5(( "cjk blue zones computation\n"
319 "==========================\n"
320 "\n" ));
321
322 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
323 shaper_buf = af_shaper_buf_create( face );
324 #endif
325
326 for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
327 {
328 const char* p = &af_blue_strings[bs->string];
329 FT_Pos* blue_ref;
330 FT_Pos* blue_shoot;
331
332
333 if ( AF_CJK_IS_HORIZ_BLUE( bs ) )
334 axis = &metrics->axis[AF_DIMENSION_HORZ];
335 else
336 axis = &metrics->axis[AF_DIMENSION_VERT];
337
338 #ifdef FT_DEBUG_LEVEL_TRACE
339 {
340 FT_String* cjk_blue_name[4] =
341 {
342 (FT_String*)"bottom", /* -- , -- */
343 (FT_String*)"top", /* -- , TOP */
344 (FT_String*)"left", /* HORIZ, -- */
562
563 } /* end for loop */
564
565 af_shaper_buf_destroy( face, shaper_buf );
566
567 FT_TRACE5(( "\n" ));
568
569 return;
570 }
571
572
573 /* Basically the Latin version with type AF_CJKMetrics for metrics. */
574
575 FT_LOCAL_DEF( void )
576 af_cjk_metrics_check_digits( AF_CJKMetrics metrics,
577 FT_Face face )
578 {
579 FT_Bool started = 0, same_width = 1;
580 FT_Fixed advance = 0, old_advance = 0;
581
582 /* If HarfBuzz is not available, we need a pointer to a single */
583 /* unsigned long value. */
584 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
585 void* shaper_buf;
586 #else
587 FT_ULong shaper_buf_;
588 void* shaper_buf = &shaper_buf_;
589 #endif
590
591 /* in all supported charmaps, digits have character codes 0x30-0x39 */
592 const char digits[] = "0 1 2 3 4 5 6 7 8 9";
593 const char* p;
594
595
596 p = digits;
597
598 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
599 shaper_buf = af_shaper_buf_create( face );
600 #endif
601
602 while ( *p )
603 {
604 FT_ULong glyph_index;
605 unsigned int num_idx;
606
607
608 /* reject input that maps to more than a single glyph */
609 p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
610 if ( num_idx > 1 )
611 continue;
612
613 glyph_index = af_shaper_get_elem( &metrics->root,
614 shaper_buf,
615 0,
616 &advance,
617 NULL );
618 if ( !glyph_index )
619 continue;
620
1007 AF_Dimension dim )
1008 {
1009 AF_AxisHints axis = &hints->axis[dim];
1010 FT_Error error = FT_Err_Ok;
1011 FT_Memory memory = hints->memory;
1012 AF_CJKAxis laxis = &((AF_CJKMetrics)hints->metrics)->axis[dim];
1013
1014 AF_Segment segments = axis->segments;
1015 AF_Segment segment_limit = segments + axis->num_segments;
1016 AF_Segment seg;
1017
1018 FT_Fixed scale;
1019 FT_Pos edge_distance_threshold;
1020
1021
1022 axis->num_edges = 0;
1023
1024 scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
1025 : hints->y_scale;
1026
1027 /**********************************************************************
1028 *
1029 * We begin by generating a sorted table of edges for the current
1030 * direction. To do so, we simply scan each segment and try to find
1031 * an edge in our table that corresponds to its position.
1032 *
1033 * If no edge is found, we create and insert a new edge in the
1034 * sorted table. Otherwise, we simply add the segment to the edge's
1035 * list which is then processed in the second step to compute the
1036 * edge's properties.
1037 *
1038 * Note that the edges table is sorted along the segment/edge
1039 * position.
1040 *
1041 */
1042
1043 edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
1044 scale );
1045 if ( edge_distance_threshold > 64 / 4 )
1046 edge_distance_threshold = FT_DivFix( 64 / 4, scale );
1047 else
1048 edge_distance_threshold = laxis->edge_distance_threshold;
1049
1050 for ( seg = segments; seg < segment_limit; seg++ )
1051 {
1052 AF_Edge found = NULL;
1053 FT_Pos best = 0xFFFFU;
1054 FT_Int ee;
1055
1056
1057 /* look for an edge corresponding to the segment */
1058 for ( ee = 0; ee < axis->num_edges; ee++ )
1059 {
1060 AF_Edge edge = axis->edges + ee;
1061 FT_Pos dist;
1121 FT_ZERO( edge );
1122
1123 edge->first = seg;
1124 edge->last = seg;
1125 edge->dir = seg->dir;
1126 edge->fpos = seg->pos;
1127 edge->opos = FT_MulFix( seg->pos, scale );
1128 edge->pos = edge->opos;
1129 seg->edge_next = seg;
1130 }
1131 else
1132 {
1133 /* if an edge was found, simply add the segment to the edge's */
1134 /* list */
1135 seg->edge_next = found->first;
1136 found->last->edge_next = seg;
1137 found->last = seg;
1138 }
1139 }
1140
1141 /*******************************************************************
1142 *
1143 * Good, we now compute each edge's properties according to the
1144 * segments found on its position. Basically, these are
1145 *
1146 * - the edge's main direction
1147 * - stem edge, serif edge or both (which defaults to stem then)
1148 * - rounded edge, straight or both (which defaults to straight)
1149 * - link for edge
1150 *
1151 */
1152
1153 /* first of all, set the `edge' field in each segment -- this is */
1154 /* required in order to compute edge links */
1155
1156 /*
1157 * Note that removing this loop and setting the `edge' field of each
1158 * segment directly in the code above slows down execution speed for
1159 * some reasons on platforms like the Sun.
1160 */
1161 {
1162 AF_Edge edges = axis->edges;
1163 AF_Edge edge_limit = edges + axis->num_edges;
1164 AF_Edge edge;
1165
1166
1167 for ( edge = edges; edge < edge_limit; edge++ )
1168 {
1169 seg = edge->first;
1170 if ( seg )
1171 do
1181 {
1182 FT_Int is_round = 0; /* does it contain round segments? */
1183 FT_Int is_straight = 0; /* does it contain straight segments? */
1184
1185
1186 seg = edge->first;
1187
1188 do
1189 {
1190 FT_Bool is_serif;
1191
1192
1193 /* check for roundness of segment */
1194 if ( seg->flags & AF_EDGE_ROUND )
1195 is_round++;
1196 else
1197 is_straight++;
1198
1199 /* check for links -- if seg->serif is set, then seg->link must */
1200 /* be ignored */
1201 is_serif = FT_BOOL( seg->serif && seg->serif->edge != edge );
1202
1203 if ( seg->link || is_serif )
1204 {
1205 AF_Edge edge2;
1206 AF_Segment seg2;
1207
1208
1209 edge2 = edge->link;
1210 seg2 = seg->link;
1211
1212 if ( is_serif )
1213 {
1214 seg2 = seg->serif;
1215 edge2 = edge->serif;
1216 }
1217
1218 if ( edge2 )
1219 {
1220 FT_Pos edge_delta;
1221 FT_Pos seg_delta;
|