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