/* Pango * pango-ot-ruleset.c: Shaping using OpenType features * * 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-impl-utils.h" static void pango_ot_ruleset_finalize (GObject *object); G_DEFINE_TYPE (PangoOTRuleset, pango_ot_ruleset, G_TYPE_OBJECT); static void pango_ot_ruleset_class_init (PangoOTRulesetClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = pango_ot_ruleset_finalize; } static void pango_ot_ruleset_init (PangoOTRuleset *ruleset) { ruleset->rules = g_array_new (FALSE, FALSE, sizeof (PangoOTRule)); ruleset->script_index[0] = PANGO_OT_NO_SCRIPT; ruleset->script_index[1] = PANGO_OT_NO_SCRIPT; ruleset->language_index[0] = PANGO_OT_DEFAULT_LANGUAGE; ruleset->language_index[1] = PANGO_OT_DEFAULT_LANGUAGE; } static void pango_ot_ruleset_finalize (GObject *object) { PangoOTRuleset *ruleset = PANGO_OT_RULESET (object); g_array_free (ruleset->rules, TRUE); if (ruleset->info) g_object_remove_weak_pointer (G_OBJECT (ruleset->info), (gpointer *)(void *)&ruleset->info); G_OBJECT_CLASS (pango_ot_ruleset_parent_class)->finalize (object); } /** * pango_ot_ruleset_get_for_description: * @info: a #PangoOTInfo. * @desc: a #PangoOTRulesetDescription. * * Returns a ruleset for the given OpenType info and ruleset * description. Rulesets are created on demand using * pango_ot_ruleset_new_from_description(). * The returned ruleset should not be modified or destroyed. * * The static feature map members of @desc should be alive as * long as @info is. * * Return value: the #PangoOTRuleset for @desc. This object will have * the same lifetime as @info. * * Since: 1.18 **/ const PangoOTRuleset * pango_ot_ruleset_get_for_description (PangoOTInfo *info, const PangoOTRulesetDescription *desc) { PangoOTRuleset *ruleset; static GQuark rulesets_quark = 0; GHashTable *rulesets; g_return_val_if_fail (info != NULL, NULL); g_return_val_if_fail (desc != NULL, NULL); if (!rulesets_quark) rulesets_quark = g_quark_from_string ("pango-info-rulesets"); rulesets = g_object_get_qdata (G_OBJECT (info), rulesets_quark); if (!rulesets) { rulesets = g_hash_table_new_full ((GHashFunc) pango_ot_ruleset_description_hash, (GEqualFunc) pango_ot_ruleset_description_equal, (GDestroyNotify) pango_ot_ruleset_description_free, (GDestroyNotify) g_object_unref); g_object_set_qdata_full (G_OBJECT (info), rulesets_quark, rulesets, (GDestroyNotify) g_hash_table_destroy); } ruleset = g_hash_table_lookup (rulesets, desc); if (!ruleset) { ruleset = pango_ot_ruleset_new_from_description (info, desc); g_hash_table_insert (rulesets, pango_ot_ruleset_description_copy (desc), ruleset); } return ruleset; } /** * pango_ot_ruleset_new: * @info: a #PangoOTInfo. * * Creates a new #PangoOTRuleset for the given OpenType info. * * Return value: the newly allocated #PangoOTRuleset, which * should be freed with g_object_unref(). **/ PangoOTRuleset * pango_ot_ruleset_new (PangoOTInfo *info) { PangoOTRuleset *ruleset; g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL); ruleset = g_object_new (PANGO_TYPE_OT_RULESET, NULL); ruleset->info = info; g_object_add_weak_pointer (G_OBJECT (ruleset->info), (gpointer *)(void*)&ruleset->info); return ruleset; } /** * pango_ot_ruleset_new_for: * @info: a #PangoOTInfo. * @script: a #PangoScript. * @language: a #PangoLanguage. * * Creates a new #PangoOTRuleset for the given OpenType info, script, and * language. * * This function is part of a convenience scheme that highly simplifies * using a #PangoOTRuleset to represent features for a specific pair of script * and language. So one can use this function passing in the script and * language of interest, and later try to add features to the ruleset by just * specifying the feature name or tag, without having to deal with finding * script, language, or feature indices manually. * * In excess to what pango_ot_ruleset_new() does, this function will: * * * Find the #PangoOTTag script and language tags associated with * @script and @language using pango_ot_tag_from_script() and * pango_ot_tag_from_language(), * * * For each of table types %PANGO_OT_TABLE_GSUB and %PANGO_OT_TABLE_GPOS, * find the script index of the script tag found and the language * system index of the language tag found in that script system, using * pango_ot_info_find_script() and pango_ot_info_find_language(), * * * For found language-systems, if they have required feature * index, add that feature to the ruleset using * pango_ot_ruleset_add_feature(), * * * Remember found script and language indices for both table types, * and use them in future pango_ot_ruleset_maybe_add_feature() and * pango_ot_ruleset_maybe_add_features(). * * * * Because of the way return values of pango_ot_info_find_script() and * pango_ot_info_find_language() are ignored, this function automatically * finds and uses the 'DFLT' script and the default language-system. * * Return value: the newly allocated #PangoOTRuleset, which * should be freed with g_object_unref(). * * Since: 1.18 **/ PangoOTRuleset * pango_ot_ruleset_new_for (PangoOTInfo *info, PangoScript script, PangoLanguage *language) { PangoOTRuleset *ruleset; PangoOTTag script_tag, language_tag; PangoOTTableType table_type; g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL); ruleset = pango_ot_ruleset_new (info); script_tag = pango_ot_tag_from_script (script); language_tag = pango_ot_tag_from_language (language); for (table_type = PANGO_OT_TABLE_GSUB; table_type <= PANGO_OT_TABLE_GPOS; table_type++) { guint script_index, language_index, feature_index; pango_ot_info_find_script (ruleset->info, table_type, script_tag, &script_index); pango_ot_info_find_language (ruleset->info, table_type, script_index, language_tag, &language_index, &feature_index); ruleset->script_index[table_type] = script_index; ruleset->language_index[table_type] = language_index; /* add required feature of the language */ pango_ot_ruleset_add_feature (ruleset, table_type, feature_index, PANGO_OT_ALL_GLYPHS); } return ruleset; } /** * pango_ot_ruleset_new_from_description: * @info: a #PangoOTInfo. * @desc: a #PangoOTRulesetDescription. * * Creates a new #PangoOTRuleset for the given OpenType infor and * matching the given ruleset description. * * This is a convenience function that calls pango_ot_ruleset_new_for() and * adds the static GSUB/GPOS features to the resulting ruleset, followed by * adding other features to both GSUB and GPOS. * * The static feature map members of @desc should be alive as * long as @info is. * * Return value: the newly allocated #PangoOTRuleset, which * should be freed with g_object_unref(). * * Since: 1.18 **/ PangoOTRuleset * pango_ot_ruleset_new_from_description (PangoOTInfo *info, const PangoOTRulesetDescription *desc) { PangoOTRuleset *ruleset; g_return_val_if_fail (info != NULL, NULL); g_return_val_if_fail (desc != NULL, NULL); ruleset = pango_ot_ruleset_new_for (info, desc->script, desc->language); if (desc->n_static_gsub_features) pango_ot_ruleset_maybe_add_features (ruleset, PANGO_OT_TABLE_GSUB, desc->static_gsub_features, desc->n_static_gsub_features); if (desc->n_static_gpos_features) pango_ot_ruleset_maybe_add_features (ruleset, PANGO_OT_TABLE_GPOS, desc->static_gpos_features, desc->n_static_gpos_features); if (desc->n_other_features) { pango_ot_ruleset_maybe_add_features (ruleset, PANGO_OT_TABLE_GSUB, desc->other_features, desc->n_other_features); pango_ot_ruleset_maybe_add_features (ruleset, PANGO_OT_TABLE_GPOS, desc->other_features, desc->n_other_features); } return ruleset; } /** * pango_ot_ruleset_add_feature: * @ruleset: a #PangoOTRuleset. * @table_type: the table type to add a feature to. * @feature_index: the index of the feature to add. * @property_bit: the property bit to use for this feature. Used to identify * the glyphs that this feature should be applied to, or * %PANGO_OT_ALL_GLYPHS if it should be applied to all glyphs. * * Adds a feature to the ruleset. **/ void pango_ot_ruleset_add_feature (PangoOTRuleset *ruleset, PangoOTTableType table_type, guint feature_index, gulong property_bit) { PangoOTRule tmp_rule; g_return_if_fail (PANGO_IS_OT_RULESET (ruleset)); g_return_if_fail (ruleset->info != NULL); if (feature_index == PANGO_OT_NO_FEATURE) return; tmp_rule.table_type = table_type; tmp_rule.feature_index = feature_index; tmp_rule.property_bit = property_bit; g_array_append_val (ruleset->rules, tmp_rule); ruleset->n_features[table_type]++; } /** * pango_ot_ruleset_maybe_add_feature: * @ruleset: a #PangoOTRuleset. * @table_type: the table type to add a feature to. * @feature_tag: the tag of the feature to add. * @property_bit: the property bit to use for this feature. Used to identify * the glyphs that this feature should be applied to, or * %PANGO_OT_ALL_GLYPHS if it should be applied to all glyphs. * * This is a convenience function that first tries to find the feature * using pango_ot_info_find_feature() and the ruleset script and language * passed to pango_ot_ruleset_new_for(), * and if the feature is found, adds it to the ruleset. * * If @ruleset was not created using pango_ot_ruleset_new_for(), this function * does nothing. * * Return value: %TRUE if the feature was found and added to ruleset, * %FALSE otherwise. * * Since: 1.18 **/ gboolean pango_ot_ruleset_maybe_add_feature (PangoOTRuleset *ruleset, PangoOTTableType table_type, PangoOTTag feature_tag, gulong property_bit) { guint feature_index; g_return_val_if_fail (PANGO_IS_OT_RULESET (ruleset), FALSE); g_return_val_if_fail (ruleset->info != NULL, FALSE); pango_ot_info_find_feature (ruleset->info, table_type, feature_tag, ruleset->script_index[table_type], ruleset->language_index[table_type], &feature_index); if (feature_index != PANGO_OT_NO_FEATURE) { pango_ot_ruleset_add_feature (ruleset, table_type, feature_index, property_bit); return TRUE; } return FALSE; } /** * pango_ot_ruleset_maybe_add_features: * @ruleset: a #PangoOTRuleset. * @table_type: the table type to add features to. * @features: array of feature name and property bits to add. * @n_features: number of feature records in @features array. * * This is a convenience function that * for each feature in the feature map array @features * converts the feature name to a #PangoOTTag feature tag using PANGO_OT_TAG_MAKE() * and calls pango_ot_ruleset_maybe_add_feature() on it. * * Return value: The number of features in @features that were found * and added to @ruleset. * * Since: 1.18 **/ guint pango_ot_ruleset_maybe_add_features (PangoOTRuleset *ruleset, PangoOTTableType table_type, const PangoOTFeatureMap *features, guint n_features) { guint i, n_found_features = 0; g_return_val_if_fail (PANGO_IS_OT_RULESET (ruleset), 0); g_return_val_if_fail (ruleset->info != NULL, 0); for (i = 0; i < n_features; i++) { PangoOTTag feature_tag = PANGO_OT_TAG_MAKE (features[i].feature_name[0], features[i].feature_name[1], features[i].feature_name[2], features[i].feature_name[3]); n_found_features += pango_ot_ruleset_maybe_add_feature (ruleset, table_type, feature_tag, features[i].property_bit); } return n_found_features; } /** * pango_ot_ruleset_get_feature_count: * @ruleset: a #PangoOTRuleset. * @n_gsub_features: location to store number of GSUB features, or %NULL. * @n_gpos_features: location to store number of GPOS features, or %NULL. * * Gets the number of GSUB and GPOS features in the ruleset. * * Return value: Total number of features in the @ruleset. * * Since: 1.18 **/ guint pango_ot_ruleset_get_feature_count (const PangoOTRuleset *ruleset, guint *n_gsub_features, guint *n_gpos_features) { g_return_val_if_fail (PANGO_IS_OT_RULESET (ruleset), 0); if (n_gsub_features) *n_gsub_features = ruleset->n_features[PANGO_OT_TABLE_GSUB]; if (n_gpos_features) *n_gpos_features = ruleset->n_features[PANGO_OT_TABLE_GPOS]; return ruleset->n_features[PANGO_OT_TABLE_GSUB] + ruleset->n_features[PANGO_OT_TABLE_GPOS]; } /** * pango_ot_ruleset_substitute: * @ruleset: a #PangoOTRuleset. * @buffer: a #PangoOTBuffer. * * Performs the OpenType GSUB substitution on @buffer using the features * in @ruleset * * Since: 1.4 **/ void pango_ot_ruleset_substitute (const PangoOTRuleset *ruleset, PangoOTBuffer *buffer) { g_return_if_fail (PANGO_IS_OT_RULESET (ruleset)); g_return_if_fail (ruleset->info != NULL); _pango_ot_info_substitute (ruleset->info, ruleset, buffer); } /** * pango_ot_ruleset_position: * @ruleset: a #PangoOTRuleset. * @buffer: a #PangoOTBuffer. * * Performs the OpenType GPOS positioning on @buffer using the features * in @ruleset * * Since: 1.4 **/ void pango_ot_ruleset_position (const PangoOTRuleset *ruleset, PangoOTBuffer *buffer) { g_return_if_fail (PANGO_IS_OT_RULESET (ruleset)); g_return_if_fail (ruleset->info != NULL); _pango_ot_info_position (ruleset->info, ruleset, buffer); } /* ruleset descriptions */ /** * pango_ot_ruleset_description_hash: * @desc: a ruleset description * * Computes a hash of a #PangoOTRulesetDescription structure suitable * to be used, for example, as an argument to g_hash_table_new(). * * Return value: the hash value. * * Since: 1.18 **/ guint pango_ot_ruleset_description_hash (const PangoOTRulesetDescription *desc) { guint hash = 0; guint i; hash ^= desc->script; hash ^= GPOINTER_TO_UINT (desc->language); hash ^= desc->n_static_gsub_features << 8; hash ^= GPOINTER_TO_UINT (desc->static_gsub_features); hash ^= desc->n_static_gpos_features << 12; hash ^= GPOINTER_TO_UINT (desc->static_gpos_features); hash ^= desc->n_other_features << 16; for (i = 0; i < desc->n_other_features; i++) { hash ^= * (guint32 *) desc->other_features[i].feature_name; hash ^= desc->other_features[i].property_bit; } return hash; } /** * pango_ot_ruleset_description_equal: * @desc1: a ruleset description * @desc2: a ruleset description * * Compares two ruleset descriptions for equality. * Two ruleset descriptions are considered equal if the rulesets * they describe are provably identical. This means that their * script, language, and all feature sets should be equal. For static feature * sets, the array addresses are compared directly, while for other * features, the list of features is compared one by one. * (Two ruleset descriptions may result in identical rulesets * being created, but still compare %FALSE.) * * Return value: %TRUE if two ruleset descriptions are identical, * %FALSE otherwise. * * Since: 1.18 **/ gboolean pango_ot_ruleset_description_equal (const PangoOTRulesetDescription *desc1, const PangoOTRulesetDescription *desc2) { guint i; #undef CHECK #define CHECK(x) if (desc1->x != desc2->x) return FALSE; #define CHECK_FEATURE_NAME(x) if (*(guint32 *)desc1->x != *(guint32 *)desc2->x) return FALSE CHECK (script); CHECK (language); CHECK (static_gsub_features); CHECK (n_static_gsub_features); CHECK (static_gpos_features); CHECK (n_static_gpos_features); CHECK (n_other_features); for (i = 0; i < desc1->n_other_features; i++) { CHECK_FEATURE_NAME (other_features[i].feature_name); CHECK (other_features[i].property_bit); } #undef CHECK return TRUE; } /** * pango_ot_ruleset_description_copy: * @desc: ruleset description to copy * * Creates a copy of @desc, which should be freed with * pango_ot_ruleset_description_free(). Primarily used internally * by pango_ot_ruleset_get_for_description() to cache rulesets for * ruleset descriptions. * * Return value: the newly allocated #PangoOTRulesetDescription, which * should be freed with pango_ot_ruleset_description_free(). * * Since: 1.18 **/ PangoOTRulesetDescription * pango_ot_ruleset_description_copy (const PangoOTRulesetDescription *desc) { PangoOTRulesetDescription *copy; g_return_val_if_fail (desc != NULL, NULL); copy = g_slice_new (PangoOTRulesetDescription); *copy = *desc; if (desc->n_other_features) { PangoOTFeatureMap *map = g_new (PangoOTFeatureMap, desc->n_other_features); memcpy (map, desc->other_features, desc->n_other_features * sizeof (PangoOTFeatureMap)); copy->other_features = map; } else { copy->other_features = NULL; } return copy; } /** * pango_ot_ruleset_description_free: * @desc: an allocated #PangoOTRulesetDescription * * Frees a ruleset description allocated by * pango_ot_ruleset_description_copy(). * * Since: 1.18 **/ void pango_ot_ruleset_description_free (PangoOTRulesetDescription *desc) { g_return_if_fail (desc != NULL); free ((gpointer) desc->other_features); g_slice_free (PangoOTRulesetDescription, desc); }