2 * pango-ot-ruleset.c: Shaping using OpenType features
4 * Copyright (C) 2000 Red Hat Software
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
24 #include "pango-ot-private.h"
25 #include "pango-impl-utils.h"
27 static void pango_ot_ruleset_class_init (GObjectClass *object_class);
28 static void pango_ot_ruleset_init (PangoOTRuleset *ruleset);
29 static void pango_ot_ruleset_finalize (GObject *object);
31 static GObjectClass *parent_class;
34 pango_ot_ruleset_get_type (void)
36 static GType object_type = 0;
38 if (G_UNLIKELY (!object_type))
40 const GTypeInfo object_info =
42 sizeof (PangoOTRulesetClass),
44 (GBaseFinalizeFunc) NULL,
45 (GClassInitFunc)pango_ot_ruleset_class_init,
46 NULL, /* class_finalize */
47 NULL, /* class_data */
48 sizeof (PangoOTRuleset),
50 (GInstanceInitFunc)pango_ot_ruleset_init,
51 NULL /* value_table */
54 object_type = g_type_register_static (G_TYPE_OBJECT,
63 pango_ot_ruleset_class_init (GObjectClass *object_class)
65 parent_class = g_type_class_peek_parent (object_class);
67 object_class->finalize = pango_ot_ruleset_finalize;
71 pango_ot_ruleset_init (PangoOTRuleset *ruleset)
73 ruleset->rules = g_array_new (FALSE, FALSE, sizeof (PangoOTRule));
74 ruleset->script_index[0] = PANGO_OT_NO_SCRIPT;
75 ruleset->script_index[1] = PANGO_OT_NO_SCRIPT;
76 ruleset->language_index[0] = PANGO_OT_DEFAULT_LANGUAGE;
77 ruleset->language_index[1] = PANGO_OT_DEFAULT_LANGUAGE;
81 pango_ot_ruleset_finalize (GObject *object)
83 PangoOTRuleset *ruleset = PANGO_OT_RULESET (object);
85 g_array_free (ruleset->rules, TRUE);
87 g_object_remove_weak_pointer (G_OBJECT (ruleset->info), (gpointer *)(void *)&ruleset->info);
89 parent_class->finalize (object);
93 * pango_ot_ruleset_get_for_description:
94 * @info: a #PangoOTInfo.
95 * @desc: a #PangoOTRulesetDescription.
97 * Returns a ruleset for the given OpenType info and ruleset
98 * description. Rulesets are created on demand using
99 * pango_ot_ruleset_new_from_description().
100 * The returned ruleset should not be modified or destroyed.
102 * The static feature map members of @desc should be alive as
105 * Return value: the #PangoOTRuleset for @desc. This object will have
106 * the same lifetime as @info.
110 G_CONST_RETURN PangoOTRuleset *
111 pango_ot_ruleset_get_for_description (PangoOTInfo *info,
112 const PangoOTRulesetDescription *desc)
114 PangoOTRuleset *ruleset;
115 static GQuark rulesets_quark = 0;
116 GHashTable *rulesets;
118 g_return_val_if_fail (info != NULL, NULL);
119 g_return_val_if_fail (desc != NULL, NULL);
122 rulesets_quark = g_quark_from_string ("pango-info-rulesets");
124 rulesets = g_object_get_qdata (G_OBJECT (info), rulesets_quark);
128 rulesets = g_hash_table_new_full ((GHashFunc) pango_ot_ruleset_description_hash,
129 (GEqualFunc) pango_ot_ruleset_description_equal,
130 (GDestroyNotify) pango_ot_ruleset_description_free,
131 (GDestroyNotify) g_object_unref);
133 g_object_set_qdata_full (G_OBJECT (info), rulesets_quark, rulesets, (GDestroyNotify) g_hash_table_destroy);
136 ruleset = g_hash_table_lookup (rulesets, desc);
140 ruleset = pango_ot_ruleset_new_from_description (info, desc);
142 g_hash_table_insert (rulesets,
143 pango_ot_ruleset_description_copy (desc),
151 * pango_ot_ruleset_new:
152 * @info: a #PangoOTInfo.
154 * Creates a new #PangoOTRuleset for the given OpenType info.
156 * Return value: the newly allocated #PangoOTRuleset, which
157 * should be freed with g_object_unref().
160 pango_ot_ruleset_new (PangoOTInfo *info)
162 PangoOTRuleset *ruleset;
164 g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL);
166 ruleset = g_object_new (PANGO_TYPE_OT_RULESET, NULL);
168 ruleset->info = info;
169 g_object_add_weak_pointer (G_OBJECT (ruleset->info), (gpointer *)(void*)&ruleset->info);
175 * pango_ot_ruleset_new_for:
176 * @info: a #PangoOTInfo.
177 * @script: a #PangoScript.
178 * @language: a #PangoLanguage.
180 * Creates a new #PangoOTRuleset for the given OpenType info, script, and
183 * This function is part of a convenience scheme that highly simplifies
184 * using a #PangoOTRuleset to represent features for a specific pair of script
185 * and language. So one can use this function passing in the script and
186 * language of interest, and later try to add features to the ruleset by just
187 * specifying the feature name or tag, without having to deal with finding
188 * script, language, or feature indices manually.
190 * In excess to what pango_ot_ruleset_new() does, this function will:
193 * Find the #PangoOTTag script and language tags associated with
194 * @script and @language using pango_ot_tag_from_script() and
195 * pango_ot_tag_from_language(),
198 * For each of table types %PANGO_OT_TABLE_GSUB and %PANGO_OT_TABLE_GPOS,
199 * find the script index of the script tag found and the language
200 * system index of the language tag found in that script system, using
201 * pango_ot_info_find_script() and pango_ot_info_find_language(),
204 * For found language-systems, if they have required feature
205 * index, add that feature to the ruleset using
206 * pango_ot_ruleset_add_feature(),
209 * Remember found script and language indices for both table types,
210 * and use them in future pango_ot_ruleset_maybe_add_feature() and
211 * pango_ot_ruleset_maybe_add_features().
215 * Because of the way return values of pango_ot_info_find_script() and
216 * pango_ot_info_find_language() are ignored, this function automatically
217 * finds and uses the 'DFLT' script and the default language-system.
219 * Return value: the newly allocated #PangoOTRuleset, which
220 * should be freed with g_object_unref().
225 pango_ot_ruleset_new_for (PangoOTInfo *info,
227 PangoLanguage *language)
229 PangoOTRuleset *ruleset;
230 PangoOTTag script_tag, language_tag;
231 PangoOTTableType table_type;
233 g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL);
235 ruleset = pango_ot_ruleset_new (info);
237 script_tag = pango_ot_tag_from_script (script);
238 language_tag = pango_ot_tag_from_language (language);
240 for (table_type = PANGO_OT_TABLE_GSUB; table_type <= PANGO_OT_TABLE_GPOS; table_type++)
242 guint script_index, language_index, feature_index;
244 pango_ot_info_find_script (ruleset->info, table_type,
245 script_tag, &script_index);
246 pango_ot_info_find_language (ruleset->info, table_type, script_index,
247 language_tag, &language_index,
250 ruleset->script_index[table_type] = script_index;
251 ruleset->language_index[table_type] = language_index;
253 /* add required feature of the language */
254 pango_ot_ruleset_add_feature (ruleset, table_type,
255 feature_index, PANGO_OT_ALL_GLYPHS);
262 * pango_ot_ruleset_new_from_description:
263 * @info: a #PangoOTInfo.
264 * @desc: a #PangoOTRulesetDescription.
266 * Creates a new #PangoOTRuleset for the given OpenType infor and
267 * matching the given ruleset description.
269 * This is a convenience function that calls pango_ot_ruleset_new_for() and
270 * adds the static GSUB/GPOS features to the resulting ruleset, followed by
271 * adding other features to both GSUB and GPOS.
273 * The static feature map members of @desc should be alive as
276 * Return value: the newly allocated #PangoOTRuleset, which
277 * should be freed with g_object_unref().
282 pango_ot_ruleset_new_from_description (PangoOTInfo *info,
283 const PangoOTRulesetDescription *desc)
285 PangoOTRuleset *ruleset;
287 g_return_val_if_fail (info != NULL, NULL);
288 g_return_val_if_fail (desc != NULL, NULL);
290 ruleset = pango_ot_ruleset_new_for (info,
294 if (desc->n_static_gsub_features)
295 pango_ot_ruleset_maybe_add_features (ruleset, PANGO_OT_TABLE_GSUB,
296 desc->static_gsub_features,
297 desc->n_static_gsub_features);
298 if (desc->n_static_gpos_features)
299 pango_ot_ruleset_maybe_add_features (ruleset, PANGO_OT_TABLE_GPOS,
300 desc->static_gpos_features,
301 desc->n_static_gpos_features);
303 if (desc->n_other_features)
305 pango_ot_ruleset_maybe_add_features (ruleset, PANGO_OT_TABLE_GSUB,
306 desc->other_features,
307 desc->n_other_features);
308 pango_ot_ruleset_maybe_add_features (ruleset, PANGO_OT_TABLE_GPOS,
309 desc->other_features,
310 desc->n_other_features);
317 * pango_ot_ruleset_add_feature:
318 * @ruleset: a #PangoOTRuleset.
319 * @table_type: the table type to add a feature to.
320 * @feature_index: the index of the feature to add.
321 * @property_bit: the property bit to use for this feature. Used to identify
322 * the glyphs that this feature should be applied to, or
323 * %PANGO_OT_ALL_GLYPHS if it should be applied to all glyphs.
325 * Adds a feature to the ruleset.
328 pango_ot_ruleset_add_feature (PangoOTRuleset *ruleset,
329 PangoOTTableType table_type,
333 PangoOTRule tmp_rule;
335 g_return_if_fail (PANGO_IS_OT_RULESET (ruleset));
336 g_return_if_fail (ruleset->info != NULL);
338 if (feature_index == PANGO_OT_NO_FEATURE)
341 tmp_rule.table_type = table_type;
342 tmp_rule.feature_index = feature_index;
343 tmp_rule.property_bit = property_bit;
345 g_array_append_val (ruleset->rules, tmp_rule);
347 ruleset->n_features[table_type]++;
351 * pango_ot_ruleset_maybe_add_feature:
352 * @ruleset: a #PangoOTRuleset.
353 * @table_type: the table type to add a feature to.
354 * @feature_tag: the tag of the feature to add.
355 * @property_bit: the property bit to use for this feature. Used to identify
356 * the glyphs that this feature should be applied to, or
357 * %PANGO_OT_ALL_GLYPHS if it should be applied to all glyphs.
359 * This is a convenience function that first tries to find the feature
360 * using pango_ot_info_find_feature() and the ruleset script and language
361 * passed to pango_ot_ruleset_new_for(),
362 * and if the feature is found, adds it to the ruleset.
364 * If @ruleset was not created using pango_ot_ruleset_new_for(), this function
367 * Return value: %TRUE if the feature was found and added to ruleset,
373 pango_ot_ruleset_maybe_add_feature (PangoOTRuleset *ruleset,
374 PangoOTTableType table_type,
375 PangoOTTag feature_tag,
380 g_return_val_if_fail (PANGO_IS_OT_RULESET (ruleset), FALSE);
381 g_return_val_if_fail (ruleset->info != NULL, FALSE);
383 pango_ot_info_find_feature (ruleset->info, table_type,
385 ruleset->script_index[table_type],
386 ruleset->language_index[table_type],
389 if (feature_index != PANGO_OT_NO_FEATURE)
391 pango_ot_ruleset_add_feature (ruleset, table_type,
392 feature_index, property_bit);
400 * pango_ot_ruleset_maybe_add_features:
401 * @ruleset: a #PangoOTRuleset.
402 * @table_type: the table type to add features to.
403 * @features: array of feature name and property bits to add.
404 * @n_features: number of feature records in @features array.
406 * This is a convenience function that
407 * for each feature in the feature map array @features
408 * converts the feature name to a #PangoOTTag feature tag using PANGO_OT_TAG_MAKE()
409 * and calls pango_ot_ruleset_maybe_add_feature() on it.
411 * Return value: The number of features in @features that were found
412 * and added to @ruleset.
417 pango_ot_ruleset_maybe_add_features (PangoOTRuleset *ruleset,
418 PangoOTTableType table_type,
419 const PangoOTFeatureMap *features,
422 guint i, n_found_features = 0;
424 g_return_val_if_fail (PANGO_IS_OT_RULESET (ruleset), 0);
425 g_return_val_if_fail (ruleset->info != NULL, 0);
427 for (i = 0; i < n_features; i++)
429 PangoOTTag feature_tag = PANGO_OT_TAG_MAKE (features[i].feature_name[0],
430 features[i].feature_name[1],
431 features[i].feature_name[2],
432 features[i].feature_name[3]);
434 n_found_features += pango_ot_ruleset_maybe_add_feature (ruleset,
437 features[i].property_bit);
440 return n_found_features;
444 * pango_ot_ruleset_get_feature_count:
445 * @ruleset: a #PangoOTRuleset.
446 * @n_gsub_features: location to store number of GSUB features, or %NULL.
447 * @n_gpos_features: location to store number of GPOS features, or %NULL.
449 * Gets the number of GSUB and GPOS features in the ruleset.
451 * Return value: Total number of features in the @ruleset.
456 pango_ot_ruleset_get_feature_count (const PangoOTRuleset *ruleset,
457 guint *n_gsub_features,
458 guint *n_gpos_features)
460 g_return_val_if_fail (PANGO_IS_OT_RULESET (ruleset), 0);
463 *n_gsub_features = ruleset->n_features[PANGO_OT_TABLE_GSUB];
466 *n_gpos_features = ruleset->n_features[PANGO_OT_TABLE_GPOS];
468 return ruleset->n_features[PANGO_OT_TABLE_GSUB] + ruleset->n_features[PANGO_OT_TABLE_GPOS];
472 * pango_ot_ruleset_substitute:
473 * @ruleset: a #PangoOTRuleset.
474 * @buffer: a #PangoOTBuffer.
476 * Performs the OpenType GSUB substitution on @buffer using the features
482 pango_ot_ruleset_substitute (const PangoOTRuleset *ruleset,
483 PangoOTBuffer *buffer)
485 g_return_if_fail (PANGO_IS_OT_RULESET (ruleset));
486 g_return_if_fail (ruleset->info != NULL);
488 _pango_ot_info_substitute (ruleset->info,
494 * pango_ot_ruleset_position:
495 * @ruleset: a #PangoOTRuleset.
496 * @buffer: a #PangoOTBuffer.
498 * Performs the OpenType GPOS positioning on @buffer using the features
504 pango_ot_ruleset_position (const PangoOTRuleset *ruleset,
505 PangoOTBuffer *buffer)
507 g_return_if_fail (PANGO_IS_OT_RULESET (ruleset));
508 g_return_if_fail (ruleset->info != NULL);
510 _pango_ot_info_position (ruleset->info,
516 /* ruleset descriptions */
519 * pango_ot_ruleset_description_hash:
520 * @desc: a ruleset description
522 * Computes a hash of a #PangoOTRulesetDescription structure suitable
523 * to be used, for example, as an argument to g_hash_table_new().
525 * Return value: the hash value.
530 pango_ot_ruleset_description_hash (const PangoOTRulesetDescription *desc)
535 hash ^= desc->script;
536 hash ^= GPOINTER_TO_UINT (desc->language);
538 hash ^= desc->n_static_gsub_features << 8;
539 hash ^= GPOINTER_TO_UINT (desc->static_gsub_features);
541 hash ^= desc->n_static_gpos_features << 12;
542 hash ^= GPOINTER_TO_UINT (desc->static_gpos_features);
544 hash ^= desc->n_other_features << 16;
545 for (i = 0; i < desc->n_other_features; i++)
547 hash ^= * (guint32 *) desc->other_features[i].feature_name;
548 hash ^= desc->other_features[i].property_bit;
555 * pango_ot_ruleset_description_equal:
556 * @desc1: a ruleset description
557 * @desc2: a ruleset description
559 * Compares two ruleset descriptions for equality.
560 * Two ruleset descriptions are considered equal if the rulesets
561 * they describe are provably identical. This means that their
562 * script, language, and all feature sets should be equal. For static feature
563 * sets, the array addresses are compared directly, while for other
564 * features, the list of features is compared one by one.
565 * (Two ruleset descriptions may result in identical rulesets
566 * being created, but still compare %FALSE.)
568 * Return value: %TRUE if two ruleset descriptions are identical,
574 pango_ot_ruleset_description_equal (const PangoOTRulesetDescription *desc1,
575 const PangoOTRulesetDescription *desc2)
580 #define CHECK(x) if (desc1->x != desc2->x) return FALSE;
581 #define CHECK_FEATURE_NAME(x) if (*(guint32 *)desc1->x != *(guint32 *)desc2->x) return FALSE
586 CHECK (static_gsub_features);
587 CHECK (n_static_gsub_features);
588 CHECK (static_gpos_features);
589 CHECK (n_static_gpos_features);
591 CHECK (n_other_features);
593 for (i = 0; i < desc1->n_other_features; i++)
595 CHECK_FEATURE_NAME (other_features[i].feature_name);
596 CHECK (other_features[i].property_bit);
605 * pango_ot_ruleset_description_copy:
606 * @desc: ruleset description to copy
608 * Creates a copy of @desc, which should be freed with
609 * pango_ot_ruleset_description_free(). Primarily used internally
610 * by pango_ot_ruleset_get_for_description() to cache rulesets for
611 * ruleset descriptions.
613 * Return value: the newly allocated #PangoOTRulesetDescription, which
614 * should be freed with pango_ot_ruleset_description_free().
618 PangoOTRulesetDescription *
619 pango_ot_ruleset_description_copy (const PangoOTRulesetDescription *desc)
621 PangoOTRulesetDescription *copy;
623 g_return_val_if_fail (desc != NULL, NULL);
625 copy = g_slice_new (PangoOTRulesetDescription);
629 if (desc->n_other_features)
631 PangoOTFeatureMap *map = g_new (PangoOTFeatureMap, desc->n_other_features);
632 memcpy (map, desc->other_features, desc->n_other_features * sizeof (PangoOTFeatureMap));
633 copy->other_features = map;
637 copy->other_features = NULL;
644 * pango_ot_ruleset_description_free:
645 * @desc: an allocated #PangoOTRulesetDescription
647 * Frees a ruleset description allocated by
648 * pango_ot_ruleset_description_copy().
653 pango_ot_ruleset_description_free (PangoOTRulesetDescription *desc)
655 g_return_if_fail (desc != NULL);
657 free ((gpointer) desc->other_features);
659 g_slice_free (PangoOTRulesetDescription, desc);