Git init
[external/pango1.0.git] / pango / pango-ot-info.c
1 /* Pango
2  * pango-ot-info.c: Store tables for OpenType
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 #include FT_TRUETYPE_TABLES_H
27
28 static void pango_ot_info_class_init (GObjectClass *object_class);
29 static void pango_ot_info_finalize   (GObject *object);
30
31 static void synthesize_class_def (PangoOTInfo *info);
32
33 static GObjectClass *parent_class;
34
35 GType
36 pango_ot_info_get_type (void)
37 {
38   static GType object_type = 0;
39
40   if (G_UNLIKELY (!object_type))
41     {
42       const GTypeInfo object_info =
43       {
44         sizeof (PangoOTInfoClass),
45         (GBaseInitFunc) NULL,
46         (GBaseFinalizeFunc) NULL,
47         (GClassInitFunc)pango_ot_info_class_init,
48         NULL,           /* class_finalize */
49         NULL,           /* class_data */
50         sizeof (PangoOTInfo),
51         0,              /* n_preallocs */
52         NULL,           /* init */
53         NULL,           /* value_table */
54       };
55
56       object_type = g_type_register_static (G_TYPE_OBJECT,
57                                             I_("PangoOTInfo"),
58                                             &object_info, 0);
59     }
60
61   return object_type;
62 }
63
64 static void
65 pango_ot_info_class_init (GObjectClass *object_class)
66 {
67   parent_class = g_type_class_peek_parent (object_class);
68
69   object_class->finalize = pango_ot_info_finalize;
70 }
71
72 static void
73 pango_ot_info_finalize (GObject *object)
74 {
75   PangoOTInfo *info = PANGO_OT_INFO (object);
76
77   if (info->hb_face)
78     hb_face_destroy (info->hb_face);
79
80   parent_class->finalize (object);
81 }
82
83 static void
84 pango_ot_info_finalizer (void *object)
85 {
86   FT_Face face = object;
87   PangoOTInfo *info = face->generic.data;
88
89   info->face = NULL;
90   g_object_unref (info);
91 }
92
93 static hb_blob_t *
94 _get_table  (hb_tag_t tag, void *user_data)
95 {
96   PangoOTInfo *info = (PangoOTInfo *) user_data;
97   FT_Byte *buffer;
98   FT_ULong  length = 0;
99   FT_Error error;
100
101   error = FT_Load_Sfnt_Table (info->face, tag, 0, NULL, &length);
102   if (error)
103     return hb_blob_create_empty ();
104
105   buffer = g_malloc (length);
106   if (buffer == NULL)
107     return hb_blob_create_empty ();
108
109   error = FT_Load_Sfnt_Table (info->face, tag, 0, buffer, &length);
110   if (error)
111     return hb_blob_create_empty ();
112
113   return hb_blob_create ((const char *) buffer, length,
114                          HB_MEMORY_MODE_WRITABLE,
115                          g_free, buffer);
116 }
117
118
119 /**
120  * pango_ot_info_get:
121  * @face: a <type>FT_Face</type>.
122  *
123  * Returns the #PangoOTInfo structure for the given FreeType font face.
124  *
125  * Return value: the #PangoOTInfo for @face. This object will have
126  * the same lifetime as @face.
127  *
128  * Since: 1.2
129  **/
130 PangoOTInfo *
131 pango_ot_info_get (FT_Face face)
132 {
133   PangoOTInfo *info;
134
135   if (G_LIKELY (face->generic.data && face->generic.finalizer == pango_ot_info_finalizer))
136     return face->generic.data;
137   else
138     {
139       if (face->generic.finalizer)
140         face->generic.finalizer (face->generic.data);
141
142       info = face->generic.data = g_object_new (PANGO_TYPE_OT_INFO, NULL);
143       face->generic.finalizer = pango_ot_info_finalizer;
144
145       info->face = face;
146
147       if (face->stream->base != NULL) {
148         hb_blob_t *blob;
149
150         blob = hb_blob_create ((const char *) face->stream->base,
151                                (unsigned int) face->stream->size,
152                                HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE,
153                                NULL, NULL);
154         info->hb_face = hb_face_create_for_data (blob, face->face_index);
155         hb_blob_destroy (blob);
156       } else {
157         info->hb_face = hb_face_create_for_tables (_get_table, NULL, info);
158       }
159
160
161       hb_face_set_unicode_funcs (info->hb_face, hb_glib_get_unicode_funcs ());
162
163       /* XXX this is such a waste if not SFNT */
164       if (!hb_ot_layout_has_font_glyph_classes (info->hb_face))
165         synthesize_class_def (info);
166     }
167
168   return info;
169 }
170
171 hb_face_t *
172 _pango_ot_info_get_hb_face (PangoOTInfo *info)
173 {
174   return info->hb_face;
175 }
176
177 typedef struct _GlyphInfo GlyphInfo;
178
179 struct _GlyphInfo {
180   unsigned short glyph;
181   unsigned short class;
182 };
183
184 static int
185 compare_glyph_info (gconstpointer a,
186                     gconstpointer b)
187 {
188   const GlyphInfo *info_a = a;
189   const GlyphInfo *info_b = b;
190
191   return (info_a->glyph < info_b->glyph) ? -1 :
192     (info_a->glyph == info_b->glyph) ? 0 : 1;
193 }
194
195 /* Make a guess at the appropriate class for a glyph given
196  * a character code that maps to the glyph
197  */
198 static gboolean
199 get_glyph_class (gunichar        charcode,
200                  unsigned short *class)
201 {
202   /* For characters mapped into the Arabic Presentation forms, using properties
203    * derived as we apply GSUB substitutions will be more reliable
204    */
205   if ((charcode >= 0xFB50 && charcode <= 0xFDFF) || /* Arabic Presentation Forms-A */
206       (charcode >= 0xFE70 && charcode <= 0XFEFF))   /* Arabic Presentation Forms-B */
207     return FALSE;
208
209   switch ((int) g_unichar_type (charcode))
210     {
211     case G_UNICODE_COMBINING_MARK:
212     case G_UNICODE_ENCLOSING_MARK:
213     case G_UNICODE_NON_SPACING_MARK:
214       *class = 3;               /* Mark glyph (non-spacing combining glyph) */
215       return TRUE;
216     case G_UNICODE_UNASSIGNED:
217     case G_UNICODE_PRIVATE_USE:
218       return FALSE;             /* Unknown, don't assign a class; classes get
219                                  * propagated during GSUB application */
220     default:
221       *class = 1;               /* Base glyph (single character, spacing glyph) */
222       return TRUE;
223     }
224 }
225
226 static gboolean
227 set_unicode_charmap (FT_Face face)
228 {
229   int charmap;
230
231   for (charmap = 0; charmap < face->num_charmaps; charmap++)
232     if (face->charmaps[charmap]->encoding == ft_encoding_unicode)
233       {
234         FT_Error error = FT_Set_Charmap(face, face->charmaps[charmap]);
235         return error == FT_Err_Ok;
236       }
237
238   return FALSE;
239 }
240
241 /* Synthesize a GDEF table using the font's charmap and the
242  * Unicode property database. We'll fill in class definitions
243  * for glyphs not in the charmap as we walk through the tables.
244  */
245 static void
246 synthesize_class_def (PangoOTInfo *info)
247 {
248   GArray *glyph_infos;
249   hb_codepoint_t *glyph_indices;
250   unsigned char *classes;
251   gunichar charcode;
252   FT_UInt glyph;
253   unsigned int i, j;
254   FT_CharMap old_charmap;
255
256   old_charmap = info->face->charmap;
257
258   if (!old_charmap || !old_charmap->encoding != ft_encoding_unicode)
259     if (!set_unicode_charmap (info->face))
260       return;
261
262   glyph_infos = g_array_new (FALSE, FALSE, sizeof (GlyphInfo));
263
264   /* Collect all the glyphs in the charmap, and guess
265    * the appropriate classes for them
266    */
267   charcode = FT_Get_First_Char (info->face, &glyph);
268   while (glyph != 0)
269     {
270       GlyphInfo glyph_info;
271
272       if (glyph <= 65535)
273         {
274           glyph_info.glyph = glyph;
275           if (get_glyph_class (charcode, &glyph_info.class))
276             g_array_append_val (glyph_infos, glyph_info);
277         }
278
279       charcode = FT_Get_Next_Char (info->face, charcode, &glyph);
280     }
281
282   /* Sort and remove duplicates
283    */
284   g_array_sort (glyph_infos, compare_glyph_info);
285
286   glyph_indices = g_new (hb_codepoint_t, glyph_infos->len);
287   classes = g_new (unsigned char, glyph_infos->len);
288
289   for (i = 0, j = 0; i < glyph_infos->len; i++)
290     {
291       GlyphInfo *info = &g_array_index (glyph_infos, GlyphInfo, i);
292
293       if (j == 0 || info->glyph != glyph_indices[j - 1])
294         {
295           glyph_indices[j] = info->glyph;
296           classes[j] = info->class;
297
298           j++;
299         }
300     }
301
302   g_array_free (glyph_infos, TRUE);
303
304   hb_ot_layout_build_glyph_classes (info->hb_face, info->face->num_glyphs,
305                                     glyph_indices, classes, j);
306
307   g_free (glyph_indices);
308   g_free (classes);
309
310   if (old_charmap && info->face->charmap != old_charmap)
311     FT_Set_Charmap (info->face, old_charmap);
312 }
313
314 static hb_tag_t
315 get_hb_table_type (PangoOTTableType table_type)
316 {
317   switch (table_type) {
318     case PANGO_OT_TABLE_GSUB: return HB_OT_TAG_GSUB;
319     case PANGO_OT_TABLE_GPOS: return HB_OT_TAG_GPOS;
320     default:                  return HB_TAG_NONE;
321   }
322 }
323
324 /**
325  * pango_ot_info_find_script:
326  * @info: a #PangoOTInfo.
327  * @table_type: the table type to obtain information about.
328  * @script_tag: the tag of the script to find.
329  * @script_index: location to store the index of the script, or %NULL.
330  *
331  * Finds the index of a script.  If not found, tries to find the 'DFLT'
332  * and then 'dflt' scripts and return the index of that in @script_index.
333  * If none of those is found either, %PANGO_OT_NO_SCRIPT is placed in
334  * @script_index.
335  *
336  * All other functions taking an input script_index parameter know
337  * how to handle %PANGO_OT_NO_SCRIPT, so one can ignore the return
338  * value of this function completely and proceed, to enjoy the automatic
339  * fallback to the 'DFLT'/'dflt' script.
340  *
341  * Return value: %TRUE if the script was found.
342  **/
343 gboolean
344 pango_ot_info_find_script (PangoOTInfo      *info,
345                            PangoOTTableType  table_type,
346                            PangoOTTag        script_tag,
347                            guint            *script_index)
348 {
349   hb_tag_t tt = get_hb_table_type (table_type);
350
351   return hb_ot_layout_table_find_script (info->hb_face, tt,
352                                          script_tag,
353                                          script_index);
354 }
355
356 /**
357  * pango_ot_info_find_language:
358  * @info: a #PangoOTInfo.
359  * @table_type: the table type to obtain information about.
360  * @script_index: the index of the script whose languages are searched.
361  * @language_tag: the tag of the language to find.
362  * @language_index: location to store the index of the language, or %NULL.
363  * @required_feature_index: location to store the required feature index of
364  *    the language, or %NULL.
365  *
366  * Finds the index of a language and its required feature index.
367  * If the language is not found, sets @language_index to
368  * PANGO_OT_DEFAULT_LANGUAGE and the required feature of the default language
369  * system is returned in required_feature_index.  For best compatibility with
370  * some fonts, also searches the language system tag 'dflt' before falling
371  * back to the default language system, but that is transparent to the user.
372  * The user can simply ignore the return value of this function to
373  * automatically fall back to the default language system.
374  *
375  * Return value: %TRUE if the language was found.
376  **/
377 gboolean
378 pango_ot_info_find_language (PangoOTInfo      *info,
379                              PangoOTTableType  table_type,
380                              guint             script_index,
381                              PangoOTTag        language_tag,
382                              guint            *language_index,
383                              guint            *required_feature_index)
384 {
385   gboolean ret;
386   unsigned l_index;
387   hb_tag_t tt = get_hb_table_type (table_type);
388
389   ret = hb_ot_layout_script_find_language (info->hb_face, tt,
390                                            script_index,
391                                            language_tag,
392                                            &l_index);
393   if (language_index) *language_index = l_index;
394
395   hb_ot_layout_language_get_required_feature_index (info->hb_face, tt,
396                                                     script_index,
397                                                     l_index,
398                                                     required_feature_index);
399
400   return ret;
401 }
402
403 /**
404  * pango_ot_info_find_feature:
405  * @info: a #PangoOTInfo.
406  * @table_type: the table type to obtain information about.
407  * @feature_tag: the tag of the feature to find.
408  * @script_index: the index of the script.
409  * @language_index: the index of the language whose features are searched,
410  *     or %PANGO_OT_DEFAULT_LANGUAGE to use the default language of the script.
411  * @feature_index: location to store the index of the feature, or %NULL.
412  *
413  * Finds the index of a feature.  If the feature is not found, sets
414  * @feature_index to PANGO_OT_NO_FEATURE, which is safe to pass to
415  * pango_ot_ruleset_add_feature() and similar functions.
416  *
417  * In the future, this may set @feature_index to an special value that if used
418  * in pango_ot_ruleset_add_feature() will ask Pango to synthesize the
419  * requested feature based on Unicode properties and data.  However, this
420  * function will still return %FALSE in those cases.  So, users may want to
421  * ignore the return value of this function in certain cases.
422  *
423  * Return value: %TRUE if the feature was found.
424  **/
425 gboolean
426 pango_ot_info_find_feature  (PangoOTInfo      *info,
427                              PangoOTTableType  table_type,
428                              PangoOTTag        feature_tag,
429                              guint             script_index,
430                              guint             language_index,
431                              guint            *feature_index)
432 {
433   hb_tag_t tt = get_hb_table_type (table_type);
434
435   return hb_ot_layout_language_find_feature (info->hb_face, tt,
436                                              script_index,
437                                              language_index,
438                                              feature_tag,
439                                              feature_index);
440 }
441
442 /**
443  * pango_ot_info_list_scripts:
444  * @info: a #PangoOTInfo.
445  * @table_type: the table type to obtain information about.
446  *
447  * Obtains the list of available scripts.
448  *
449  * Return value: a newly-allocated zero-terminated array containing the tags of the
450  *   available scripts.  Should be freed using g_free().
451  **/
452 PangoOTTag *
453 pango_ot_info_list_scripts (PangoOTInfo      *info,
454                             PangoOTTableType  table_type)
455 {
456   hb_tag_t tt = get_hb_table_type (table_type);
457   PangoOTTag *result;
458   unsigned int count = 0;
459
460   hb_ot_layout_table_get_script_tags (info->hb_face, tt, &count, NULL);
461   result = g_new (PangoOTTag, count + 1);
462   hb_ot_layout_table_get_script_tags (info->hb_face, tt, &count, result);
463   result[count] = 0;
464
465   return result;
466 }
467
468 /**
469  * pango_ot_info_list_languages:
470  * @info: a #PangoOTInfo.
471  * @table_type: the table type to obtain information about.
472  * @script_index: the index of the script to list languages for.
473  * @language_tag: unused parameter.
474  *
475  * Obtains the list of available languages for a given script.
476  *
477  * Return value: a newly-allocated zero-terminated array containing the tags of the
478  *   available languages.  Should be freed using g_free().
479  **/
480 PangoOTTag *
481 pango_ot_info_list_languages (PangoOTInfo      *info,
482                               PangoOTTableType  table_type,
483                               guint             script_index,
484                               PangoOTTag        language_tag G_GNUC_UNUSED)
485 {
486   hb_tag_t tt = get_hb_table_type (table_type);
487   PangoOTTag *result;
488   unsigned int count = 0;
489
490   hb_ot_layout_script_get_language_tags (info->hb_face, tt, script_index, &count, NULL);
491   result = g_new (PangoOTTag, count + 1);
492   hb_ot_layout_script_get_language_tags (info->hb_face, tt, script_index, &count, result);
493   result[count] = 0;
494
495   return result;
496 }
497
498 /**
499  * pango_ot_info_list_features:
500  * @info: a #PangoOTInfo.
501  * @table_type: the table type to obtain information about.
502  * @tag: unused parameter.
503  * @script_index: the index of the script to obtain information about.
504  * @language_index: the index of the language to list features for, or
505  *     %PANGO_OT_DEFAULT_LANGUAGE, to list features for the default
506  *     language of the script.
507  *
508  * Obtains the list of features for the given language of the given script.
509  *
510  * Return value: a newly-allocated zero-terminated array containing the tags of the
511  * available features.  Should be freed using g_free().
512  **/
513 PangoOTTag *
514 pango_ot_info_list_features  (PangoOTInfo      *info,
515                               PangoOTTableType  table_type,
516                               PangoOTTag        tag G_GNUC_UNUSED,
517                               guint             script_index,
518                               guint             language_index)
519 {
520   hb_tag_t tt = get_hb_table_type (table_type);
521   PangoOTTag *result;
522   unsigned int count = 0;
523
524   hb_ot_layout_language_get_feature_tags (info->hb_face, tt, script_index, language_index, &count, NULL);
525   result = g_new (PangoOTTag, count + 1);
526   hb_ot_layout_language_get_feature_tags (info->hb_face, tt, script_index, language_index, &count, result);
527   result[count] = 0;
528
529   return result;
530 }
531
532 void
533 _pango_ot_info_substitute  (const PangoOTInfo    *info,
534                             const PangoOTRuleset *ruleset,
535                             PangoOTBuffer        *buffer)
536 {
537   unsigned int i;
538
539   for (i = 0; i < ruleset->rules->len; i++)
540     {
541       PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i);
542       hb_mask_t mask;
543       unsigned int lookup_count, j;
544       unsigned int lookup_indexes[1000];
545
546       if (rule->table_type != PANGO_OT_TABLE_GSUB)
547         continue;
548
549       mask = rule->property_bit;
550       lookup_count = G_N_ELEMENTS (lookup_indexes);
551       hb_ot_layout_feature_get_lookup_indexes (info->hb_face,
552                                                HB_OT_TAG_GSUB,
553                                                rule->feature_index,
554                                                &lookup_count,
555                                                lookup_indexes);
556
557       lookup_count = MIN (G_N_ELEMENTS (lookup_indexes), lookup_count);
558       for (j = 0; j < lookup_count; j++)
559         hb_ot_layout_substitute_lookup (info->hb_face,
560                                         buffer->buffer,
561                                         lookup_indexes[j],
562                                         rule->property_bit);
563     }
564 }
565
566 void
567 _pango_ot_info_position    (const PangoOTInfo    *info,
568                             const PangoOTRuleset *ruleset,
569                             PangoOTBuffer        *buffer)
570 {
571   unsigned int i;
572   gboolean is_hinted;
573   hb_font_t *hb_font;
574
575   hb_buffer_clear_positions (buffer->buffer);
576
577   /* XXX reuse hb_font */
578   hb_font = hb_font_create ();
579   hb_font_set_scale (hb_font,
580                      info->face->size->metrics.x_scale,
581                      info->face->size->metrics.y_scale);
582   is_hinted = buffer->font->is_hinted;
583   hb_font_set_ppem (hb_font,
584                     is_hinted ? info->face->size->metrics.x_ppem : 0,
585                     is_hinted ? info->face->size->metrics.y_ppem : 0);
586
587   for (i = 0; i < ruleset->rules->len; i++)
588     {
589       PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i);
590       hb_mask_t mask;
591       unsigned int lookup_count, j;
592       unsigned int lookup_indexes[1000];
593
594       if (rule->table_type != PANGO_OT_TABLE_GPOS)
595         continue;
596
597       mask = rule->property_bit;
598       lookup_count = G_N_ELEMENTS (lookup_indexes);
599       hb_ot_layout_feature_get_lookup_indexes (info->hb_face,
600                                                HB_OT_TAG_GPOS,
601                                                rule->feature_index,
602                                                &lookup_count,
603                                                lookup_indexes);
604
605       lookup_count = MIN (G_N_ELEMENTS (lookup_indexes), lookup_count);
606       for (j = 0; j < lookup_count; j++)
607         hb_ot_layout_position_lookup (info->hb_face, hb_font,
608                                       buffer->buffer,
609                                       lookup_indexes[j],
610                                       rule->property_bit);
611
612       buffer->applied_gpos = TRUE;
613     }
614
615     if (buffer->applied_gpos)
616     {
617       unsigned int i, j;
618       unsigned int len = hb_buffer_get_len (buffer->buffer);
619       hb_glyph_position_t *positions = hb_buffer_get_glyph_positions (buffer->buffer);
620
621       /* First handle all left-to-right connections */
622       for (j = 0; j < len; j++)
623       {
624         if (positions[j].cursive_chain > 0)
625           positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
626       }
627
628       /* Then handle all right-to-left connections */
629       for (i = len; i > 0; i--)
630       {
631         j = i - 1;
632
633         if (positions[j].cursive_chain < 0)
634           positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
635       }
636     }
637
638   hb_font_destroy (hb_font);
639 }