Git init
[external/pango1.0.git] / pango / pango-ot-ruleset.c
1 /* Pango
2  * pango-ot-ruleset.c: Shaping using OpenType features
3  *
4  * Copyright (C) 2000 Red Hat Software
5  *
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.
10  *
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.
15  *
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.
20  */
21
22 #include "config.h"
23
24 #include "pango-ot-private.h"
25 #include "pango-impl-utils.h"
26
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);
30
31 static GObjectClass *parent_class;
32
33 GType
34 pango_ot_ruleset_get_type (void)
35 {
36   static GType object_type = 0;
37
38   if (G_UNLIKELY (!object_type))
39     {
40       const GTypeInfo object_info =
41       {
42         sizeof (PangoOTRulesetClass),
43         (GBaseInitFunc) NULL,
44         (GBaseFinalizeFunc) NULL,
45         (GClassInitFunc)pango_ot_ruleset_class_init,
46         NULL,           /* class_finalize */
47         NULL,           /* class_data */
48         sizeof (PangoOTRuleset),
49         0,              /* n_preallocs */
50         (GInstanceInitFunc)pango_ot_ruleset_init,
51         NULL            /* value_table */
52       };
53
54       object_type = g_type_register_static (G_TYPE_OBJECT,
55                                             I_("PangoOTRuleset"),
56                                             &object_info, 0);
57     }
58
59   return object_type;
60 }
61
62 static void
63 pango_ot_ruleset_class_init (GObjectClass *object_class)
64 {
65   parent_class = g_type_class_peek_parent (object_class);
66
67   object_class->finalize = pango_ot_ruleset_finalize;
68 }
69
70 static void
71 pango_ot_ruleset_init (PangoOTRuleset *ruleset)
72 {
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;
78 }
79
80 static void
81 pango_ot_ruleset_finalize (GObject *object)
82 {
83   PangoOTRuleset *ruleset = PANGO_OT_RULESET (object);
84
85   g_array_free (ruleset->rules, TRUE);
86   if (ruleset->info)
87     g_object_remove_weak_pointer (G_OBJECT (ruleset->info), (gpointer *)(void *)&ruleset->info);
88
89   parent_class->finalize (object);
90 }
91
92 /**
93  * pango_ot_ruleset_get_for_description:
94  * @info: a #PangoOTInfo.
95  * @desc: a #PangoOTRulesetDescription.
96  *
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.
101  *
102  * The static feature map members of @desc should be alive as
103  * long as @info is.
104  *
105  * Return value: the #PangoOTRuleset for @desc. This object will have
106  * the same lifetime as @info.
107  *
108  * Since: 1.18
109  **/
110 G_CONST_RETURN PangoOTRuleset *
111 pango_ot_ruleset_get_for_description (PangoOTInfo                     *info,
112                                       const PangoOTRulesetDescription *desc)
113 {
114   PangoOTRuleset *ruleset;
115   static GQuark rulesets_quark = 0;
116   GHashTable *rulesets;
117
118   g_return_val_if_fail (info != NULL, NULL);
119   g_return_val_if_fail (desc != NULL, NULL);
120
121   if (!rulesets_quark)
122     rulesets_quark = g_quark_from_string ("pango-info-rulesets");
123
124   rulesets = g_object_get_qdata (G_OBJECT (info), rulesets_quark);
125
126   if (!rulesets)
127     {
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);
132
133       g_object_set_qdata_full (G_OBJECT (info), rulesets_quark, rulesets, (GDestroyNotify) g_hash_table_destroy);
134     }
135
136   ruleset = g_hash_table_lookup (rulesets, desc);
137
138   if (!ruleset)
139     {
140       ruleset = pango_ot_ruleset_new_from_description (info, desc);
141
142       g_hash_table_insert (rulesets,
143                            pango_ot_ruleset_description_copy (desc),
144                            ruleset);
145     }
146
147   return ruleset;
148 }
149
150 /**
151  * pango_ot_ruleset_new:
152  * @info: a #PangoOTInfo.
153  *
154  * Creates a new #PangoOTRuleset for the given OpenType info.
155  *
156  * Return value: the newly allocated #PangoOTRuleset, which
157  *               should be freed with g_object_unref().
158  **/
159 PangoOTRuleset *
160 pango_ot_ruleset_new (PangoOTInfo *info)
161 {
162   PangoOTRuleset *ruleset;
163
164   g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL);
165
166   ruleset = g_object_new (PANGO_TYPE_OT_RULESET, NULL);
167
168   ruleset->info = info;
169   g_object_add_weak_pointer (G_OBJECT (ruleset->info), (gpointer *)(void*)&ruleset->info);
170
171   return ruleset;
172 }
173
174 /**
175  * pango_ot_ruleset_new_for:
176  * @info: a #PangoOTInfo.
177  * @script: a #PangoScript.
178  * @language: a #PangoLanguage.
179  *
180  * Creates a new #PangoOTRuleset for the given OpenType info, script, and
181  * language.
182  *
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.
189  *
190  * In excess to what pango_ot_ruleset_new() does, this function will:
191  * <itemizedlist>
192  *   <listitem>
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(),
196  *   </listitem>
197  *   <listitem>
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(),
202  *   </listitem>
203  *   <listitem>
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(),
207  *   </listitem>
208  *   <listitem>
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().
212  *   </listitem>
213  * </itemizedlist>
214  *
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.
218  *
219  * Return value: the newly allocated #PangoOTRuleset, which
220  *               should be freed with g_object_unref().
221  *
222  * Since: 1.18
223  **/
224 PangoOTRuleset *
225 pango_ot_ruleset_new_for (PangoOTInfo       *info,
226                           PangoScript        script,
227                           PangoLanguage     *language)
228 {
229   PangoOTRuleset *ruleset;
230   PangoOTTag script_tag, language_tag;
231   PangoOTTableType table_type;
232
233   g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL);
234
235   ruleset = pango_ot_ruleset_new (info);
236
237   script_tag   = pango_ot_tag_from_script (script);
238   language_tag = pango_ot_tag_from_language (language);
239
240   for (table_type = PANGO_OT_TABLE_GSUB; table_type <= PANGO_OT_TABLE_GPOS; table_type++)
241     {
242       guint script_index, language_index, feature_index;
243
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,
248                                    &feature_index);
249
250       ruleset->script_index[table_type] = script_index;
251       ruleset->language_index[table_type] = language_index;
252
253       /* add required feature of the language */
254       pango_ot_ruleset_add_feature (ruleset, table_type,
255                                     feature_index, PANGO_OT_ALL_GLYPHS);
256     }
257
258   return ruleset;
259 }
260
261 /**
262  * pango_ot_ruleset_new_from_description:
263  * @info: a #PangoOTInfo.
264  * @desc: a #PangoOTRulesetDescription.
265  *
266  * Creates a new #PangoOTRuleset for the given OpenType infor and
267  * matching the given ruleset description.
268  *
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.
272  *
273  * The static feature map members of @desc should be alive as
274  * long as @info is.
275  *
276  * Return value: the newly allocated #PangoOTRuleset, which
277  *               should be freed with g_object_unref().
278  *
279  * Since: 1.18
280  **/
281 PangoOTRuleset *
282 pango_ot_ruleset_new_from_description (PangoOTInfo                     *info,
283                                        const PangoOTRulesetDescription *desc)
284 {
285   PangoOTRuleset *ruleset;
286
287   g_return_val_if_fail (info != NULL, NULL);
288   g_return_val_if_fail (desc != NULL, NULL);
289
290   ruleset = pango_ot_ruleset_new_for (info,
291                                       desc->script,
292                                       desc->language);
293
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);
302
303   if (desc->n_other_features)
304     {
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);
311     }
312
313   return ruleset;
314 }
315
316 /**
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.
324  *
325  * Adds a feature to the ruleset.
326  **/
327 void
328 pango_ot_ruleset_add_feature (PangoOTRuleset   *ruleset,
329                               PangoOTTableType  table_type,
330                               guint             feature_index,
331                               gulong            property_bit)
332 {
333   PangoOTRule tmp_rule;
334
335   g_return_if_fail (PANGO_IS_OT_RULESET (ruleset));
336   g_return_if_fail (ruleset->info != NULL);
337
338   if (feature_index == PANGO_OT_NO_FEATURE)
339     return;
340
341   tmp_rule.table_type = table_type;
342   tmp_rule.feature_index = feature_index;
343   tmp_rule.property_bit = property_bit;
344
345   g_array_append_val (ruleset->rules, tmp_rule);
346
347   ruleset->n_features[table_type]++;
348 }
349
350 /**
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.
358  *
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.
363  *
364  * If @ruleset was not created using pango_ot_ruleset_new_for(), this function
365  * does nothing.
366  *
367  * Return value: %TRUE if the feature was found and added to ruleset,
368  *               %FALSE otherwise.
369  *
370  * Since: 1.18
371  **/
372 gboolean
373 pango_ot_ruleset_maybe_add_feature (PangoOTRuleset          *ruleset,
374                                     PangoOTTableType         table_type,
375                                     PangoOTTag               feature_tag,
376                                     gulong                   property_bit)
377 {
378   guint feature_index;
379
380   g_return_val_if_fail (PANGO_IS_OT_RULESET (ruleset), FALSE);
381   g_return_val_if_fail (ruleset->info != NULL, FALSE);
382
383   pango_ot_info_find_feature (ruleset->info, table_type,
384                               feature_tag,
385                               ruleset->script_index[table_type],
386                               ruleset->language_index[table_type],
387                               &feature_index);
388
389   if (feature_index != PANGO_OT_NO_FEATURE)
390     {
391       pango_ot_ruleset_add_feature (ruleset, table_type,
392                                     feature_index, property_bit);
393       return TRUE;
394     }
395
396   return FALSE;
397 }
398
399 /**
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.
405  *
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.
410  *
411  * Return value: The number of features in @features that were found
412  *               and added to @ruleset.
413  *
414  * Since: 1.18
415  **/
416 guint
417 pango_ot_ruleset_maybe_add_features (PangoOTRuleset          *ruleset,
418                                      PangoOTTableType         table_type,
419                                      const PangoOTFeatureMap *features,
420                                      guint                    n_features)
421 {
422   guint i, n_found_features = 0;
423
424   g_return_val_if_fail (PANGO_IS_OT_RULESET (ruleset), 0);
425   g_return_val_if_fail (ruleset->info != NULL, 0);
426
427   for (i = 0; i < n_features; i++)
428     {
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]);
433
434       n_found_features += pango_ot_ruleset_maybe_add_feature (ruleset,
435                                                               table_type,
436                                                               feature_tag,
437                                                               features[i].property_bit);
438     }
439
440   return n_found_features;
441 }
442
443 /**
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.
448  *
449  * Gets the number of GSUB and GPOS features in the ruleset.
450  *
451  * Return value: Total number of features in the @ruleset.
452  *
453  * Since: 1.18
454  **/
455 guint
456 pango_ot_ruleset_get_feature_count (const PangoOTRuleset   *ruleset,
457                                     guint                  *n_gsub_features,
458                                     guint                  *n_gpos_features)
459 {
460   g_return_val_if_fail (PANGO_IS_OT_RULESET (ruleset), 0);
461   
462   if (n_gsub_features)
463     *n_gsub_features = ruleset->n_features[PANGO_OT_TABLE_GSUB];
464
465   if (n_gpos_features)
466     *n_gpos_features = ruleset->n_features[PANGO_OT_TABLE_GPOS];
467
468   return ruleset->n_features[PANGO_OT_TABLE_GSUB] + ruleset->n_features[PANGO_OT_TABLE_GPOS];
469 }
470
471 /**
472  * pango_ot_ruleset_substitute:
473  * @ruleset: a #PangoOTRuleset.
474  * @buffer: a #PangoOTBuffer.
475  *
476  * Performs the OpenType GSUB substitution on @buffer using the features
477  * in @ruleset
478  *
479  * Since: 1.4
480  **/
481 void
482 pango_ot_ruleset_substitute  (const PangoOTRuleset *ruleset,
483                               PangoOTBuffer        *buffer)
484 {
485   g_return_if_fail (PANGO_IS_OT_RULESET (ruleset));
486   g_return_if_fail (ruleset->info != NULL);
487
488   _pango_ot_info_substitute (ruleset->info,
489                              ruleset,
490                              buffer);
491 }
492
493 /**
494  * pango_ot_ruleset_position:
495  * @ruleset: a #PangoOTRuleset.
496  * @buffer: a #PangoOTBuffer.
497  *
498  * Performs the OpenType GPOS positioning on @buffer using the features
499  * in @ruleset
500  *
501  * Since: 1.4
502  **/
503 void
504 pango_ot_ruleset_position (const PangoOTRuleset *ruleset,
505                            PangoOTBuffer        *buffer)
506 {
507   g_return_if_fail (PANGO_IS_OT_RULESET (ruleset));
508   g_return_if_fail (ruleset->info != NULL);
509
510   _pango_ot_info_position   (ruleset->info,
511                              ruleset,
512                              buffer);
513 }
514
515
516 /* ruleset descriptions */
517
518 /**
519  * pango_ot_ruleset_description_hash:
520  * @desc: a ruleset description
521  *
522  * Computes a hash of a #PangoOTRulesetDescription structure suitable
523  * to be used, for example, as an argument to g_hash_table_new().
524  *
525  * Return value: the hash value.
526  *
527  * Since: 1.18
528  **/
529 guint
530 pango_ot_ruleset_description_hash  (const PangoOTRulesetDescription *desc)
531 {
532   guint hash = 0;
533   guint i;
534
535   hash ^= desc->script;
536   hash ^= GPOINTER_TO_UINT (desc->language);
537
538   hash ^= desc->n_static_gsub_features << 8;
539   hash ^= GPOINTER_TO_UINT (desc->static_gsub_features);
540
541   hash ^= desc->n_static_gpos_features << 12;
542   hash ^= GPOINTER_TO_UINT (desc->static_gpos_features);
543
544   hash ^= desc->n_other_features << 16;
545   for (i = 0; i < desc->n_other_features; i++)
546     {
547       hash ^= * (guint32 *) desc->other_features[i].feature_name;
548       hash ^= desc->other_features[i].property_bit;
549     }
550
551   return hash;
552 }
553
554 /**
555  * pango_ot_ruleset_description_equal:
556  * @desc1: a ruleset description
557  * @desc2: a ruleset description
558  *
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.)
567  *
568  * Return value: %TRUE if two ruleset descriptions are identical,
569  *               %FALSE otherwise.
570  *
571  * Since: 1.18
572  **/
573 gboolean
574 pango_ot_ruleset_description_equal (const PangoOTRulesetDescription *desc1,
575                                     const PangoOTRulesetDescription *desc2)
576 {
577   guint i;
578
579 #undef CHECK
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
582
583   CHECK (script);
584   CHECK (language);
585
586   CHECK (static_gsub_features);
587   CHECK (n_static_gsub_features);
588   CHECK (static_gpos_features);
589   CHECK (n_static_gpos_features);
590
591   CHECK (n_other_features);
592
593   for (i = 0; i < desc1->n_other_features; i++)
594     {
595       CHECK_FEATURE_NAME (other_features[i].feature_name);
596       CHECK (other_features[i].property_bit);
597     }
598
599 #undef CHECK
600
601   return TRUE;
602 }
603
604 /**
605  * pango_ot_ruleset_description_copy:
606  * @desc: ruleset description to copy
607  *
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.
612  *
613  * Return value: the newly allocated #PangoOTRulesetDescription, which
614  *               should be freed with pango_ot_ruleset_description_free().
615  *
616  * Since: 1.18
617  **/
618 PangoOTRulesetDescription *
619 pango_ot_ruleset_description_copy  (const PangoOTRulesetDescription *desc)
620 {
621   PangoOTRulesetDescription *copy;
622
623   g_return_val_if_fail (desc != NULL, NULL);
624
625   copy = g_slice_new (PangoOTRulesetDescription);
626
627   *copy = *desc;
628
629   if (desc->n_other_features)
630     {
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;
634     }
635   else
636     {
637       copy->other_features = NULL;
638     }
639
640   return copy;
641 }
642
643 /**
644  * pango_ot_ruleset_description_free:
645  * @desc: an allocated #PangoOTRulesetDescription
646  *
647  * Frees a ruleset description allocated by 
648  * pango_ot_ruleset_description_copy().
649  *
650  * Since: 1.18
651  **/
652 void
653 pango_ot_ruleset_description_free  (PangoOTRulesetDescription *desc)
654 {
655   g_return_if_fail (desc != NULL);
656
657   free ((gpointer) desc->other_features);
658
659   g_slice_free (PangoOTRulesetDescription, desc);
660 }