| /***************************************************************************/ |
| /* */ |
| /* gxlayout.c */ |
| /* */ |
| /* AAT/TrueTypeGX based layout engine(body). */ |
| /* */ |
| /* Copyright 2003 by */ |
| /* Masatake YAMATO and Redhat K.K. */ |
| /* */ |
| /* This file 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. */ |
| /* */ |
| /***************************************************************************/ |
| |
| /***************************************************************************/ |
| /* Development of the code in this file is support of */ |
| /* Information-technology Promotion Agency, Japan. */ |
| /***************************************************************************/ |
| |
| #include <ft2build.h> |
| |
| #include FT_LIST_H |
| #include FT_GXLAYOUT_H |
| #include FT_INTERNAL_FTL_TYPES_H |
| |
| #include "gxltypes.h" |
| #include "gxtypes.h" |
| #include "gxaccess.h" |
| #include "gxutils.h" |
| #include "gxlookuptbl.h" |
| #include "gxfeatreg.h" |
| #include "gxlfeatreg.h" |
| #include "gxerrors.h" |
| |
| static int gxl_feature_compare (const void * a, const void * b); |
| static FT_Error gxl_feature_initialize_for_mort ( GXL_Feature feature, |
| FT_Memory memory, |
| GX_Mort mort, |
| GX_Feat feat, |
| FT_UShort * index ); |
| static FT_Error gxl_feature_initialize_for_morx ( GXL_Feature feature, |
| FT_Memory memory, |
| GX_Morx morx, |
| GX_Feat feat, |
| FT_UShort * index ); |
| /* |
| * FEAT |
| */ |
| FT_LOCAL_DEF ( GXL_Setting ) |
| gxl_feature_get_setting_by_value (GXL_Feature feature, FT_UShort value) |
| { |
| FT_Int i; |
| if ( feature->exclusive.exclusive ) |
| { |
| if ( value == feature->exclusive.setting->value ) |
| return feature->exclusive.setting; |
| } |
| else |
| { |
| for ( i = 0; i < feature->nSettings; i++ ) |
| { |
| if ( value == feature->setting[i].value ) |
| return &feature->setting[i]; |
| } |
| } |
| return NULL; |
| } |
| |
| FT_LOCAL_DEF ( GXL_Feature ) |
| gxl_features_request_get_feature_by_type ( GXL_FeaturesRequest request, |
| FT_UShort featureType ) |
| { |
| GXL_Feature feature = NULL; |
| int i; |
| for ( i = 0; i < request->nFeatures; i++ ) |
| { |
| if ( request->feature[i].value == featureType ) |
| { |
| feature = &request->feature[i]; |
| break; |
| } |
| } |
| return feature; |
| } |
| |
| FT_LOCAL_DEF ( void ) |
| gxl_features_request_free( GXL_FeaturesRequest request, FT_Memory memory ) |
| { |
| FT_Int i; |
| GXL_Feature feature; |
| |
| for ( i = request->nFeatures; i > 0; i-- ) |
| { |
| feature = &(request->feature[i - 1]); |
| FT_FREE( feature->setting ); |
| } |
| FT_FREE ( request->feature ); |
| FT_FREE ( request ); |
| } |
| |
| static GXL_Setting |
| gxl_setting_get_exclusive_setting( GXL_Setting setting ) |
| { |
| return setting->feature->exclusive.setting; |
| } |
| |
| static void |
| gxl_setting_set_exclusive_setting( GXL_Setting setting ) |
| { |
| setting->feature->exclusive.setting = setting; |
| } |
| |
| static void |
| gxl_setting_set_exclusive_state( GXL_Setting setting , FT_Bool state ) |
| { |
| if ( state ) |
| gxl_setting_set_exclusive_setting ( setting ); |
| } |
| |
| static FT_Bool |
| gxl_setting_get_exclusive_state ( GXL_Setting setting ) |
| { |
| if ( gxl_setting_get_exclusive_setting (setting) == setting ) |
| return 1; |
| else |
| return 0; |
| } |
| |
| FT_EXPORT ( void ) |
| GXL_FeaturesRequest_Set_Initial_State ( GXL_FeaturesRequest request, |
| GXL_Initial_State initial_state ) |
| { |
| FT_ASSERT( request ); |
| request->initial_state = initial_state; |
| } |
| |
| FT_EXPORT ( GXL_Initial_State ) |
| GXL_FeaturesRequest_Get_Initial_State ( GXL_FeaturesRequest request ) |
| { |
| FT_ASSERT( request ); |
| return request->initial_state; |
| } |
| |
| FT_EXPORT_DEF ( FT_ULong ) |
| GXL_FeaturesRequest_Get_Feature_Count ( GXL_FeaturesRequest request ) |
| { |
| if ( request ) |
| return request->nFeatures; |
| else |
| return 0; |
| } |
| |
| FT_EXPORT_DEF ( GXL_Feature ) |
| GXL_FeaturesRequest_Get_Feature ( GXL_FeaturesRequest request, |
| FT_ULong index) |
| { |
| GXL_Feature feature = NULL; |
| if ( index < GXL_FeaturesRequest_Get_Feature_Count ( request ) ) |
| feature = &(request->feature[index]); |
| return feature; |
| } |
| |
| FT_EXPORT_DEF( FT_Error ) |
| GXL_Feature_Get_Name ( GXL_Feature feature, |
| FT_UShort platform_id, |
| FT_UShort encoding_id, |
| FT_UShort language_id, |
| FT_SfntName *aname ) |
| { |
| GXL_Font font; |
| FT_Face face; |
| font = (GXL_Font)feature->request->root.font; |
| face = font->root.face; |
| |
| if ( ! face ) |
| return GX_Err_Invalid_Face_Handle; |
| |
| if ( ! feature->name.string ) |
| return gx_get_name_from_id ( face, |
| feature->name.index, |
| platform_id, encoding_id, language_id, |
| aname ); |
| else |
| { |
| aname->platform_id = 0; |
| aname->encoding_id = 0; |
| aname->language_id = 0; |
| aname->name_id = 0; |
| aname->string = (FT_Byte*)feature->name.string; |
| aname->string_len = ft_strlen ( feature->name.string ); |
| return GX_Err_Ok; |
| } |
| } |
| |
| FT_EXPORT_DEF( FT_UShort ) |
| GXL_Feature_Get_Setting_Count ( GXL_Feature feature ) |
| { |
| return feature->nSettings; |
| } |
| |
| |
| FT_EXPORT_DEF( GXL_Setting ) |
| GXL_Feature_Get_Setting ( GXL_Feature feature, |
| FT_ULong index ) |
| { |
| GXL_Setting setting = NULL; |
| if ( index < feature->nSettings ) |
| setting = &feature->setting[index]; |
| return setting; |
| } |
| |
| FT_EXPORT_DEF ( FT_Bool ) |
| GXL_Feature_Is_Setting_Exclusive ( GXL_Feature feature ) |
| { |
| return feature->exclusive.exclusive; |
| } |
| |
| |
| FT_EXPORT_DEF( FT_Bool ) |
| GXL_Setting_Get_State ( GXL_Setting setting ) |
| { |
| GXL_Feature feature = setting->feature; |
| if ( GXL_Feature_Is_Setting_Exclusive ( feature ) ) |
| return gxl_setting_get_exclusive_state ( setting ); |
| else |
| return gx_feat_setting_state(setting->value); |
| } |
| |
| FT_EXPORT_DEF( FT_Error ) |
| GXL_Setting_Get_Name ( GXL_Setting setting, |
| FT_UShort platform_id, |
| FT_UShort encoding_id, |
| FT_UShort language_id, |
| FT_SfntName *aname ) |
| { |
| GXL_Font font = (GXL_Font)setting->feature->request->root.font; |
| FT_Face face = font->root.face; |
| |
| if ( ! setting->name.string ) |
| return gx_get_name_from_id ( face, |
| setting->name.index, |
| platform_id, encoding_id, language_id, |
| aname ); |
| else |
| { |
| aname->platform_id = 0; |
| aname->encoding_id = 0; |
| aname->language_id = 0; |
| aname->name_id = 0; |
| aname->string = (FT_Byte*)setting->name.string; |
| aname->string_len = ft_strlen ( setting->name.string ); |
| return GX_Err_Ok; |
| } |
| } |
| |
| FT_EXPORT_DEF( void ) |
| GXL_Setting_Set_State ( GXL_Setting setting, |
| FT_Bool state ) |
| { |
| if ( setting->feature->exclusive.exclusive ) |
| gxl_setting_set_exclusive_state( setting, state ); |
| else if ( state ) |
| setting->value = gx_feat_setting_on( setting->value ); |
| else |
| setting->value = gx_feat_setting_off( setting->value ); |
| } |
| |
| /* |
| * Substitution |
| */ |
| static int |
| gxl_feature_compare (const void * a, const void * b) |
| { |
| return (FT_Long)((GXL_Feature)a)->value - (FT_Long)((GXL_Feature)b)->value; |
| } |
| |
| typedef struct gxl_feature_initialize_for_mort_data_rec_ |
| { |
| FT_Int count; |
| GXL_Feature feature; |
| GX_Feat feat; |
| FT_Memory memory; |
| } gxl_feature_initialize_for_mort_data_rec, *gxl_feature_initialize_for_mort_data; |
| |
| |
| static FT_Error |
| gxl_feature_initialize_for_mort_cb ( GX_MetamorphosisFeatureTable feat_Subtbl, FT_Pointer user ) |
| { |
| FT_Error error; |
| GX_Feature_Registry featreg; |
| gxl_feature_initialize_for_mort_data data = user; |
| FT_Int i; |
| |
| if ( gx_feat_has_feature_type ( data->feat, feat_Subtbl->featureType ) ) |
| { /* In feat table? */ |
| return GX_Err_Ok; |
| } |
| |
| for ( i = 0; i < data->count; i ++ ) |
| { /* Duplication check */ |
| if ( data->feature[i].value == feat_Subtbl->featureType ) |
| return GX_Err_Ok; |
| } |
| |
| if (!( featreg = gx_get_feature_registry ( feat_Subtbl->featureType ) )) |
| { /* Not in feature_registry */ |
| return GX_Err_Ok; |
| } |
| |
| error = gxl_feature_registry_fill_feature( featreg, data->memory, &(data->feature[data->count]) ); |
| if ( error ) |
| goto Exit; |
| data->count++; |
| Exit: |
| return error; |
| } |
| |
| static FT_Error |
| gxl_feature_initialize_for_mort ( GXL_Feature feature, |
| FT_Memory memory, |
| GX_Mort mort, |
| GX_Feat feat, |
| FT_UShort * index ) |
| { |
| FT_Error error; |
| FT_Int i; |
| gxl_feature_initialize_for_mort_data_rec data; |
| data.count = 0; |
| data.feature = feature; |
| data.feat = feat; |
| data.memory = memory; |
| |
| error = gx_mort_foreach_feature ( mort, gxl_feature_initialize_for_mort_cb, &data ); |
| if ( error ) |
| { |
| for ( i = data.count; i > 0; i-- ) |
| FT_FREE (feature[i - 1].setting); |
| return error; |
| } |
| else |
| { |
| *index = data.count; |
| return GX_Err_Ok ; |
| } |
| } |
| |
| typedef struct gxl_feature_initialize_for_morx_data_rec_ |
| { |
| FT_Int count; |
| GXL_Feature feature; |
| GX_Feat feat; |
| FT_Memory memory; |
| } gxl_feature_initialize_for_morx_data_rec, *gxl_feature_initialize_for_morx_data; |
| |
| |
| static FT_Error |
| gxl_feature_initialize_for_morx_cb ( GX_MetamorphosisFeatureTable feat_Subtbl, FT_Pointer user ) |
| { |
| FT_Error error; |
| GX_Feature_Registry featreg; |
| gxl_feature_initialize_for_morx_data data = user; |
| FT_Int i; |
| |
| if ( gx_feat_has_feature_type ( data->feat, feat_Subtbl->featureType ) ) |
| return GX_Err_Ok; |
| |
| for ( i = 0; i < data->count; i ++ ) |
| { /* Duplication check */ |
| if ( data->feature[i].value == feat_Subtbl->featureType ) |
| return GX_Err_Ok; |
| } |
| |
| featreg = gx_get_feature_registry ( feat_Subtbl->featureType ); |
| if ( ! featreg ) |
| return GX_Err_Ok; |
| |
| error = gxl_feature_registry_fill_feature( featreg, data->memory, &(data->feature[data->count]) ); |
| if ( error ) |
| return error; |
| data->count++; |
| return GX_Err_Ok; |
| } |
| |
| static FT_Error |
| gxl_feature_initialize_for_morx ( GXL_Feature feature, |
| FT_Memory memory, |
| GX_Morx morx, |
| GX_Feat feat, |
| FT_UShort * index ) |
| { |
| FT_Error error; |
| FT_Int i; |
| gxl_feature_initialize_for_morx_data_rec data; |
| data.count = 0; |
| data.feature = feature; |
| data.feat = feat; |
| data.memory = memory; |
| error = gx_morx_foreach_feature ( morx, gxl_feature_initialize_for_morx_cb, &data ); |
| if ( error ) |
| { |
| for ( i = data.count; i > 0; i-- ) |
| FT_FREE (feature[i - 1].setting); |
| return error; |
| } |
| else |
| { |
| *index = data.count; |
| return GX_Err_Ok ; |
| } |
| } |
| |
| |
| FT_LOCAL_DEF ( FT_Error ) |
| gxl_get_font ( FT_Face face, FTL_Font * font) |
| { |
| *font = ((GX_Face)face)->extra.data; |
| |
| if ( *font ) |
| return FT_Err_Ok; |
| else |
| return GX_Err_Invalid_Face_Handle; |
| } |
| |
| FT_LOCAL_DEF ( FTL_EngineType ) |
| gxl_get_engine_type ( FT_Face face ) |
| { |
| return FTL_TRUETYPEGX_ENGINE; |
| } |
| |
| FT_LOCAL_DEF ( FT_Error ) |
| gxl_new_features_request( FT_Face face, FTL_FeaturesRequest * ftl_request) |
| { |
| FT_Memory memory = face->driver->root.memory; |
| FT_Error error; |
| GXL_Font font; |
| GXL_FeaturesRequest request; |
| |
| GX_Feat feat; |
| GX_Mort mort; |
| GX_Morx morx; |
| |
| GXL_Feature feature; |
| GXL_Setting setting; |
| FT_UShort default_setting_index; |
| GXL_Setting default_setting; |
| FT_UShort infeat_feat_count, inmort_feat_count = 0, inmorx_feat_count = 0; |
| |
| FT_Int i_feat, i_setting, j_feat, i_mort, i_morx; |
| |
| if (( error = gxl_get_font ( face, (FTL_Font*)&font ) )) |
| return error; |
| |
| if ( FT_NEW ( request ) ) |
| goto Exit; |
| if (( error = FTL_FeaturesRequest_Init ( face, (FTL_FeaturesRequest)request ) )) |
| { |
| FT_FREE( request ); |
| goto Exit; |
| } |
| |
| request->initial_state = GXL_START_OF_TEXT_STATE; |
| |
| feat = font->feat; |
| mort = font->mort; |
| morx = font->morx; |
| |
| if ( !feat ) |
| { |
| error = FT_Err_Ok; |
| (request)->nFeatures = 0; |
| (request)->feature = NULL; |
| *ftl_request = (FTL_FeaturesRequest)request; |
| goto Exit; |
| } |
| |
| if ( mort ) |
| inmort_feat_count = gx_mort_count_feat_not_in_feat( mort, feat ); |
| |
| if ( morx ) |
| inmorx_feat_count = gx_morx_count_feat_not_in_feat( morx, feat ); |
| |
| if ( ( mort == NULL ) && ( morx == NULL ) ) |
| { |
| error = GX_Err_Missing_Glyph_Substitution_Data; |
| goto Failure_request; |
| } |
| |
| infeat_feat_count = feat->featureNameCount; |
| request->nFeatures = infeat_feat_count + inmort_feat_count + inmorx_feat_count; |
| |
| if ( FT_NEW_ARRAY ( request->feature, request->nFeatures ) ) |
| goto Failure_request; |
| for ( i_feat = 0; i_feat < infeat_feat_count; i_feat++ ) |
| { |
| feature = (&request->feature[i_feat]); |
| feature->value = feat->names[i_feat].feature; |
| feature->exclusive.exclusive = ((feat->names[i_feat].featureFlags) |
| & GX_FEAT_MASK_EXCLUSIVE_SETTINGS) |
| ? 1: 0; |
| feature->exclusive.setting = NULL; /* dummy */ |
| feature->name.index = feat->names[i_feat].nameIndex; |
| feature->name.string = NULL; |
| feature->request = request; |
| feature->nSettings = feat->names[i_feat].nSettings; |
| if ( FT_NEW_ARRAY ( feature->setting, feature->nSettings ) ) |
| goto Failure_loop_feat; |
| for ( i_setting = 0; i_setting < feature->nSettings; i_setting++ ) |
| { |
| setting = &feature->setting[i_setting]; |
| setting->value = feat->names[i_feat].settingName[i_setting].setting; |
| setting->name.index = feat->names[i_feat].settingName[i_setting].nameIndex; |
| setting->name.string = NULL; |
| setting->feature = feature; |
| } |
| if ( feature->exclusive.exclusive ) |
| { |
| default_setting_index = 0; |
| if ( (feat->names[i_feat].featureFlags)&GX_FEAT_MASK_DYNAMIC_DEFAULT ) |
| default_setting_index = (feat->names[i_feat].featureFlags)&GX_FEAT_MASK_DEFAULT_SETTING; |
| default_setting = &feature->setting[default_setting_index]; |
| if ( default_setting_index >= feature->nSettings ) |
| { |
| error = GX_Err_Invalid_File_Format; |
| goto Failure_loop_feat; |
| } |
| GXL_Setting_Set_State ( default_setting, 1 ); |
| } |
| else if ( feat->names[i_feat].featureFlags&GX_FEAT_MASK_DYNAMIC_DEFAULT ) |
| { |
| FT_Bool state; |
| default_setting_index = feat->names[i_feat].featureFlags&GX_FEAT_MASK_DEFAULT_SETTING; |
| |
| if ( default_setting_index < feature->nSettings ) |
| { |
| default_setting = &feature->setting[default_setting_index]; |
| state = 1; |
| } |
| else if ( default_setting_index == 1 ) |
| { |
| /* If default_setting_index is 1 but nSettings is also 1, |
| there is not setting for default_setting_index in the |
| font file. In this case setting[0] should be off. */ |
| default_setting = &feature->setting[0]; |
| state = 0; |
| } |
| else |
| { |
| error = GX_Err_Invalid_File_Format; |
| goto Failure_loop_feat; |
| } |
| GXL_Setting_Set_State ( default_setting, state ); |
| } |
| else |
| { /* TODO: getting from mort's and morx's default? */ |
| default_setting_index = 0; |
| default_setting = &feature->setting[default_setting_index]; |
| GXL_Setting_Set_State ( default_setting, 1 ); |
| } |
| } |
| |
| if ( inmort_feat_count ) |
| { |
| inmort_feat_count = 0; |
| feature = (&request->feature[i_feat]); |
| if (( error = gxl_feature_initialize_for_mort (feature, memory, mort, feat, &inmort_feat_count) )) |
| goto Failure_loop_feat; |
| |
| for ( i_mort = 0; i_mort < inmort_feat_count; i_mort++ ) |
| feature[i_mort].request = request; |
| |
| i_feat += inmort_feat_count; |
| request->nFeatures = i_feat; |
| feature = (&request->feature[inmort_feat_count]); |
| } |
| |
| if ( inmorx_feat_count ) |
| { |
| inmorx_feat_count = 0; |
| feature = (&(request)->feature[i_feat]); |
| if (( error = gxl_feature_initialize_for_morx (feature, memory, morx, feat, &inmorx_feat_count) )) |
| goto Failure_loop_feat; |
| |
| for ( i_morx = 0; i_morx < inmorx_feat_count; i_morx++ ) |
| feature[i_morx].request = request; |
| i_feat += inmorx_feat_count; |
| request->nFeatures = i_feat; |
| } |
| |
| if ( inmort_feat_count || inmorx_feat_count ) |
| { |
| ft_qsort(request->feature, |
| request->nFeatures, |
| sizeof (*(request->feature)), |
| gxl_feature_compare ); |
| for ( i_feat = 0; i_feat < request->nFeatures; i_feat++ ) |
| { |
| feature = &request->feature[i_feat]; |
| for ( i_setting = 0; i_setting < feature->nSettings; i_setting++ ) |
| feature->setting[i_setting].feature = feature; |
| } |
| } |
| *ftl_request = (FTL_FeaturesRequest)request; |
| Exit: |
| return error; |
| Failure_loop_feat: |
| for ( j_feat = i_feat; j_feat > 0; j_feat-- ) |
| FT_FREE( request->feature[j_feat - 1].setting ); |
| Failure_request: |
| FT_FREE(request); |
| return error; |
| } |
| |
| FT_LOCAL_DEF ( FT_Error ) |
| gxl_done_features_request( FTL_FeaturesRequest request) |
| { |
| FTL_Font font; |
| FT_Face face; |
| |
| FT_Memory memory; |
| |
| FT_ASSERT( request ); |
| font = request->font; |
| FT_ASSERT( font ); |
| face = font->face; |
| if ( !face ) |
| return GX_Err_Invalid_Argument; |
| memory = face->driver->root.memory; |
| gxl_features_request_free( (GXL_FeaturesRequest)request, memory ); |
| return FTL_FeaturesRequest_Finalize ( request ); |
| } |
| |
| FT_LOCAL_DEF ( FT_Error ) |
| gxl_copy_features_request( FTL_FeaturesRequest from, |
| FTL_FeaturesRequest to) |
| { |
| FT_Error error; |
| FT_Int i_feat, i_setting; |
| GXL_Feature from_feature, to_feature; |
| GXL_Setting from_setting, to_setting; |
| |
| if (( error = FTL_FeaturesRequest_Copy( from, to ) )) |
| return error; |
| |
| for ( i_feat = 0; i_feat < ((GXL_FeaturesRequest)from)->nFeatures; i_feat++ ) |
| { |
| from_feature = GXL_FeaturesRequest_Get_Feature((GXL_FeaturesRequest)from, |
| i_feat); |
| to_feature = GXL_FeaturesRequest_Get_Feature((GXL_FeaturesRequest)to, |
| i_feat); |
| |
| for ( i_setting = 0; |
| i_setting < GXL_Feature_Get_Setting_Count(from_feature); |
| i_setting++ ) |
| { |
| from_setting = GXL_Feature_Get_Setting ( from_feature, i_setting ); |
| to_setting = GXL_Feature_Get_Setting ( to_feature, i_setting ); |
| GXL_Setting_Set_State(to_setting, |
| GXL_Setting_Get_State(from_setting)); |
| } |
| } |
| return GX_Err_Ok; |
| } |
| |
| FT_LOCAL_DEF ( FT_UShort ) |
| gxl_get_ligature_caret_count ( FT_Face face, |
| FT_UShort glyphID ) |
| { |
| FTL_Font font; |
| GX_Lcar lcar; |
| GX_LigCaretClassEntry class_entry; |
| |
| if ( FTL_Get_Font( face, &font ) ) |
| return 0; |
| lcar = ((GXL_Font)font)->lcar; |
| if ( !lcar ) |
| return 0; |
| class_entry = gx_lcar_get( lcar, glyphID ); |
| if ( class_entry ) |
| return class_entry->count; |
| else |
| return 0; |
| } |
| |
| /* TODO: return an error */ |
| FT_LOCAL_DEF ( FT_UShort ) |
| gxl_get_ligature_caret_division( FT_Face face, |
| FT_UShort glyph_id, |
| FT_UShort nth ) |
| { |
| FTL_Font font; |
| GX_Lcar lcar; |
| GX_LigCaretClassEntry class_entry; |
| FT_UShort partials; |
| FTL_Direction direction; |
| FT_Outline *outline; |
| FT_Vector point; |
| if ( FTL_Get_Font( face, &font ) ) |
| return 0; |
| |
| lcar = ((GXL_Font)font)->lcar; |
| if ( !lcar ) |
| return 0; |
| |
| class_entry = gx_lcar_get( lcar, glyph_id ); |
| if ( !class_entry ) |
| return 0; |
| if ( nth >= class_entry->count ) |
| return 0; |
| |
| partials = class_entry->partials[nth]; |
| if ( lcar->format == GX_LCAR_DISTANCE ) |
| return partials; |
| |
| /* font -> feature_requst -> direction */ |
| direction = FTL_Get_FeaturesRequest_Direction ( font->features_request ); |
| |
| /* glyph_id -> glyph -> outline -> point -> x or y */ |
| if ( FT_Load_Glyph ( face, glyph_id, |
| FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP ) ) |
| return 0; |
| |
| outline = &face->glyph->outline; |
| if ( ! ( partials < outline->n_points ) ) |
| return 0; |
| point = outline->points[partials]; |
| |
| /* TODO: Are the unit for the outline and that |
| for ligature caret same? */ |
| if ( direction == FTL_HORIZONTAL ) |
| return (FT_UShort)point.x; |
| else |
| return (FT_UShort)point.y; |
| } |
| |
| FT_LOCAL_DEF ( FT_Error ) |
| gxl_substitute_glyphs ( FT_Face face, |
| FTL_FeaturesRequest request, |
| FTL_GlyphArray in, |
| FTL_GlyphArray out ) |
| { |
| FT_Error error; |
| GXL_Font font; |
| GX_Mort mort; |
| GX_Morx morx; |
| |
| if (( error = FTL_Get_Font ( face, (FTL_Font*)&font ))) |
| return error; |
| |
| mort = font->mort; |
| morx = font->morx; |
| if ( mort ) |
| return gx_mort_substitute_glyph(mort, (GXL_FeaturesRequest)request, in, out ); |
| else if ( morx ) |
| return gx_morx_substitute_glyph(morx, (GXL_FeaturesRequest)request, in, out ); |
| else |
| return FT_Err_Invalid_Argument; |
| } |
| |
| /* END */ |