diff options
Diffstat (limited to 'pango/pango-ot-info.c')
-rw-r--r-- | pango/pango-ot-info.c | 680 |
1 files changed, 680 insertions, 0 deletions
diff --git a/pango/pango-ot-info.c b/pango/pango-ot-info.c new file mode 100644 index 00000000..7ea83933 --- /dev/null +++ b/pango/pango-ot-info.c @@ -0,0 +1,680 @@ +/* Pango + * pango-ot-info.c: Store tables for OpenType + * + * Copyright (C) 2000 Red Hat Software + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> + +#include "pango-ot-private.h" +#include "pango-utils.h" +#include FT_MODULE_H + +static void pango_ot_info_class_init (GObjectClass *object_class); +static void pango_ot_info_finalize (GObject *object); + +static GObjectClass *parent_class; + +enum +{ + INFO_LOADED_GDEF = 1 << 0, + INFO_LOADED_GSUB = 1 << 1, + INFO_LOADED_GPOS = 1 << 2 +}; + +GType +pango_ot_info_get_type (void) +{ + static GType object_type = 0; + + if (!object_type) + { + static const GTypeInfo object_info = + { + sizeof (PangoOTInfoClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc)pango_ot_info_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PangoOTInfo), + 0, /* n_preallocs */ + NULL, /* init */ + NULL, /* value_table */ + }; + + object_type = g_type_register_static (G_TYPE_OBJECT, + I_("PangoOTInfo"), + &object_info, 0); + } + + return object_type; +} + +static void +pango_ot_info_class_init (GObjectClass *object_class) +{ + parent_class = g_type_class_peek_parent (object_class); + + object_class->finalize = pango_ot_info_finalize; +} + +static void +pango_ot_info_finalize (GObject *object) +{ + PangoOTInfo *info = PANGO_OT_INFO (object); + + if (info->gdef) + { + TT_Done_GDEF_Table (info->gdef); + info->gdef = NULL; + } + if (info->gsub) + { + TT_Done_GSUB_Table (info->gsub); + info->gsub = NULL; + } + if (info->gpos) + { + TT_Done_GPOS_Table (info->gpos); + info->gpos = NULL; + } + + parent_class->finalize (object); +} + +static void +pango_ot_info_finalizer (void *object) +{ + FT_Face face = object; + PangoOTInfo *info = face->generic.data; + + info->face = NULL; + g_object_unref (info); +} + +/** + * pango_ot_info_get: + * @face: a #FT_Face. + * @returns: the #PangoOTInfo for @face. This object will + * have the same lifetime as @face. + * + * Returns the #PangoOTInfo structure for the given FreeType font. + * + * Since: 1.2 + **/ +PangoOTInfo * +pango_ot_info_get (FT_Face face) +{ + PangoOTInfo *info; + + if (face->generic.data) + return face->generic.data; + else + { + info = face->generic.data = g_object_new (PANGO_TYPE_OT_INFO, NULL); + face->generic.finalizer = pango_ot_info_finalizer; + + info->face = face; + } + + return info; +} + +/* There must be be a better way to do this + */ +static gboolean +is_truetype (FT_Face face) +{ + return FT_IS_SFNT(face); +} + +typedef struct _GlyphInfo GlyphInfo; + +struct _GlyphInfo { + FT_UShort glyph; + FT_UShort class; +}; + +static int +compare_glyph_info (gconstpointer a, + gconstpointer b) +{ + const GlyphInfo *info_a = a; + const GlyphInfo *info_b = b; + + return (info_a->glyph < info_b->glyph) ? -1 : + (info_a->glyph == info_b->glyph) ? 0 : 1; +} + +/* Make a guess at the appropriate class for a glyph given + * a character code that maps to the glyph + */ +static gboolean +get_glyph_class (gunichar charcode, + FT_UShort *class) +{ + /* For characters mapped into the Arabic Presentation forms, using properties + * derived as we apply GSUB substitutions will be more reliable + */ + if ((charcode >= 0xFB50 && charcode <= 0xFDFF) || /* Arabic Presentation Forms-A */ + (charcode >= 0xFE70 && charcode <= 0XFEFF)) /* Arabic Presentation Forms-B */ + return FALSE; + + switch (g_unichar_type (charcode)) + { + case G_UNICODE_COMBINING_MARK: + case G_UNICODE_ENCLOSING_MARK: + case G_UNICODE_NON_SPACING_MARK: + *class = 3; /* Mark glyph (non-spacing combining glyph) */ + return TRUE; + case G_UNICODE_UNASSIGNED: + case G_UNICODE_PRIVATE_USE: + return FALSE; /* Unknown, don't assign a class; classes get + * propagated during GSUB application */ + default: + *class = 1; /* Base glyph (single character, spacing glyph) */ + return TRUE; + } +} + +static gboolean +set_unicode_charmap (FT_Face face) +{ + int charmap; + + for (charmap = 0; charmap < face->num_charmaps; charmap++) + if (face->charmaps[charmap]->encoding == ft_encoding_unicode) + { + FT_Error error = FT_Set_Charmap(face, face->charmaps[charmap]); + return error == FT_Err_Ok; + } + + return FALSE; +} + +/* Synthesize a GDEF table using the font's charmap and the + * unicode property database. We'll fill in class definitions + * for glyphs not in the charmap as we walk through the tables. + */ +static void +synthesize_class_def (PangoOTInfo *info) +{ + GArray *glyph_infos; + FT_UShort *glyph_indices; + FT_UShort *classes; + FT_ULong charcode; + FT_UInt glyph; + unsigned int i, j; + FT_CharMap old_charmap; + + old_charmap = info->face->charmap; + + if (!old_charmap || !old_charmap->encoding != ft_encoding_unicode) + if (!set_unicode_charmap (info->face)) + return; + + glyph_infos = g_array_new (FALSE, FALSE, sizeof (GlyphInfo)); + + /* Collect all the glyphs in the charmap, and guess + * the appropriate classes for them + */ + charcode = FT_Get_First_Char (info->face, &glyph); + while (glyph != 0) + { + GlyphInfo glyph_info; + + if (glyph <= 65535) + { + glyph_info.glyph = glyph; + if (get_glyph_class (charcode, &glyph_info.class)) + g_array_append_val (glyph_infos, glyph_info); + } + + charcode = FT_Get_Next_Char (info->face, charcode, &glyph); + } + + /* Sort and remove duplicates + */ + g_array_sort (glyph_infos, compare_glyph_info); + + glyph_indices = g_new (FT_UShort, glyph_infos->len); + classes = g_new (FT_UShort, glyph_infos->len); + + for (i = 0, j = 0; i < glyph_infos->len; i++) + { + GlyphInfo *info = &g_array_index (glyph_infos, GlyphInfo, i); + + if (j == 0 || info->glyph != glyph_indices[j - 1]) + { + glyph_indices[j] = info->glyph; + classes[j] = info->class; + + j++; + } + } + + g_array_free (glyph_infos, TRUE); + + TT_GDEF_Build_ClassDefinition (info->gdef, info->face->num_glyphs, j, + glyph_indices, classes); + + g_free (glyph_indices); + g_free (classes); + + if (old_charmap && info->face->charmap != old_charmap) + FT_Set_Charmap (info->face, old_charmap); +} + +TTO_GDEF +pango_ot_info_get_gdef (PangoOTInfo *info) +{ + g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL); + + if (!(info->loaded & INFO_LOADED_GDEF)) + { + FT_Error error; + + info->loaded |= INFO_LOADED_GDEF; + + if (is_truetype (info->face)) + { + error = TT_Load_GDEF_Table (info->face, &info->gdef); + + if (error && error != TT_Err_Table_Missing) + g_warning ("Error loading GDEF table %d", error); + + if (!info->gdef) + error = TT_New_GDEF_Table (info->face, &info->gdef); + + if (info->gdef && !info->gdef->GlyphClassDef.loaded) + synthesize_class_def (info); + } + } + + return info->gdef; +} + +TTO_GSUB +pango_ot_info_get_gsub (PangoOTInfo *info) +{ + g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL); + + if (!(info->loaded & INFO_LOADED_GSUB)) + { + FT_Error error; + TTO_GDEF gdef = pango_ot_info_get_gdef (info); + + info->loaded |= INFO_LOADED_GSUB; + + if (is_truetype (info->face)) + { + error = TT_Load_GSUB_Table (info->face, &info->gsub, gdef); + + if (error && error != TT_Err_Table_Missing) + g_warning ("Error loading GSUB table %d", error); + } + } + + return info->gsub; +} + +TTO_GPOS +pango_ot_info_get_gpos (PangoOTInfo *info) +{ + g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL); + + if (!(info->loaded & INFO_LOADED_GPOS)) + { + FT_Error error; + TTO_GDEF gdef = pango_ot_info_get_gdef (info); + + info->loaded |= INFO_LOADED_GPOS; + + if (is_truetype (info->face)) + { + error = TT_Load_GPOS_Table (info->face, &info->gpos, gdef); + + if (error && error != TT_Err_Table_Missing) + g_warning ("Error loading GPOS table %d", error); + } + } + + return info->gpos; +} + +static gboolean +get_tables (PangoOTInfo *info, + PangoOTTableType table_type, + TTO_ScriptList **script_list, + TTO_FeatureList **feature_list) +{ + if (table_type == PANGO_OT_TABLE_GSUB) + { + TTO_GSUB gsub = pango_ot_info_get_gsub (info); + + if (!gsub) + return FALSE; + else + { + if (script_list) + *script_list = &gsub->ScriptList; + if (feature_list) + *feature_list = &gsub->FeatureList; + return TRUE; + } + } + else + { + TTO_GPOS gpos = pango_ot_info_get_gpos (info); + + if (!gpos) + return FALSE; + else + { + if (script_list) + *script_list = &gpos->ScriptList; + if (feature_list) + *feature_list = &gpos->FeatureList; + return TRUE; + } + } +} + +/** + * pango_ot_info_find_script: + * @info: a #PangoOTInfo. + * @table_type: the table type to obtain information about. + * @script_tag: the tag of the script to find. + * @script_index: location to store the index of the script, or %NULL. + * @returns: %TRUE if the script was found. + * + * Finds the index of a script. + **/ +gboolean +pango_ot_info_find_script (PangoOTInfo *info, + PangoOTTableType table_type, + PangoOTTag script_tag, + guint *script_index) +{ + TTO_ScriptList *script_list; + int i; + + g_return_val_if_fail (PANGO_IS_OT_INFO (info), FALSE); + + if (!get_tables (info, table_type, &script_list, NULL)) + return FALSE; + + for (i=0; i < script_list->ScriptCount; i++) + { + if (script_list->ScriptRecord[i].ScriptTag == script_tag) + { + if (script_index) + *script_index = i; + + return TRUE; + } + } + + return FALSE; +} + +/** + * pango_ot_info_find_language: + * @info: a #PangoOTInfo. + * @table_type: the table type to obtain information about. + * @script_index: the index of the script whose languages are searched. + * @language_tag: the tag of the language to find. + * @language_index: location to store the index of the language, or %NULL. + * @required_feature_index: location to store the required feature index of + * the language, or %NULL. + * @returns: %TRUE if the language was found. + * + * Finds the index of a language and its required feature index. + **/ +gboolean +pango_ot_info_find_language (PangoOTInfo *info, + PangoOTTableType table_type, + guint script_index, + PangoOTTag language_tag, + guint *language_index, + guint *required_feature_index) +{ + TTO_ScriptList *script_list; + TTO_Script *script; + int i; + + g_return_val_if_fail (PANGO_IS_OT_INFO (info), FALSE); + + if (!get_tables (info, table_type, &script_list, NULL)) + return FALSE; + + g_return_val_if_fail (script_index < script_list->ScriptCount, FALSE); + + script = &script_list->ScriptRecord[script_index].Script; + + for (i = 0; i < script->LangSysCount; i++) + { + if (script->LangSysRecord[i].LangSysTag == language_tag) + { + if (language_index) + *language_index = i; + if (required_feature_index) + *required_feature_index = script->LangSysRecord[i].LangSys.ReqFeatureIndex; + return TRUE; + } + } + + return FALSE; +} + +/** + * pango_ot_info_find_feature: + * @info: a #PangoOTInfo. + * @table_type: the table type to obtain information about. + * @feature_tag: the tag of the feature to find. + * @script_index: the index of the script. + * @language_index: the index of the language whose features are searched, + * or 0xffff to use the default language of the script. + * @feature_index: location to store the index of the feature, or %NULL. + * @returns: %TRUE if the feature was found. + * + * Finds the index of a feature. + **/ +gboolean +pango_ot_info_find_feature (PangoOTInfo *info, + PangoOTTableType table_type, + PangoOTTag feature_tag, + guint script_index, + guint language_index, + guint *feature_index) +{ + TTO_ScriptList *script_list; + TTO_FeatureList *feature_list; + TTO_Script *script; + TTO_LangSys *lang_sys; + + int i; + + g_return_val_if_fail (PANGO_IS_OT_INFO (info), FALSE); + + if (!get_tables (info, table_type, &script_list, &feature_list)) + return FALSE; + + g_return_val_if_fail (script_index < script_list->ScriptCount, FALSE); + + script = &script_list->ScriptRecord[script_index].Script; + + if (language_index == 0xffff) + lang_sys = &script->DefaultLangSys; + else + { + g_return_val_if_fail (language_index < script->LangSysCount, FALSE); + lang_sys = &script->LangSysRecord[language_index].LangSys; + } + + for (i = 0; i < lang_sys->FeatureCount; i++) + { + FT_UShort index = lang_sys->FeatureIndex[i]; + + if (feature_list->FeatureRecord[index].FeatureTag == feature_tag) + { + if (feature_index) + *feature_index = index; + + return TRUE; + } + } + + return FALSE; +} + +/** + * pango_ot_info_list_scripts: + * @info: a #PangoOTInfo. + * @table_type: the table type to obtain information about. + * @returns: a newly-allocated array containing the tags of the + * available scripts. + * + * Obtains the list of available scripts. + **/ +PangoOTTag * +pango_ot_info_list_scripts (PangoOTInfo *info, + PangoOTTableType table_type) +{ + PangoOTTag *result; + TTO_ScriptList *script_list; + int i; + + g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL); + + if (!get_tables (info, table_type, &script_list, NULL)) + return NULL; + + result = g_new (PangoOTTag, script_list->ScriptCount + 1); + + for (i=0; i < script_list->ScriptCount; i++) + result[i] = script_list->ScriptRecord[i].ScriptTag; + + result[i] = 0; + + return result; +} + +/** + * pango_ot_info_list_languages: + * @info: a #PangoOTInfo. + * @table_type: the table type to obtain information about. + * @script_index: the index of the script to list languages for. + * @language_tag: unused parameter. + * @returns: a newly-allocated array containing the tags of the + * available languages. + * + * Obtains the list of available languages for a given script. + **/ +PangoOTTag * +pango_ot_info_list_languages (PangoOTInfo *info, + PangoOTTableType table_type, + guint script_index, + PangoOTTag language_tag) +{ + PangoOTTag *result; + TTO_ScriptList *script_list; + TTO_Script *script; + int i; + + g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL); + + if (!get_tables (info, table_type, &script_list, NULL)) + return NULL; + + g_return_val_if_fail (script_index < script_list->ScriptCount, NULL); + + script = &script_list->ScriptRecord[script_index].Script; + + result = g_new (PangoOTTag, script->LangSysCount + 1); + + for (i = 0; i < script->LangSysCount; i++) + result[i] = script->LangSysRecord[i].LangSysTag; + + result[i] = 0; + + return result; +} + +/** + * pango_ot_info_list_features: + * @info: a #PangoOTInfo. + * @table_type: the table type to obtain information about. + * @tag: unused parameter. + * @script_index: the index of the script to obtain information about. + * @language_index: the indes of the language to list features for, or + * 0xffff, to list features for the default language of the script. + * @returns: a newly-allocated array containing the tags of the available + * features. + * + * Obtains the list of features for the given language of the given script. + **/ +PangoOTTag * +pango_ot_info_list_features (PangoOTInfo *info, + PangoOTTableType table_type, + PangoOTTag tag, + guint script_index, + guint language_index) +{ + PangoOTTag *result; + + TTO_ScriptList *script_list; + TTO_FeatureList *feature_list; + TTO_Script *script; + TTO_LangSys *lang_sys; + + int i; + + g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL); + + if (!get_tables (info, table_type, &script_list, &feature_list)) + return NULL; + + g_return_val_if_fail (script_index < script_list->ScriptCount, NULL); + + script = &script_list->ScriptRecord[script_index].Script; + + if (language_index == 0xffff) + lang_sys = &script->DefaultLangSys; + else + { + g_return_val_if_fail (language_index < script->LangSysCount, NULL); + lang_sys = &script->LangSysRecord[language_index].LangSys; + } + + result = g_new (PangoOTTag, lang_sys->FeatureCount + 1); + + for (i = 0; i < lang_sys->FeatureCount; i++) + { + FT_UShort index = lang_sys->FeatureIndex[i]; + + result[i] = feature_list->FeatureRecord[index].FeatureTag; + } + + result[i] = 0; + + return result; +} + + |