summaryrefslogtreecommitdiff
path: root/trunk/pango/pango-ot-ruleset.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/pango/pango-ot-ruleset.c')
-rw-r--r--trunk/pango/pango-ot-ruleset.c716
1 files changed, 716 insertions, 0 deletions
diff --git a/trunk/pango/pango-ot-ruleset.c b/trunk/pango/pango-ot-ruleset.c
new file mode 100644
index 00000000..141ce262
--- /dev/null
+++ b/trunk/pango/pango-ot-ruleset.c
@@ -0,0 +1,716 @@
+/* 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"
+
+typedef struct _PangoOTRule PangoOTRule;
+
+struct _PangoOTRule
+{
+ gulong property_bit;
+ FT_UShort feature_index;
+ guint table_type : 1;
+};
+
+static void pango_ot_ruleset_class_init (GObjectClass *object_class);
+static void pango_ot_ruleset_init (PangoOTRuleset *ruleset);
+static void pango_ot_ruleset_finalize (GObject *object);
+
+static GObjectClass *parent_class;
+
+GType
+pango_ot_ruleset_get_type (void)
+{
+ static GType object_type = 0;
+
+ if (G_UNLIKELY (!object_type))
+ {
+ const GTypeInfo object_info =
+ {
+ sizeof (PangoOTRulesetClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc)pango_ot_ruleset_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (PangoOTRuleset),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc)pango_ot_ruleset_init,
+ NULL /* value_table */
+ };
+
+ object_type = g_type_register_static (G_TYPE_OBJECT,
+ I_("PangoOTRuleset"),
+ &object_info, 0);
+ }
+
+ return object_type;
+}
+
+static void
+pango_ot_ruleset_class_init (GObjectClass *object_class)
+{
+ parent_class = g_type_class_peek_parent (object_class);
+
+ object_class->finalize = pango_ot_ruleset_finalize;
+}
+
+static void
+pango_ot_ruleset_init (PangoOTRuleset *ruleset)
+{
+ ruleset->rules = g_array_new (FALSE, FALSE, sizeof (PangoOTRule));
+}
+
+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 (ruleset->info, &ruleset->info);
+
+ 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
+ **/
+G_CONST_RETURN 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 (ruleset->info, &ruleset->info);
+
+ 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;
+
+ 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:
+ * <itemizedlist>
+ * <listitem>
+ * Find the #PangoOTTag script and language tags associated with
+ * @script and @language using pango_ot_tag_from_script() and
+ * pango_ot_tag_from_language(),
+ * </listitem>
+ * <listitem>
+ * 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(),
+ * </listitem>
+ * <listitem>
+ * For found language-systems, if they have required feature
+ * index, add that feature to the ruleset using
+ * pango_ot_ruleset_add_feature(),
+ * </listitem>
+ * <listitem>
+ * 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().
+ * </listitem>
+ * </itemizedlist>
+ *
+ * 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 FT_MAKE_TAG()
+ * 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 = FT_MAKE_TAG (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)
+{
+ unsigned int i;
+
+ HB_GSUB gsub = NULL;
+
+ g_return_if_fail (PANGO_IS_OT_RULESET (ruleset));
+ g_return_if_fail (ruleset->info != NULL);
+
+ for (i = 0; i < ruleset->rules->len; i++)
+ {
+ PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i);
+
+ if (rule->table_type != PANGO_OT_TABLE_GSUB)
+ continue;
+
+ if (!gsub)
+ {
+ gsub = pango_ot_info_get_gsub (ruleset->info);
+
+ if (gsub)
+ HB_GSUB_Clear_Features (gsub);
+ else
+ return;
+ }
+
+ HB_GSUB_Add_Feature (gsub, rule->feature_index, rule->property_bit);
+ }
+
+ HB_GSUB_Apply_String (gsub, buffer->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)
+{
+ unsigned int i;
+
+ HB_GPOS gpos = NULL;
+
+ g_return_if_fail (PANGO_IS_OT_RULESET (ruleset));
+ g_return_if_fail (ruleset->info != NULL);
+
+ for (i = 0; i < ruleset->rules->len; i++)
+ {
+ PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i);
+
+ if (rule->table_type != PANGO_OT_TABLE_GPOS)
+ continue;
+
+ if (!gpos)
+ {
+ gpos = pango_ot_info_get_gpos (ruleset->info);
+
+ if (gpos)
+ HB_GPOS_Clear_Features (gpos);
+ else
+ return;
+ }
+
+ HB_GPOS_Add_Feature (gpos, rule->feature_index, rule->property_bit);
+ }
+
+ if (HB_GPOS_Apply_String (ruleset->info->face, gpos, 0, buffer->buffer,
+ FALSE /* enable device-dependant values */,
+ buffer->rtl) == FT_Err_Ok)
+ buffer->applied_gpos = TRUE;
+}
+
+/* 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);
+}