| /**************************************************************************** |
| * |
| * ftmm.c |
| * |
| * Multiple Master font support (body). |
| * |
| * Copyright (C) 1996-2022 by |
| * David Turner, Robert Wilhelm, and Werner Lemberg. |
| * |
| * This file is part of the FreeType project, and may only be used, |
| * modified, and distributed under the terms of the FreeType project |
| * license, LICENSE.TXT. By continuing to use, modify, or distribute |
| * this file you indicate that you have read the license and |
| * understand and accept it fully. |
| * |
| */ |
| |
| |
| #include <freetype/internal/ftdebug.h> |
| |
| #include <freetype/ftmm.h> |
| #include <freetype/internal/ftobjs.h> |
| #include <freetype/internal/services/svmm.h> |
| #include <freetype/internal/services/svmetric.h> |
| |
| |
| /************************************************************************** |
| * |
| * The macro FT_COMPONENT is used in trace mode. It is an implicit |
| * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log |
| * messages during execution. |
| */ |
| #undef FT_COMPONENT |
| #define FT_COMPONENT mm |
| |
| |
| static FT_Error |
| ft_face_get_mm_service( FT_Face face, |
| FT_Service_MultiMasters *aservice ) |
| { |
| FT_Error error; |
| |
| |
| *aservice = NULL; |
| |
| if ( !face ) |
| return FT_THROW( Invalid_Face_Handle ); |
| |
| error = FT_ERR( Invalid_Argument ); |
| |
| if ( FT_HAS_MULTIPLE_MASTERS( face ) ) |
| { |
| FT_FACE_LOOKUP_SERVICE( face, |
| *aservice, |
| MULTI_MASTERS ); |
| |
| if ( *aservice ) |
| error = FT_Err_Ok; |
| } |
| |
| return error; |
| } |
| |
| |
| static FT_Error |
| ft_face_get_mvar_service( FT_Face face, |
| FT_Service_MetricsVariations *aservice ) |
| { |
| FT_Error error; |
| |
| |
| *aservice = NULL; |
| |
| if ( !face ) |
| return FT_THROW( Invalid_Face_Handle ); |
| |
| error = FT_ERR( Invalid_Argument ); |
| |
| if ( FT_HAS_MULTIPLE_MASTERS( face ) ) |
| { |
| FT_FACE_LOOKUP_SERVICE( face, |
| *aservice, |
| METRICS_VARIATIONS ); |
| |
| if ( *aservice ) |
| error = FT_Err_Ok; |
| } |
| |
| return error; |
| } |
| |
| |
| /* documentation is in ftmm.h */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FT_Get_Multi_Master( FT_Face face, |
| FT_Multi_Master *amaster ) |
| { |
| FT_Error error; |
| FT_Service_MultiMasters service; |
| |
| |
| /* check of `face' delayed to `ft_face_get_mm_service' */ |
| |
| if ( !amaster ) |
| return FT_THROW( Invalid_Argument ); |
| |
| error = ft_face_get_mm_service( face, &service ); |
| if ( !error ) |
| { |
| error = FT_ERR( Invalid_Argument ); |
| if ( service->get_mm ) |
| error = service->get_mm( face, amaster ); |
| } |
| |
| return error; |
| } |
| |
| |
| /* documentation is in ftmm.h */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FT_Get_MM_Var( FT_Face face, |
| FT_MM_Var* *amaster ) |
| { |
| FT_Error error; |
| FT_Service_MultiMasters service; |
| |
| |
| /* check of `face' delayed to `ft_face_get_mm_service' */ |
| |
| if ( !amaster ) |
| return FT_THROW( Invalid_Argument ); |
| |
| error = ft_face_get_mm_service( face, &service ); |
| if ( !error ) |
| { |
| error = FT_ERR( Invalid_Argument ); |
| if ( service->get_mm_var ) |
| error = service->get_mm_var( face, amaster ); |
| } |
| |
| return error; |
| } |
| |
| |
| /* documentation is in ftmm.h */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FT_Done_MM_Var( FT_Library library, |
| FT_MM_Var* amaster ) |
| { |
| FT_Memory memory; |
| |
| |
| if ( !library ) |
| return FT_THROW( Invalid_Library_Handle ); |
| |
| memory = library->memory; |
| FT_FREE( amaster ); |
| |
| return FT_Err_Ok; |
| } |
| |
| |
| /* documentation is in ftmm.h */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FT_Set_MM_Design_Coordinates( FT_Face face, |
| FT_UInt num_coords, |
| FT_Long* coords ) |
| { |
| FT_Error error; |
| FT_Service_MultiMasters service; |
| |
| |
| /* check of `face' delayed to `ft_face_get_mm_service' */ |
| |
| if ( num_coords && !coords ) |
| return FT_THROW( Invalid_Argument ); |
| |
| error = ft_face_get_mm_service( face, &service ); |
| if ( !error ) |
| { |
| error = FT_ERR( Invalid_Argument ); |
| if ( service->set_mm_design ) |
| error = service->set_mm_design( face, num_coords, coords ); |
| } |
| |
| /* enforce recomputation of auto-hinting data */ |
| if ( !error && face->autohint.finalizer ) |
| { |
| face->autohint.finalizer( face->autohint.data ); |
| face->autohint.data = NULL; |
| } |
| |
| return error; |
| } |
| |
| |
| /* documentation is in ftmm.h */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FT_Set_MM_WeightVector( FT_Face face, |
| FT_UInt len, |
| FT_Fixed* weightvector ) |
| { |
| FT_Error error; |
| FT_Service_MultiMasters service; |
| |
| |
| /* check of `face' delayed to `ft_face_get_mm_service' */ |
| |
| if ( len && !weightvector ) |
| return FT_THROW( Invalid_Argument ); |
| |
| error = ft_face_get_mm_service( face, &service ); |
| if ( !error ) |
| { |
| error = FT_ERR( Invalid_Argument ); |
| if ( service->set_mm_weightvector ) |
| error = service->set_mm_weightvector( face, len, weightvector ); |
| } |
| |
| /* enforce recomputation of auto-hinting data */ |
| if ( !error && face->autohint.finalizer ) |
| { |
| face->autohint.finalizer( face->autohint.data ); |
| face->autohint.data = NULL; |
| } |
| |
| return error; |
| } |
| |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FT_Get_MM_WeightVector( FT_Face face, |
| FT_UInt* len, |
| FT_Fixed* weightvector ) |
| { |
| FT_Error error; |
| FT_Service_MultiMasters service; |
| |
| |
| /* check of `face' delayed to `ft_face_get_mm_service' */ |
| |
| if ( len && !weightvector ) |
| return FT_THROW( Invalid_Argument ); |
| |
| error = ft_face_get_mm_service( face, &service ); |
| if ( !error ) |
| { |
| error = FT_ERR( Invalid_Argument ); |
| if ( service->get_mm_weightvector ) |
| error = service->get_mm_weightvector( face, len, weightvector ); |
| } |
| |
| return error; |
| } |
| |
| |
| /* documentation is in ftmm.h */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FT_Set_Var_Design_Coordinates( FT_Face face, |
| FT_UInt num_coords, |
| FT_Fixed* coords ) |
| { |
| FT_Error error; |
| FT_Service_MultiMasters service_mm = NULL; |
| FT_Service_MetricsVariations service_mvar = NULL; |
| |
| |
| /* check of `face' delayed to `ft_face_get_mm_service' */ |
| |
| if ( num_coords && !coords ) |
| return FT_THROW( Invalid_Argument ); |
| |
| error = ft_face_get_mm_service( face, &service_mm ); |
| if ( !error ) |
| { |
| error = FT_ERR( Invalid_Argument ); |
| if ( service_mm->set_var_design ) |
| error = service_mm->set_var_design( face, num_coords, coords ); |
| |
| /* internal error code -1 means `no change'; we can exit immediately */ |
| if ( error == -1 ) |
| return FT_Err_Ok; |
| } |
| |
| if ( !error ) |
| { |
| (void)ft_face_get_mvar_service( face, &service_mvar ); |
| |
| if ( service_mvar && service_mvar->metrics_adjust ) |
| service_mvar->metrics_adjust( face ); |
| } |
| |
| /* enforce recomputation of auto-hinting data */ |
| if ( !error && face->autohint.finalizer ) |
| { |
| face->autohint.finalizer( face->autohint.data ); |
| face->autohint.data = NULL; |
| } |
| |
| return error; |
| } |
| |
| |
| /* documentation is in ftmm.h */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FT_Get_Var_Design_Coordinates( FT_Face face, |
| FT_UInt num_coords, |
| FT_Fixed* coords ) |
| { |
| FT_Error error; |
| FT_Service_MultiMasters service; |
| |
| |
| /* check of `face' delayed to `ft_face_get_mm_service' */ |
| |
| if ( !coords ) |
| return FT_THROW( Invalid_Argument ); |
| |
| error = ft_face_get_mm_service( face, &service ); |
| if ( !error ) |
| { |
| error = FT_ERR( Invalid_Argument ); |
| if ( service->get_var_design ) |
| error = service->get_var_design( face, num_coords, coords ); |
| } |
| |
| return error; |
| } |
| |
| |
| /* documentation is in ftmm.h */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FT_Set_MM_Blend_Coordinates( FT_Face face, |
| FT_UInt num_coords, |
| FT_Fixed* coords ) |
| { |
| FT_Error error; |
| FT_Service_MultiMasters service_mm = NULL; |
| FT_Service_MetricsVariations service_mvar = NULL; |
| |
| |
| /* check of `face' delayed to `ft_face_get_mm_service' */ |
| |
| if ( num_coords && !coords ) |
| return FT_THROW( Invalid_Argument ); |
| |
| error = ft_face_get_mm_service( face, &service_mm ); |
| if ( !error ) |
| { |
| error = FT_ERR( Invalid_Argument ); |
| if ( service_mm->set_mm_blend ) |
| error = service_mm->set_mm_blend( face, num_coords, coords ); |
| |
| /* internal error code -1 means `no change'; we can exit immediately */ |
| if ( error == -1 ) |
| return FT_Err_Ok; |
| } |
| |
| if ( !error ) |
| { |
| (void)ft_face_get_mvar_service( face, &service_mvar ); |
| |
| if ( service_mvar && service_mvar->metrics_adjust ) |
| service_mvar->metrics_adjust( face ); |
| } |
| |
| /* enforce recomputation of auto-hinting data */ |
| if ( !error && face->autohint.finalizer ) |
| { |
| face->autohint.finalizer( face->autohint.data ); |
| face->autohint.data = NULL; |
| } |
| |
| return error; |
| } |
| |
| |
| /* documentation is in ftmm.h */ |
| |
| /* This is exactly the same as the previous function. It exists for */ |
| /* orthogonality. */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FT_Set_Var_Blend_Coordinates( FT_Face face, |
| FT_UInt num_coords, |
| FT_Fixed* coords ) |
| { |
| FT_Error error; |
| FT_Service_MultiMasters service_mm = NULL; |
| FT_Service_MetricsVariations service_mvar = NULL; |
| |
| |
| /* check of `face' delayed to `ft_face_get_mm_service' */ |
| |
| if ( num_coords && !coords ) |
| return FT_THROW( Invalid_Argument ); |
| |
| error = ft_face_get_mm_service( face, &service_mm ); |
| if ( !error ) |
| { |
| error = FT_ERR( Invalid_Argument ); |
| if ( service_mm->set_mm_blend ) |
| error = service_mm->set_mm_blend( face, num_coords, coords ); |
| |
| /* internal error code -1 means `no change'; we can exit immediately */ |
| if ( error == -1 ) |
| return FT_Err_Ok; |
| } |
| |
| if ( !error ) |
| { |
| (void)ft_face_get_mvar_service( face, &service_mvar ); |
| |
| if ( service_mvar && service_mvar->metrics_adjust ) |
| service_mvar->metrics_adjust( face ); |
| } |
| |
| /* enforce recomputation of auto-hinting data */ |
| if ( !error && face->autohint.finalizer ) |
| { |
| face->autohint.finalizer( face->autohint.data ); |
| face->autohint.data = NULL; |
| } |
| |
| return error; |
| } |
| |
| |
| /* documentation is in ftmm.h */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FT_Get_MM_Blend_Coordinates( FT_Face face, |
| FT_UInt num_coords, |
| FT_Fixed* coords ) |
| { |
| FT_Error error; |
| FT_Service_MultiMasters service; |
| |
| |
| /* check of `face' delayed to `ft_face_get_mm_service' */ |
| |
| if ( !coords ) |
| return FT_THROW( Invalid_Argument ); |
| |
| error = ft_face_get_mm_service( face, &service ); |
| if ( !error ) |
| { |
| error = FT_ERR( Invalid_Argument ); |
| if ( service->get_mm_blend ) |
| error = service->get_mm_blend( face, num_coords, coords ); |
| } |
| |
| return error; |
| } |
| |
| |
| /* documentation is in ftmm.h */ |
| |
| /* This is exactly the same as the previous function. It exists for */ |
| /* orthogonality. */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FT_Get_Var_Blend_Coordinates( FT_Face face, |
| FT_UInt num_coords, |
| FT_Fixed* coords ) |
| { |
| FT_Error error; |
| FT_Service_MultiMasters service; |
| |
| |
| /* check of `face' delayed to `ft_face_get_mm_service' */ |
| |
| if ( !coords ) |
| return FT_THROW( Invalid_Argument ); |
| |
| error = ft_face_get_mm_service( face, &service ); |
| if ( !error ) |
| { |
| error = FT_ERR( Invalid_Argument ); |
| if ( service->get_mm_blend ) |
| error = service->get_mm_blend( face, num_coords, coords ); |
| } |
| |
| return error; |
| } |
| |
| |
| /* documentation is in ftmm.h */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FT_Get_Var_Axis_Flags( FT_MM_Var* master, |
| FT_UInt axis_index, |
| FT_UInt* flags ) |
| { |
| FT_UShort* axis_flags; |
| |
| |
| if ( !master || !flags ) |
| return FT_THROW( Invalid_Argument ); |
| |
| if ( axis_index >= master->num_axis ) |
| return FT_THROW( Invalid_Argument ); |
| |
| /* the axis flags array immediately follows the data of `master' */ |
| axis_flags = (FT_UShort*)&( master[1] ); |
| *flags = axis_flags[axis_index]; |
| |
| return FT_Err_Ok; |
| } |
| |
| |
| /* documentation is in ftmm.h */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FT_Set_Named_Instance( FT_Face face, |
| FT_UInt instance_index ) |
| { |
| FT_Error error; |
| |
| FT_Service_MultiMasters service_mm = NULL; |
| FT_Service_MetricsVariations service_mvar = NULL; |
| |
| |
| /* check of `face' delayed to `ft_face_get_mm_service' */ |
| |
| error = ft_face_get_mm_service( face, &service_mm ); |
| if ( !error ) |
| { |
| error = FT_ERR( Invalid_Argument ); |
| if ( service_mm->set_instance ) |
| error = service_mm->set_instance( face, instance_index ); |
| } |
| |
| if ( !error ) |
| { |
| (void)ft_face_get_mvar_service( face, &service_mvar ); |
| |
| if ( service_mvar && service_mvar->metrics_adjust ) |
| service_mvar->metrics_adjust( face ); |
| } |
| |
| /* enforce recomputation of auto-hinting data */ |
| if ( !error && face->autohint.finalizer ) |
| { |
| face->autohint.finalizer( face->autohint.data ); |
| face->autohint.data = NULL; |
| } |
| |
| if ( !error ) |
| { |
| face->face_index = ( instance_index << 16 ) | |
| ( face->face_index & 0xFFFFL ); |
| face->face_flags &= ~FT_FACE_FLAG_VARIATION; |
| } |
| |
| return error; |
| } |
| |
| |
| /* END */ |