/**************************************************************************** * * hvfdrv.c * * HVF font driver implementation (body). * * Copyright (C) 2025-2026 by * Apple Inc. * written by Deborah Goldsmith * * 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 #include #include #include #include /* For TT_Face */ #include #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT #include #include #include #endif #include "hvfdrv.h" #include "hvfobjs.h" #include "hvfload.h" #include "hvferror.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 hvfdrv #ifdef FT_CONFIG_OPTION_HVF /************************************************************************** * * FACE MANAGEMENT * */ /************************************************************************** * * @Function: * hvf_get_kerning * * @Description: * Get kerning vector between two glyphs. Delegates to SFNT service * since HVF fonts are TrueType/OpenType fonts. * * @Input: * face :: * A handle to the source face object. * * left_glyph :: * The index of the left glyph in the kern pair. * * right_glyph :: * The index of the right glyph in the kern pair. * * @Output: * kerning :: * The kerning vector in font units. * * @Return: * FreeType error code. 0 means success. */ static FT_Error hvf_get_kerning( FT_Face face, FT_UInt left_glyph, FT_UInt right_glyph, FT_Vector* kerning ) { TT_Face tt_face = (TT_Face)face; SFNT_Service sfnt = (SFNT_Service)tt_face->sfnt; kerning->x = 0; kerning->y = 0; if ( sfnt ) { /* Use 'kern' table if available since that can be faster; otherwise */ /* use GPOS kerning pairs if available. */ if ( tt_face->kern_avail_bits ) kerning->x = sfnt->get_kerning( tt_face, left_glyph, right_glyph ); #ifdef TT_CONFIG_OPTION_GPOS_KERNING else if ( tt_face->num_gpos_lookups_kerning ) kerning->x = sfnt->get_gpos_kerning( tt_face, left_glyph, right_glyph ); #endif } return FT_Err_Ok; } /************************************************************************** * * @Function: * hvf_get_advances * * @Description: * Get advance widths/heights for a range of glyphs. Delegates to * TrueType infrastructure since HVF fonts are TrueType/OpenType fonts. * * @Input: * face :: * A handle to the source face object. * * start :: * The first glyph index. * * count :: * The number of advance values to retrieve. * * flags :: * Loading flags (vertical/horizontal layout). * * @Output: * advances :: * The advance values in 16.16 format. * * @Return: * FreeType error code. 0 means success. */ static FT_Error hvf_get_advances( FT_Face face, FT_UInt start, FT_UInt count, FT_Int32 flags, FT_Fixed *advances ) { TT_Face tt_face = (TT_Face)face; FT_Bool horz; FT_UInt nn; if ( !FT_IS_SFNT( face ) ) return FT_THROW( Unimplemented_Feature ); horz = !( flags & FT_LOAD_VERTICAL_LAYOUT ); if ( horz ) { /* Check availability of horizontal metrics. */ if ( !tt_face->horizontal.number_Of_HMetrics ) return FT_THROW( Unimplemented_Feature ); #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT /* No fast retrieval for blended MM fonts without 'HVAR' table. */ if ( ( FT_IS_NAMED_INSTANCE( face ) || FT_IS_VARIATION( face ) ) && !( tt_face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) ) return FT_THROW( Unimplemented_Feature ); #endif } else /* vertical */ { /* Check whether we have data from the 'vmtx' table at all. */ if ( !tt_face->vertical_info ) return FT_THROW( Unimplemented_Feature ); #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT /* No fast retrieval for blended MM fonts without 'VVAR' table. */ if ( ( FT_IS_NAMED_INSTANCE( face ) || FT_IS_VARIATION( face ) ) && !( tt_face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) ) return FT_THROW( Unimplemented_Feature ); #endif } /* Proceed to fast advances. */ for ( nn = 0; nn < count; nn++ ) { FT_UShort aw; FT_Short dummy; ( (SFNT_Service)tt_face->sfnt )->get_metrics( tt_face, !horz, start + nn, &dummy, &aw ); FT_TRACE5(( " idx %u: advance %s %d font unit%s\n", start + nn, horz ? "width" : "height", aw, aw == 1 ? "" : "s" )); advances[nn] = aw; } return FT_Err_Ok; } /************************************************************************** * * SERVICE DEFINITIONS * */ #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT /* Service delegation functions following CFF module pattern. */ FT_CALLBACK_DEF( FT_Error ) hvf_set_mm_blend( FT_Face face, /* HVF_Face */ FT_UInt num_coords, FT_Fixed* coords ) { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; HVF_Face hvf_face = (HVF_Face)face; FT_Error error; /* Call the underlying service. */ error = mm->set_mm_blend( face, num_coords, coords ); /* error == -1 means "no change"; early exit. */ if ( error == -1 ) return FT_Err_Ok; /* If successful, refresh our cached HVF coordinates. */ if ( !error ) { FT_Error refresh_error = hvf_refresh_axis_coordinates( hvf_face ); #ifdef FT_DEBUG_LEVEL_TRACE if ( refresh_error ) { /* Don't fail the entire operation, just log the issue. */ FT_TRACE3(( "hvf_set_mm_blend:" " failed to refresh HVF coordinates (error %d)\n", refresh_error )); } #else FT_UNUSED( refresh_error ); #endif } return error; } FT_CALLBACK_DEF( FT_Error ) hvf_get_mm_blend( FT_Face face, /* HVF_Face */ FT_UInt num_coords, FT_Fixed* coords ) { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; return mm->get_mm_blend( face, num_coords, coords ); } FT_CALLBACK_DEF( FT_Error ) hvf_get_mm_var( FT_Face face, /* HVF_Face */ FT_MM_Var** master ) { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; return mm->get_mm_var( face, master ); } FT_CALLBACK_DEF( FT_Error ) hvf_set_var_design( FT_Face face, /* HVF_Face */ FT_UInt num_coords, FT_Fixed* coords ) { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; HVF_Face hvf_face = (HVF_Face)face; FT_Error error; /* Call the underlying service. */ error = mm->set_var_design( face, num_coords, coords ); /* error == -1 means "no change"; early exit. */ if ( error == -1 ) return FT_Err_Ok; /* If successful, refresh our cached HVF coordinates. */ if ( !error || error == -2 ) { FT_Error refresh_error = hvf_refresh_axis_coordinates( hvf_face ); #ifdef FT_DEBUG_LEVEL_TRACE if ( refresh_error ) { /* Don't fail the entire operation, just log the issue. */ FT_TRACE3(( "hvf_set_var_design:" " failed to refresh HVF coordinates (error %d)\n", refresh_error )); } #else FT_UNUSED( refresh_error ); #endif } return error; } FT_CALLBACK_DEF( FT_Error ) hvf_get_var_design( FT_Face face, /* HVF_Face */ FT_UInt num_coords, FT_Fixed* coords ) { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; return mm->get_var_design( face, num_coords, coords ); } FT_CALLBACK_DEF( FT_Error ) hvf_set_named_instance( FT_Face face, /* HVF_Face */ FT_UInt instance_index ) { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; HVF_Face hvf_face = (HVF_Face)face; FT_Error error; /* Call the underlying service. */ error = mm->set_named_instance( face, instance_index ); /* error == -1 means "no change"; early exit. */ if ( error == -1 ) return FT_Err_Ok; /* If successful, refresh our cached HVF coordinates. */ if ( !error ) { FT_Error refresh_error = hvf_refresh_axis_coordinates( hvf_face ); #ifdef FT_DEBUG_LEVEL_TRACE if ( refresh_error ) { /* Don't fail the entire operation, just log the issue. */ FT_TRACE3(( "hvf_set_named_instance:" " failed to refresh HVF coordinates (error %d)\n", refresh_error )); } #else FT_UNUSED( refresh_error ); #endif } return error; } FT_CALLBACK_DEF( FT_Error ) hvf_get_default_named_instance( FT_Face face, /* HVF_Face */ FT_UInt *instance_index ) { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; return mm->get_default_named_instance( face, instance_index ); } FT_CALLBACK_DEF( void ) hvf_construct_ps_name( FT_Face face ) /* HVF_Face */ { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; mm->construct_ps_name( face ); } FT_CALLBACK_DEF( FT_Error ) hvf_load_delta_set_index_mapping( FT_Face face, /* HVF_Face */ FT_ULong offset, GX_DeltaSetIdxMap map, GX_ItemVarStore itemStore, FT_ULong table_len ) { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; return mm->load_delta_set_idx_map( face, offset, map, itemStore, table_len ); } FT_CALLBACK_DEF( FT_Error ) hvf_load_item_variation_store( FT_Face face, /* HVF_Face */ FT_ULong offset, GX_ItemVarStore itemStore ) { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; return mm->load_item_var_store( face, offset, itemStore ); } FT_CALLBACK_DEF( FT_ItemVarDelta ) hvf_get_item_delta( FT_Face face, /* HVF_Face */ GX_ItemVarStore itemStore, FT_UInt outerIndex, FT_UInt innerIndex ) { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; return mm->get_item_delta( face, itemStore, outerIndex, innerIndex ); } FT_CALLBACK_DEF( void ) hvf_done_item_variation_store( FT_Face face, /* HVF_Face */ GX_ItemVarStore itemStore ) { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; mm->done_item_var_store( face, itemStore ); } FT_CALLBACK_DEF( void ) hvf_done_delta_set_index_map( FT_Face face, /* HVF_Face */ GX_DeltaSetIdxMap deltaSetIdxMap ) { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; mm->done_delta_set_idx_map( face, deltaSetIdxMap ); } FT_CALLBACK_DEF( FT_Error ) hvf_get_var_blend( FT_Face face, /* HVF_Face */ FT_UInt* num_coords, FT_Fixed** coords, FT_Fixed** normalizedcoords, FT_MM_Var** mm_var ) { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; return mm->get_var_blend( face, num_coords, coords, normalizedcoords, mm_var ); } FT_CALLBACK_DEF( void ) hvf_done_blend( FT_Face face ) /* HVF_Face */ { TT_Face tt_face = (TT_Face)face; FT_Service_MultiMasters mm = (FT_Service_MultiMasters)tt_face->mm; mm->done_blend( face ); } FT_CALLBACK_DEF( FT_Error ) hvf_hadvance_adjust( FT_Face face, /* HVF_Face */ FT_UInt gindex, FT_Int *avalue ) { TT_Face tt_face = (TT_Face)face; FT_Service_MetricsVariations var = (FT_Service_MetricsVariations) tt_face->tt_var; return var->hadvance_adjust( face, gindex, avalue ); } FT_CALLBACK_DEF( FT_Error ) hvf_vadvance_adjust( FT_Face face, /* HVF_Face */ FT_UInt gindex, FT_Int *avalue ) { TT_Face tt_face = (TT_Face)face; FT_Service_MetricsVariations var = (FT_Service_MetricsVariations) tt_face->tt_var; return var->vadvance_adjust( face, gindex, avalue ); } FT_CALLBACK_DEF( void ) hvf_apply_mvar( FT_Face face ) /* HVF_Face */ { TT_Face tt_face = (TT_Face)face; FT_Service_MetricsVariations var = (FT_Service_MetricsVariations) tt_face->tt_var; var->metrics_adjust( face ); } FT_CALLBACK_DEF( void ) hvf_size_reset_height( FT_Size size ) { /* Delegate to size's face services. */ TT_Face tt_face = (TT_Face)size->face; FT_Service_MetricsVariations var = (FT_Service_MetricsVariations) tt_face->tt_var; if ( var->size_reset ) var->size_reset( size ); } FT_DEFINE_SERVICE_MULTIMASTERSREC( hvf_service_gx_multi_masters, NULL, /* FT_Get_MM_Func get_mm */ NULL, /* FT_Set_MM_Design_Func set_mm_design */ hvf_set_mm_blend, /* FT_Set_MM_Blend_Func set_mm_blend */ hvf_get_mm_blend, /* FT_Get_MM_Blend_Func get_mm_blend */ hvf_get_mm_var, /* FT_Get_MM_Var_Func get_mm_var */ hvf_set_var_design, /* FT_Set_Var_Design_Func set_var_design */ hvf_get_var_design, /* FT_Get_Var_Design_Func get_var_design */ hvf_set_named_instance, /* FT_Set_Named_Instance_Func set_named_instance */ hvf_get_default_named_instance, /* FT_Get_Default_Named_Instance_Func get_default_named_instance */ NULL, /* FT_Set_MM_WeightVector_Func set_mm_weightvector */ NULL, /* FT_Get_MM_WeightVector_Func get_mm_weightvector */ hvf_construct_ps_name, /* FT_Construct_PS_Name_Func construct_ps_name */ hvf_load_delta_set_index_mapping, /* FT_Var_Load_Delta_Set_Idx_Map_Func load_delta_set_idx_map */ hvf_load_item_variation_store, /* FT_Var_Load_Item_Var_Store_Func load_item_variation_store */ hvf_get_item_delta, /* FT_Var_Get_Item_Delta_Func get_item_delta */ hvf_done_item_variation_store, /* FT_Var_Done_Item_Var_Store_Func done_item_variation_store */ hvf_done_delta_set_index_map, /* FT_Var_Done_Delta_Set_Idx_Map_Func done_delta_set_index_map */ hvf_get_var_blend, /* FT_Get_Var_Blend_Func get_var_blend */ hvf_done_blend /* FT_Done_Blend_Func done_blend */ ) FT_DEFINE_SERVICE_METRICSVARIATIONSREC( hvf_service_metrics_variations, hvf_hadvance_adjust, /* FT_HAdvance_Adjust_Func hadvance_adjust */ NULL, /* FT_LSB_Adjust_Func lsb_adjust */ NULL, /* FT_RSB_Adjust_Func rsb_adjust */ hvf_vadvance_adjust, /* FT_VAdvance_Adjust_Func vadvance_adjust */ NULL, /* FT_TSB_Adjust_Func tsb_adjust */ NULL, /* FT_BSB_Adjust_Func bsb_adjust */ NULL, /* FT_VOrg_Adjust_Func vorg_adjust */ hvf_apply_mvar, /* FT_Metrics_Adjust_Func metrics_adjust */ hvf_size_reset_height /* FT_Size_Reset_Func size_reset */ ) #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */ #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT FT_DEFINE_SERVICEDESCREC3( hvf_services, FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_HVF, FT_SERVICE_ID_MULTI_MASTERS, &hvf_service_gx_multi_masters, FT_SERVICE_ID_METRICS_VARIATIONS, &hvf_service_metrics_variations ) #else FT_DEFINE_SERVICEDESCREC1( hvf_services, FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_HVF ) #endif FT_CALLBACK_DEF( FT_Module_Interface ) hvf_get_interface( FT_Module driver, /* HVF_Driver */ const char* hvf_interface ) { FT_Library library; FT_Module_Interface result; FT_Module sfntd; SFNT_Service sfnt; FT_TRACE5(( "hvf_get_interface: Looking up service %s\n", hvf_interface )); result = ft_service_list_lookup( hvf_services, hvf_interface ); if ( result ) return result; FT_TRACE3(( "hvf_get_interface: Did not find service %s; falling back\n", hvf_interface )); if ( !driver ) return NULL; library = driver->library; if ( !library ) return NULL; /* Only return the default interface from the SFNT module. */ sfntd = FT_Get_Module( library, "sfnt" ); if ( sfntd ) { sfnt = (SFNT_Service)( sfntd->clazz->module_interface ); if ( sfnt ) return sfnt->get_interface( driver, hvf_interface ); } return NULL; } #endif /* FT_CONFIG_OPTION_HVF */ /************************************************************************** * * DRIVER INTERFACE * */ #ifdef FT_CONFIG_OPTION_HVF #define PUT_HVF_MODULE( a ) a #else #define PUT_HVF_MODULE( a ) NULL #endif FT_DEFINE_DRIVER( hvf_driver_class, FT_MODULE_FONT_DRIVER | FT_MODULE_DRIVER_SCALABLE | FT_MODULE_DRIVER_HAS_HINTER, sizeof ( FT_DriverRec ), "hvf", /* driver name */ 0x10000L, /* driver version == 1.0 */ 0x20000L, /* driver requires FreeType 2.0 or above */ NULL, /* module-specific interface */ NULL, /* FT_Module_Constructor module_init */ NULL, /* FT_Module_Destructor module_done */ PUT_HVF_MODULE( hvf_get_interface ), /* FT_Module_Requester get_interface */ sizeof ( HVF_FaceRec ), sizeof ( FT_SizeRec ), sizeof ( FT_GlyphSlotRec ), hvf_face_init, /* FT_Face_InitFunc init_face */ PUT_HVF_MODULE( hvf_face_done ), /* FT_Face_DoneFunc done_face */ NULL, /* FT_Size_InitFunc init_size */ NULL, /* FT_Size_DoneFunc done_size */ NULL, /* FT_Slot_InitFunc init_slot */ NULL, /* FT_Slot_DoneFunc done_slot */ hvf_slot_load_glyph, /* FT_Slot_LoadFunc load_glyph */ PUT_HVF_MODULE( hvf_get_kerning ), /* FT_Face_GetKerningFunc get_kerning */ NULL, /* FT_Face_AttachFunc attach_file */ PUT_HVF_MODULE( hvf_get_advances ), /* FT_Face_GetAdvancesFunc get_advances */ NULL, /* FT_Size_RequestFunc request_size */ NULL /* FT_Size_SelectFunc select_size */ ) /* END */