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