1 | /**************************************************************************** |
2 | * |
3 | * afglobal.c |
4 | * |
5 | * Auto-fitter routines to compute global hinting values (body). |
6 | * |
7 | * Copyright (C) 2003-2023 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 "afglobal.h" |
20 | #include "afranges.h" |
21 | #include "afshaper.h" |
22 | #include "afws-decl.h" |
23 | #include <freetype/internal/ftdebug.h> |
24 | |
25 | |
26 | /************************************************************************** |
27 | * |
28 | * The macro FT_COMPONENT is used in trace mode. It is an implicit |
29 | * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log |
30 | * messages during execution. |
31 | */ |
32 | #undef FT_COMPONENT |
33 | #define FT_COMPONENT afglobal |
34 | |
35 | |
36 | #include "aferrors.h" |
37 | |
38 | |
39 | #undef SCRIPT |
40 | #define SCRIPT( s, S, d, h, H, ss ) \ |
41 | AF_DEFINE_SCRIPT_CLASS( \ |
42 | af_ ## s ## _script_class, \ |
43 | AF_SCRIPT_ ## S, \ |
44 | af_ ## s ## _uniranges, \ |
45 | af_ ## s ## _nonbase_uniranges, \ |
46 | AF_ ## H, \ |
47 | ss ) |
48 | |
49 | #include "afscript.h" |
50 | |
51 | |
52 | #undef STYLE |
53 | #define STYLE( s, S, d, ws, sc, ss, c ) \ |
54 | AF_DEFINE_STYLE_CLASS( \ |
55 | af_ ## s ## _style_class, \ |
56 | AF_STYLE_ ## S, \ |
57 | ws, \ |
58 | sc, \ |
59 | ss, \ |
60 | c ) |
61 | |
62 | #include "afstyles.h" |
63 | |
64 | |
65 | #undef WRITING_SYSTEM |
66 | #define WRITING_SYSTEM( ws, WS ) \ |
67 | &af_ ## ws ## _writing_system_class, |
68 | |
69 | FT_LOCAL_ARRAY_DEF( AF_WritingSystemClass ) |
70 | af_writing_system_classes[] = |
71 | { |
72 | |
73 | #include "afws-iter.h" |
74 | |
75 | NULL /* do not remove */ |
76 | }; |
77 | |
78 | |
79 | #undef SCRIPT |
80 | #define SCRIPT( s, S, d, h, H, ss ) \ |
81 | &af_ ## s ## _script_class, |
82 | |
83 | FT_LOCAL_ARRAY_DEF( AF_ScriptClass ) |
84 | af_script_classes[] = |
85 | { |
86 | |
87 | #include "afscript.h" |
88 | |
89 | NULL /* do not remove */ |
90 | }; |
91 | |
92 | |
93 | #undef STYLE |
94 | #define STYLE( s, S, d, ws, sc, ss, c ) \ |
95 | &af_ ## s ## _style_class, |
96 | |
97 | FT_LOCAL_ARRAY_DEF( AF_StyleClass ) |
98 | af_style_classes[] = |
99 | { |
100 | |
101 | #include "afstyles.h" |
102 | |
103 | NULL /* do not remove */ |
104 | }; |
105 | |
106 | |
107 | #ifdef FT_DEBUG_LEVEL_TRACE |
108 | |
109 | #undef STYLE |
110 | #define STYLE( s, S, d, ws, sc, ss, c ) #s, |
111 | |
112 | FT_LOCAL_ARRAY_DEF( char* ) |
113 | af_style_names[] = |
114 | { |
115 | |
116 | #include "afstyles.h" |
117 | |
118 | }; |
119 | |
120 | #endif /* FT_DEBUG_LEVEL_TRACE */ |
121 | |
122 | |
123 | /* Compute the style index of each glyph within a given face. */ |
124 | |
125 | static FT_Error |
126 | af_face_globals_compute_style_coverage( AF_FaceGlobals globals ) |
127 | { |
128 | FT_Error error; |
129 | FT_Face face = globals->face; |
130 | FT_CharMap old_charmap = face->charmap; |
131 | FT_UShort* gstyles = globals->glyph_styles; |
132 | FT_UShort ss; |
133 | FT_UShort dflt = 0xFFFFU; /* a non-valid value */ |
134 | FT_UInt i; |
135 | |
136 | |
137 | /* the value AF_STYLE_UNASSIGNED means `uncovered glyph' */ |
138 | for ( i = 0; i < globals->glyph_count; i++ ) |
139 | gstyles[i] = AF_STYLE_UNASSIGNED; |
140 | |
141 | error = FT_Select_Charmap( face, FT_ENCODING_UNICODE ); |
142 | if ( error ) |
143 | { |
144 | /* |
145 | * Ignore this error; we simply use the fallback style. |
146 | * XXX: Shouldn't we rather disable hinting? |
147 | */ |
148 | error = FT_Err_Ok; |
149 | goto Exit; |
150 | } |
151 | |
152 | /* scan each style in a Unicode charmap */ |
153 | for ( ss = 0; af_style_classes[ss]; ss++ ) |
154 | { |
155 | AF_StyleClass style_class = |
156 | af_style_classes[ss]; |
157 | AF_ScriptClass script_class = |
158 | af_script_classes[style_class->script]; |
159 | AF_Script_UniRange range; |
160 | |
161 | |
162 | if ( !script_class->script_uni_ranges ) |
163 | continue; |
164 | |
165 | /* |
166 | * Scan all Unicode points in the range and set the corresponding |
167 | * glyph style index. |
168 | */ |
169 | if ( style_class->coverage == AF_COVERAGE_DEFAULT ) |
170 | { |
171 | if ( style_class->script == globals->module->default_script ) |
172 | dflt = ss; |
173 | |
174 | for ( range = script_class->script_uni_ranges; |
175 | range->first != 0; |
176 | range++ ) |
177 | { |
178 | FT_ULong charcode = range->first; |
179 | FT_UInt gindex; |
180 | |
181 | |
182 | gindex = FT_Get_Char_Index( face, charcode ); |
183 | |
184 | if ( gindex != 0 && |
185 | gindex < globals->glyph_count && |
186 | ( gstyles[gindex] & AF_STYLE_MASK ) == AF_STYLE_UNASSIGNED ) |
187 | gstyles[gindex] = ss; |
188 | |
189 | for (;;) |
190 | { |
191 | charcode = FT_Get_Next_Char( face, charcode, &gindex ); |
192 | |
193 | if ( gindex == 0 || charcode > range->last ) |
194 | break; |
195 | |
196 | if ( gindex < globals->glyph_count && |
197 | ( gstyles[gindex] & AF_STYLE_MASK ) == AF_STYLE_UNASSIGNED ) |
198 | gstyles[gindex] = ss; |
199 | } |
200 | } |
201 | |
202 | /* do the same for the script's non-base characters */ |
203 | for ( range = script_class->script_uni_nonbase_ranges; |
204 | range->first != 0; |
205 | range++ ) |
206 | { |
207 | FT_ULong charcode = range->first; |
208 | FT_UInt gindex; |
209 | |
210 | |
211 | gindex = FT_Get_Char_Index( face, charcode ); |
212 | |
213 | if ( gindex != 0 && |
214 | gindex < globals->glyph_count && |
215 | ( gstyles[gindex] & AF_STYLE_MASK ) == ss ) |
216 | gstyles[gindex] |= AF_NONBASE; |
217 | |
218 | for (;;) |
219 | { |
220 | charcode = FT_Get_Next_Char( face, charcode, &gindex ); |
221 | |
222 | if ( gindex == 0 || charcode > range->last ) |
223 | break; |
224 | |
225 | if ( gindex < globals->glyph_count && |
226 | ( gstyles[gindex] & AF_STYLE_MASK ) == ss ) |
227 | gstyles[gindex] |= AF_NONBASE; |
228 | } |
229 | } |
230 | } |
231 | else |
232 | { |
233 | /* get glyphs not directly addressable by cmap */ |
234 | af_shaper_get_coverage( globals, style_class, gstyles, 0 ); |
235 | } |
236 | } |
237 | |
238 | /* handle the remaining default OpenType features ... */ |
239 | for ( ss = 0; af_style_classes[ss]; ss++ ) |
240 | { |
241 | AF_StyleClass style_class = af_style_classes[ss]; |
242 | |
243 | |
244 | if ( style_class->coverage == AF_COVERAGE_DEFAULT ) |
245 | af_shaper_get_coverage( globals, style_class, gstyles, 0 ); |
246 | } |
247 | |
248 | /* ... and finally the default OpenType features of the default script */ |
249 | af_shaper_get_coverage( globals, af_style_classes[dflt], gstyles, 1 ); |
250 | |
251 | /* mark ASCII digits */ |
252 | for ( i = 0x30; i <= 0x39; i++ ) |
253 | { |
254 | FT_UInt gindex = FT_Get_Char_Index( face, i ); |
255 | |
256 | |
257 | if ( gindex != 0 && gindex < globals->glyph_count ) |
258 | gstyles[gindex] |= AF_DIGIT; |
259 | } |
260 | |
261 | Exit: |
262 | /* |
263 | * By default, all uncovered glyphs are set to the fallback style. |
264 | * XXX: Shouldn't we disable hinting or do something similar? |
265 | */ |
266 | if ( globals->module->fallback_style != AF_STYLE_UNASSIGNED ) |
267 | { |
268 | FT_UInt nn; |
269 | |
270 | |
271 | for ( nn = 0; nn < globals->glyph_count; nn++ ) |
272 | { |
273 | if ( ( gstyles[nn] & AF_STYLE_MASK ) == AF_STYLE_UNASSIGNED ) |
274 | { |
275 | gstyles[nn] &= ~AF_STYLE_MASK; |
276 | gstyles[nn] |= globals->module->fallback_style; |
277 | } |
278 | } |
279 | } |
280 | |
281 | #ifdef FT_DEBUG_LEVEL_TRACE |
282 | |
283 | FT_TRACE4(( "\n" )); |
284 | FT_TRACE4(( "style coverage\n" )); |
285 | FT_TRACE4(( "==============\n" )); |
286 | FT_TRACE4(( "\n" )); |
287 | |
288 | for ( ss = 0; af_style_classes[ss]; ss++ ) |
289 | { |
290 | AF_StyleClass style_class = af_style_classes[ss]; |
291 | FT_UInt count = 0; |
292 | FT_UInt idx; |
293 | |
294 | |
295 | FT_TRACE4(( "%s:\n" , af_style_names[style_class->style] )); |
296 | |
297 | for ( idx = 0; idx < globals->glyph_count; idx++ ) |
298 | { |
299 | if ( ( gstyles[idx] & AF_STYLE_MASK ) == style_class->style ) |
300 | { |
301 | if ( !( count % 10 ) ) |
302 | FT_TRACE4(( " " )); |
303 | |
304 | FT_TRACE4(( " %d" , idx )); |
305 | count++; |
306 | |
307 | if ( !( count % 10 ) ) |
308 | FT_TRACE4(( "\n" )); |
309 | } |
310 | } |
311 | |
312 | if ( !count ) |
313 | FT_TRACE4(( " (none)\n" )); |
314 | if ( count % 10 ) |
315 | FT_TRACE4(( "\n" )); |
316 | } |
317 | |
318 | #endif /* FT_DEBUG_LEVEL_TRACE */ |
319 | |
320 | face->charmap = old_charmap; |
321 | return error; |
322 | } |
323 | |
324 | |
325 | FT_LOCAL_DEF( FT_Error ) |
326 | af_face_globals_new( FT_Face face, |
327 | AF_FaceGlobals *aglobals, |
328 | AF_Module module ) |
329 | { |
330 | FT_Error error; |
331 | FT_Memory memory; |
332 | AF_FaceGlobals globals = NULL; |
333 | |
334 | |
335 | memory = face->memory; |
336 | |
337 | /* we allocate an AF_FaceGlobals structure together */ |
338 | /* with the glyph_styles array */ |
339 | if ( FT_QALLOC( globals, |
340 | sizeof ( *globals ) + |
341 | (FT_ULong)face->num_glyphs * sizeof ( FT_UShort ) ) ) |
342 | goto Exit; |
343 | |
344 | FT_ZERO( &globals->metrics ); |
345 | |
346 | globals->face = face; |
347 | globals->glyph_count = (FT_UInt)face->num_glyphs; |
348 | /* right after the globals structure come the glyph styles */ |
349 | globals->glyph_styles = (FT_UShort*)( globals + 1 ); |
350 | globals->module = module; |
351 | globals->stem_darkening_for_ppem = 0; |
352 | globals->darken_x = 0; |
353 | globals->darken_y = 0; |
354 | globals->standard_vertical_width = 0; |
355 | globals->standard_horizontal_width = 0; |
356 | globals->scale_down_factor = 0; |
357 | |
358 | #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ |
359 | globals->hb_font = hb_ft_font_create_( face, NULL ); |
360 | globals->hb_buf = hb_buffer_create(); |
361 | #endif |
362 | |
363 | error = af_face_globals_compute_style_coverage( globals ); |
364 | if ( error ) |
365 | { |
366 | af_face_globals_free( globals ); |
367 | globals = NULL; |
368 | } |
369 | else |
370 | globals->increase_x_height = AF_PROP_INCREASE_X_HEIGHT_MAX; |
371 | |
372 | Exit: |
373 | *aglobals = globals; |
374 | return error; |
375 | } |
376 | |
377 | |
378 | FT_LOCAL_DEF( void ) |
379 | af_face_globals_free( void* globals_ ) |
380 | { |
381 | AF_FaceGlobals globals = (AF_FaceGlobals)globals_; |
382 | |
383 | |
384 | if ( globals ) |
385 | { |
386 | FT_Memory memory = globals->face->memory; |
387 | FT_UInt nn; |
388 | |
389 | |
390 | for ( nn = 0; nn < AF_STYLE_MAX; nn++ ) |
391 | { |
392 | if ( globals->metrics[nn] ) |
393 | { |
394 | AF_StyleClass style_class = |
395 | af_style_classes[nn]; |
396 | AF_WritingSystemClass writing_system_class = |
397 | af_writing_system_classes[style_class->writing_system]; |
398 | |
399 | |
400 | if ( writing_system_class->style_metrics_done ) |
401 | writing_system_class->style_metrics_done( globals->metrics[nn] ); |
402 | |
403 | FT_FREE( globals->metrics[nn] ); |
404 | } |
405 | } |
406 | |
407 | #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ |
408 | hb_font_destroy( globals->hb_font ); |
409 | hb_buffer_destroy( globals->hb_buf ); |
410 | #endif |
411 | |
412 | /* no need to free `globals->glyph_styles'; */ |
413 | /* it is part of the `globals' array */ |
414 | FT_FREE( globals ); |
415 | } |
416 | } |
417 | |
418 | |
419 | FT_LOCAL_DEF( FT_Error ) |
420 | af_face_globals_get_metrics( AF_FaceGlobals globals, |
421 | FT_UInt gindex, |
422 | FT_UInt options, |
423 | AF_StyleMetrics *ametrics ) |
424 | { |
425 | AF_StyleMetrics metrics = NULL; |
426 | |
427 | AF_Style style = (AF_Style)options; |
428 | AF_WritingSystemClass writing_system_class; |
429 | AF_StyleClass style_class; |
430 | |
431 | FT_Error error = FT_Err_Ok; |
432 | |
433 | |
434 | if ( gindex >= globals->glyph_count ) |
435 | { |
436 | error = FT_THROW( Invalid_Argument ); |
437 | goto Exit; |
438 | } |
439 | |
440 | /* if we have a forced style (via `options'), use it, */ |
441 | /* otherwise look into `glyph_styles' array */ |
442 | if ( style == AF_STYLE_NONE_DFLT || style + 1 >= AF_STYLE_MAX ) |
443 | style = (AF_Style)( globals->glyph_styles[gindex] & |
444 | AF_STYLE_UNASSIGNED ); |
445 | |
446 | Again: |
447 | style_class = af_style_classes[style]; |
448 | writing_system_class = af_writing_system_classes |
449 | [style_class->writing_system]; |
450 | |
451 | metrics = globals->metrics[style]; |
452 | if ( !metrics ) |
453 | { |
454 | /* create the global metrics object if necessary */ |
455 | FT_Memory memory = globals->face->memory; |
456 | |
457 | |
458 | if ( FT_ALLOC( metrics, writing_system_class->style_metrics_size ) ) |
459 | goto Exit; |
460 | |
461 | metrics->style_class = style_class; |
462 | metrics->globals = globals; |
463 | |
464 | if ( writing_system_class->style_metrics_init ) |
465 | { |
466 | error = writing_system_class->style_metrics_init( metrics, |
467 | globals->face ); |
468 | if ( error ) |
469 | { |
470 | if ( writing_system_class->style_metrics_done ) |
471 | writing_system_class->style_metrics_done( metrics ); |
472 | |
473 | FT_FREE( metrics ); |
474 | |
475 | /* internal error code -1 indicates */ |
476 | /* that no blue zones have been found */ |
477 | if ( error == -1 ) |
478 | { |
479 | style = (AF_Style)( globals->glyph_styles[gindex] & |
480 | AF_STYLE_UNASSIGNED ); |
481 | /* IMPORTANT: Clear the error code, see |
482 | * https://gitlab.freedesktop.org/freetype/freetype/-/issues/1063 |
483 | */ |
484 | error = FT_Err_Ok; |
485 | goto Again; |
486 | } |
487 | |
488 | goto Exit; |
489 | } |
490 | } |
491 | |
492 | globals->metrics[style] = metrics; |
493 | } |
494 | |
495 | Exit: |
496 | *ametrics = metrics; |
497 | |
498 | return error; |
499 | } |
500 | |
501 | |
502 | FT_LOCAL_DEF( FT_Bool ) |
503 | af_face_globals_is_digit( AF_FaceGlobals globals, |
504 | FT_UInt gindex ) |
505 | { |
506 | if ( gindex < globals->glyph_count ) |
507 | return FT_BOOL( globals->glyph_styles[gindex] & AF_DIGIT ); |
508 | |
509 | return FT_BOOL( 0 ); |
510 | } |
511 | |
512 | |
513 | /* END */ |
514 | |