1 /**************************************************************************** 2 * 3 * ftmm.c 4 * 5 * Multiple Master font support (body). 6 * 7 * Copyright (C) 1996-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 <freetype/internal/ftdebug.h> 20 21 #include <freetype/ftmm.h> 22 #include <freetype/internal/ftobjs.h> 23 #include <freetype/internal/services/svmm.h> 24 #include <freetype/internal/services/svmetric.h> 25 26 27 /************************************************************************** 28 * 29 * The macro FT_COMPONENT is used in trace mode. It is an implicit 30 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log 31 * messages during execution. 32 */ 33 #undef FT_COMPONENT 34 #define FT_COMPONENT mm 35 36 37 static FT_Error ft_face_get_mm_service(FT_Face face,FT_Service_MultiMasters * aservice)38 ft_face_get_mm_service( FT_Face face, 39 FT_Service_MultiMasters *aservice ) 40 { 41 FT_Error error; 42 43 44 *aservice = NULL; 45 46 if ( !face ) 47 return FT_THROW( Invalid_Face_Handle ); 48 49 error = FT_ERR( Invalid_Argument ); 50 51 if ( FT_HAS_MULTIPLE_MASTERS( face ) ) 52 { 53 FT_FACE_LOOKUP_SERVICE( face, 54 *aservice, 55 MULTI_MASTERS ); 56 57 if ( *aservice ) 58 error = FT_Err_Ok; 59 } 60 61 return error; 62 } 63 64 65 static FT_Error ft_face_get_mvar_service(FT_Face face,FT_Service_MetricsVariations * aservice)66 ft_face_get_mvar_service( FT_Face face, 67 FT_Service_MetricsVariations *aservice ) 68 { 69 FT_Error error; 70 71 72 *aservice = NULL; 73 74 if ( !face ) 75 return FT_THROW( Invalid_Face_Handle ); 76 77 error = FT_ERR( Invalid_Argument ); 78 79 if ( FT_HAS_MULTIPLE_MASTERS( face ) ) 80 { 81 FT_FACE_LOOKUP_SERVICE( face, 82 *aservice, 83 METRICS_VARIATIONS ); 84 85 if ( *aservice ) 86 error = FT_Err_Ok; 87 } 88 89 return error; 90 } 91 92 93 /* documentation is in ftmm.h */ 94 95 FT_EXPORT_DEF( FT_Error ) FT_Get_Multi_Master(FT_Face face,FT_Multi_Master * amaster)96 FT_Get_Multi_Master( FT_Face face, 97 FT_Multi_Master *amaster ) 98 { 99 FT_Error error; 100 FT_Service_MultiMasters service; 101 102 103 /* check of `face' delayed to `ft_face_get_mm_service' */ 104 105 if ( !amaster ) 106 return FT_THROW( Invalid_Argument ); 107 108 error = ft_face_get_mm_service( face, &service ); 109 if ( !error ) 110 { 111 error = FT_ERR( Invalid_Argument ); 112 if ( service->get_mm ) 113 error = service->get_mm( face, amaster ); 114 } 115 116 return error; 117 } 118 119 120 /* documentation is in ftmm.h */ 121 122 FT_EXPORT_DEF( FT_Error ) FT_Get_MM_Var(FT_Face face,FT_MM_Var ** amaster)123 FT_Get_MM_Var( FT_Face face, 124 FT_MM_Var* *amaster ) 125 { 126 FT_Error error; 127 FT_Service_MultiMasters service; 128 129 130 /* check of `face' delayed to `ft_face_get_mm_service' */ 131 132 if ( !amaster ) 133 return FT_THROW( Invalid_Argument ); 134 135 error = ft_face_get_mm_service( face, &service ); 136 if ( !error ) 137 { 138 error = FT_ERR( Invalid_Argument ); 139 if ( service->get_mm_var ) 140 error = service->get_mm_var( face, amaster ); 141 } 142 143 return error; 144 } 145 146 147 /* documentation is in ftmm.h */ 148 149 FT_EXPORT_DEF( FT_Error ) FT_Done_MM_Var(FT_Library library,FT_MM_Var * amaster)150 FT_Done_MM_Var( FT_Library library, 151 FT_MM_Var* amaster ) 152 { 153 FT_Memory memory; 154 155 156 if ( !library ) 157 return FT_THROW( Invalid_Library_Handle ); 158 159 memory = library->memory; 160 FT_FREE( amaster ); 161 162 return FT_Err_Ok; 163 } 164 165 166 /* documentation is in ftmm.h */ 167 168 FT_EXPORT_DEF( FT_Error ) FT_Set_MM_Design_Coordinates(FT_Face face,FT_UInt num_coords,FT_Long * coords)169 FT_Set_MM_Design_Coordinates( FT_Face face, 170 FT_UInt num_coords, 171 FT_Long* coords ) 172 { 173 FT_Error error; 174 FT_Service_MultiMasters service; 175 176 177 /* check of `face' delayed to `ft_face_get_mm_service' */ 178 179 if ( num_coords && !coords ) 180 return FT_THROW( Invalid_Argument ); 181 182 error = ft_face_get_mm_service( face, &service ); 183 if ( !error ) 184 { 185 error = FT_ERR( Invalid_Argument ); 186 if ( service->set_mm_design ) 187 error = service->set_mm_design( face, num_coords, coords ); 188 189 if ( !error ) 190 { 191 if ( num_coords ) 192 face->face_flags |= FT_FACE_FLAG_VARIATION; 193 else 194 face->face_flags &= ~FT_FACE_FLAG_VARIATION; 195 } 196 } 197 198 /* enforce recomputation of auto-hinting data */ 199 if ( !error && face->autohint.finalizer ) 200 { 201 face->autohint.finalizer( face->autohint.data ); 202 face->autohint.data = NULL; 203 } 204 205 return error; 206 } 207 208 209 /* documentation is in ftmm.h */ 210 211 FT_EXPORT_DEF( FT_Error ) FT_Set_MM_WeightVector(FT_Face face,FT_UInt len,FT_Fixed * weightvector)212 FT_Set_MM_WeightVector( FT_Face face, 213 FT_UInt len, 214 FT_Fixed* weightvector ) 215 { 216 FT_Error error; 217 FT_Service_MultiMasters service; 218 219 220 /* check of `face' delayed to `ft_face_get_mm_service' */ 221 222 if ( len && !weightvector ) 223 return FT_THROW( Invalid_Argument ); 224 225 error = ft_face_get_mm_service( face, &service ); 226 if ( !error ) 227 { 228 error = FT_ERR( Invalid_Argument ); 229 if ( service->set_mm_weightvector ) 230 error = service->set_mm_weightvector( face, len, weightvector ); 231 232 if ( !error ) 233 { 234 if ( len ) 235 face->face_flags |= FT_FACE_FLAG_VARIATION; 236 else 237 face->face_flags &= ~FT_FACE_FLAG_VARIATION; 238 } 239 } 240 241 /* enforce recomputation of auto-hinting data */ 242 if ( !error && face->autohint.finalizer ) 243 { 244 face->autohint.finalizer( face->autohint.data ); 245 face->autohint.data = NULL; 246 } 247 248 return error; 249 } 250 251 252 FT_EXPORT_DEF( FT_Error ) FT_Get_MM_WeightVector(FT_Face face,FT_UInt * len,FT_Fixed * weightvector)253 FT_Get_MM_WeightVector( FT_Face face, 254 FT_UInt* len, 255 FT_Fixed* weightvector ) 256 { 257 FT_Error error; 258 FT_Service_MultiMasters service; 259 260 261 /* check of `face' delayed to `ft_face_get_mm_service' */ 262 263 if ( len && !weightvector ) 264 return FT_THROW( Invalid_Argument ); 265 266 error = ft_face_get_mm_service( face, &service ); 267 if ( !error ) 268 { 269 error = FT_ERR( Invalid_Argument ); 270 if ( service->get_mm_weightvector ) 271 error = service->get_mm_weightvector( face, len, weightvector ); 272 } 273 274 return error; 275 } 276 277 278 /* documentation is in ftmm.h */ 279 280 FT_EXPORT_DEF( FT_Error ) FT_Set_Var_Design_Coordinates(FT_Face face,FT_UInt num_coords,FT_Fixed * coords)281 FT_Set_Var_Design_Coordinates( FT_Face face, 282 FT_UInt num_coords, 283 FT_Fixed* coords ) 284 { 285 FT_Error error; 286 FT_Service_MultiMasters service_mm = NULL; 287 FT_Service_MetricsVariations service_mvar = NULL; 288 289 290 /* check of `face' delayed to `ft_face_get_mm_service' */ 291 292 if ( num_coords && !coords ) 293 return FT_THROW( Invalid_Argument ); 294 295 error = ft_face_get_mm_service( face, &service_mm ); 296 if ( !error ) 297 { 298 error = FT_ERR( Invalid_Argument ); 299 if ( service_mm->set_var_design ) 300 error = service_mm->set_var_design( face, num_coords, coords ); 301 302 if ( !error || error == -1 ) 303 { 304 FT_Bool is_variation_old = FT_IS_VARIATION( face ); 305 306 307 if ( num_coords ) 308 face->face_flags |= FT_FACE_FLAG_VARIATION; 309 else 310 face->face_flags &= ~FT_FACE_FLAG_VARIATION; 311 312 if ( service_mm->construct_ps_name ) 313 { 314 if ( error == -1 ) 315 { 316 /* The PS name of a named instance and a non-named instance */ 317 /* usually differs, even if the axis values are identical. */ 318 if ( is_variation_old != FT_IS_VARIATION( face ) ) 319 service_mm->construct_ps_name( face ); 320 } 321 else 322 service_mm->construct_ps_name( face ); 323 } 324 } 325 326 /* internal error code -1 means `no change'; we can exit immediately */ 327 if ( error == -1 ) 328 return FT_Err_Ok; 329 } 330 331 if ( !error ) 332 { 333 (void)ft_face_get_mvar_service( face, &service_mvar ); 334 335 if ( service_mvar && service_mvar->metrics_adjust ) 336 service_mvar->metrics_adjust( face ); 337 } 338 339 /* enforce recomputation of auto-hinting data */ 340 if ( !error && face->autohint.finalizer ) 341 { 342 face->autohint.finalizer( face->autohint.data ); 343 face->autohint.data = NULL; 344 } 345 346 return error; 347 } 348 349 350 /* documentation is in ftmm.h */ 351 352 FT_EXPORT_DEF( FT_Error ) FT_Get_Var_Design_Coordinates(FT_Face face,FT_UInt num_coords,FT_Fixed * coords)353 FT_Get_Var_Design_Coordinates( FT_Face face, 354 FT_UInt num_coords, 355 FT_Fixed* coords ) 356 { 357 FT_Error error; 358 FT_Service_MultiMasters service; 359 360 361 /* check of `face' delayed to `ft_face_get_mm_service' */ 362 363 if ( !coords ) 364 return FT_THROW( Invalid_Argument ); 365 366 error = ft_face_get_mm_service( face, &service ); 367 if ( !error ) 368 { 369 error = FT_ERR( Invalid_Argument ); 370 if ( service->get_var_design ) 371 error = service->get_var_design( face, num_coords, coords ); 372 } 373 374 return error; 375 } 376 377 378 /* documentation is in ftmm.h */ 379 380 FT_EXPORT_DEF( FT_Error ) FT_Set_MM_Blend_Coordinates(FT_Face face,FT_UInt num_coords,FT_Fixed * coords)381 FT_Set_MM_Blend_Coordinates( FT_Face face, 382 FT_UInt num_coords, 383 FT_Fixed* coords ) 384 { 385 FT_Error error; 386 FT_Service_MultiMasters service_mm = NULL; 387 FT_Service_MetricsVariations service_mvar = NULL; 388 389 390 /* check of `face' delayed to `ft_face_get_mm_service' */ 391 392 if ( num_coords && !coords ) 393 return FT_THROW( Invalid_Argument ); 394 395 error = ft_face_get_mm_service( face, &service_mm ); 396 if ( !error ) 397 { 398 error = FT_ERR( Invalid_Argument ); 399 if ( service_mm->set_mm_blend ) 400 error = service_mm->set_mm_blend( face, num_coords, coords ); 401 402 if ( !error || error == -1 ) 403 { 404 FT_Bool is_variation_old = FT_IS_VARIATION( face ); 405 406 407 if ( num_coords ) 408 face->face_flags |= FT_FACE_FLAG_VARIATION; 409 else 410 face->face_flags &= ~FT_FACE_FLAG_VARIATION; 411 412 if ( service_mm->construct_ps_name ) 413 { 414 if ( error == -1 ) 415 { 416 /* The PS name of a named instance and a non-named instance */ 417 /* usually differs, even if the axis values are identical. */ 418 if ( is_variation_old != FT_IS_VARIATION( face ) ) 419 service_mm->construct_ps_name( face ); 420 } 421 else 422 service_mm->construct_ps_name( face ); 423 } 424 } 425 426 /* internal error code -1 means `no change'; we can exit immediately */ 427 if ( error == -1 ) 428 return FT_Err_Ok; 429 } 430 431 if ( !error ) 432 { 433 (void)ft_face_get_mvar_service( face, &service_mvar ); 434 435 if ( service_mvar && service_mvar->metrics_adjust ) 436 service_mvar->metrics_adjust( face ); 437 } 438 439 /* enforce recomputation of auto-hinting data */ 440 if ( !error && face->autohint.finalizer ) 441 { 442 face->autohint.finalizer( face->autohint.data ); 443 face->autohint.data = NULL; 444 } 445 446 return error; 447 } 448 449 450 /* documentation is in ftmm.h */ 451 452 /* This is exactly the same as the previous function. It exists for */ 453 /* orthogonality. */ 454 455 FT_EXPORT_DEF( FT_Error ) FT_Set_Var_Blend_Coordinates(FT_Face face,FT_UInt num_coords,FT_Fixed * coords)456 FT_Set_Var_Blend_Coordinates( FT_Face face, 457 FT_UInt num_coords, 458 FT_Fixed* coords ) 459 { 460 FT_Error error; 461 FT_Service_MultiMasters service_mm = NULL; 462 FT_Service_MetricsVariations service_mvar = NULL; 463 464 465 /* check of `face' delayed to `ft_face_get_mm_service' */ 466 467 if ( num_coords && !coords ) 468 return FT_THROW( Invalid_Argument ); 469 470 error = ft_face_get_mm_service( face, &service_mm ); 471 if ( !error ) 472 { 473 error = FT_ERR( Invalid_Argument ); 474 if ( service_mm->set_mm_blend ) 475 error = service_mm->set_mm_blend( face, num_coords, coords ); 476 477 if ( !error || error == -1 ) 478 { 479 FT_Bool is_variation_old = FT_IS_VARIATION( face ); 480 481 482 if ( num_coords ) 483 face->face_flags |= FT_FACE_FLAG_VARIATION; 484 else 485 face->face_flags &= ~FT_FACE_FLAG_VARIATION; 486 487 if ( service_mm->construct_ps_name ) 488 { 489 if ( error == -1 ) 490 { 491 /* The PS name of a named instance and a non-named instance */ 492 /* usually differs, even if the axis values are identical. */ 493 if ( is_variation_old != FT_IS_VARIATION( face ) ) 494 service_mm->construct_ps_name( face ); 495 } 496 else 497 service_mm->construct_ps_name( face ); 498 } 499 } 500 501 /* internal error code -1 means `no change'; we can exit immediately */ 502 if ( error == -1 ) 503 return FT_Err_Ok; 504 } 505 506 if ( !error ) 507 { 508 (void)ft_face_get_mvar_service( face, &service_mvar ); 509 510 if ( service_mvar && service_mvar->metrics_adjust ) 511 service_mvar->metrics_adjust( face ); 512 } 513 514 /* enforce recomputation of auto-hinting data */ 515 if ( !error && face->autohint.finalizer ) 516 { 517 face->autohint.finalizer( face->autohint.data ); 518 face->autohint.data = NULL; 519 } 520 521 return error; 522 } 523 524 525 /* documentation is in ftmm.h */ 526 527 FT_EXPORT_DEF( FT_Error ) FT_Get_MM_Blend_Coordinates(FT_Face face,FT_UInt num_coords,FT_Fixed * coords)528 FT_Get_MM_Blend_Coordinates( FT_Face face, 529 FT_UInt num_coords, 530 FT_Fixed* coords ) 531 { 532 FT_Error error; 533 FT_Service_MultiMasters service; 534 535 536 /* check of `face' delayed to `ft_face_get_mm_service' */ 537 538 if ( !coords ) 539 return FT_THROW( Invalid_Argument ); 540 541 error = ft_face_get_mm_service( face, &service ); 542 if ( !error ) 543 { 544 error = FT_ERR( Invalid_Argument ); 545 if ( service->get_mm_blend ) 546 error = service->get_mm_blend( face, num_coords, coords ); 547 } 548 549 return error; 550 } 551 552 553 /* documentation is in ftmm.h */ 554 555 /* This is exactly the same as the previous function. It exists for */ 556 /* orthogonality. */ 557 558 FT_EXPORT_DEF( FT_Error ) FT_Get_Var_Blend_Coordinates(FT_Face face,FT_UInt num_coords,FT_Fixed * coords)559 FT_Get_Var_Blend_Coordinates( FT_Face face, 560 FT_UInt num_coords, 561 FT_Fixed* coords ) 562 { 563 FT_Error error; 564 FT_Service_MultiMasters service; 565 566 567 /* check of `face' delayed to `ft_face_get_mm_service' */ 568 569 if ( !coords ) 570 return FT_THROW( Invalid_Argument ); 571 572 error = ft_face_get_mm_service( face, &service ); 573 if ( !error ) 574 { 575 error = FT_ERR( Invalid_Argument ); 576 if ( service->get_mm_blend ) 577 error = service->get_mm_blend( face, num_coords, coords ); 578 } 579 580 return error; 581 } 582 583 584 /* documentation is in ftmm.h */ 585 586 FT_EXPORT_DEF( FT_Error ) FT_Get_Var_Axis_Flags(FT_MM_Var * master,FT_UInt axis_index,FT_UInt * flags)587 FT_Get_Var_Axis_Flags( FT_MM_Var* master, 588 FT_UInt axis_index, 589 FT_UInt* flags ) 590 { 591 FT_UShort* axis_flags; 592 593 594 if ( !master || !flags ) 595 return FT_THROW( Invalid_Argument ); 596 597 if ( axis_index >= master->num_axis ) 598 return FT_THROW( Invalid_Argument ); 599 600 /* the axis flags array immediately follows the data of `master' */ 601 axis_flags = (FT_UShort*)&( master[1] ); 602 *flags = axis_flags[axis_index]; 603 604 return FT_Err_Ok; 605 } 606 607 608 /* documentation is in ftmm.h */ 609 610 FT_EXPORT_DEF( FT_Error ) FT_Set_Named_Instance(FT_Face face,FT_UInt instance_index)611 FT_Set_Named_Instance( FT_Face face, 612 FT_UInt instance_index ) 613 { 614 FT_Error error; 615 616 FT_Service_MultiMasters service_mm = NULL; 617 FT_Service_MetricsVariations service_mvar = NULL; 618 619 620 /* check of `face' delayed to `ft_face_get_mm_service' */ 621 622 error = ft_face_get_mm_service( face, &service_mm ); 623 if ( !error ) 624 { 625 error = FT_ERR( Invalid_Argument ); 626 if ( service_mm->set_named_instance ) 627 error = service_mm->set_named_instance( face, instance_index ); 628 629 if ( !error || error == -1 ) 630 { 631 FT_Bool is_variation_old = FT_IS_VARIATION( face ); 632 633 634 face->face_flags &= ~FT_FACE_FLAG_VARIATION; 635 face->face_index = ( instance_index << 16 ) | 636 ( face->face_index & 0xFFFFL ); 637 638 if ( service_mm->construct_ps_name ) 639 { 640 if ( error == -1 ) 641 { 642 /* The PS name of a named instance and a non-named instance */ 643 /* usually differs, even if the axis values are identical. */ 644 if ( is_variation_old != FT_IS_VARIATION( face ) ) 645 service_mm->construct_ps_name( face ); 646 } 647 else 648 service_mm->construct_ps_name( face ); 649 } 650 } 651 652 /* internal error code -1 means `no change'; we can exit immediately */ 653 if ( error == -1 ) 654 return FT_Err_Ok; 655 } 656 657 if ( !error ) 658 { 659 (void)ft_face_get_mvar_service( face, &service_mvar ); 660 661 if ( service_mvar && service_mvar->metrics_adjust ) 662 service_mvar->metrics_adjust( face ); 663 } 664 665 /* enforce recomputation of auto-hinting data */ 666 if ( !error && face->autohint.finalizer ) 667 { 668 face->autohint.finalizer( face->autohint.data ); 669 face->autohint.data = NULL; 670 } 671 672 return error; 673 } 674 675 676 /* documentation is in ftmm.h */ 677 678 FT_EXPORT_DEF( FT_Error ) FT_Get_Default_Named_Instance(FT_Face face,FT_UInt * instance_index)679 FT_Get_Default_Named_Instance( FT_Face face, 680 FT_UInt *instance_index ) 681 { 682 FT_Error error; 683 684 FT_Service_MultiMasters service_mm = NULL; 685 686 687 /* check of `face' delayed to `ft_face_get_mm_service' */ 688 689 error = ft_face_get_mm_service( face, &service_mm ); 690 if ( !error ) 691 { 692 /* no error if `get_default_named_instance` is not available */ 693 if ( service_mm->get_default_named_instance ) 694 error = service_mm->get_default_named_instance( face, 695 instance_index ); 696 else 697 error = FT_Err_Ok; 698 } 699 700 return error; 701 } 702 703 704 /* END */ 705