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