/***************************************************************************/ /* */ /* 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 #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 */