2 * pango-ot-info.c: Store tables for OpenType
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"
26 #include FT_TRUETYPE_TABLES_H
28 #if (!GLIB_CHECK_VERSION(2,29,15))
29 #define G_UNICODE_SPACING_MARK G_UNICODE_COMBINING_MARK
32 static void pango_ot_info_finalize (GObject *object);
34 static void synthesize_class_def (PangoOTInfo *info);
36 G_DEFINE_TYPE (PangoOTInfo, pango_ot_info, G_TYPE_OBJECT);
39 pango_ot_info_init (PangoOTInfo *self)
44 pango_ot_info_class_init (PangoOTInfoClass *klass)
46 GObjectClass *object_class = G_OBJECT_CLASS (klass);
48 object_class->finalize = pango_ot_info_finalize;
52 pango_ot_info_finalize (GObject *object)
54 PangoOTInfo *info = PANGO_OT_INFO (object);
57 hb_face_destroy (info->hb_face);
59 G_OBJECT_CLASS (pango_ot_info_parent_class)->finalize (object);
63 pango_ot_info_finalizer (void *object)
65 FT_Face face = object;
66 PangoOTInfo *info = face->generic.data;
69 g_object_unref (info);
73 _get_table (hb_tag_t tag, void *user_data)
75 PangoOTInfo *info = (PangoOTInfo *) user_data;
80 error = FT_Load_Sfnt_Table (info->face, tag, 0, NULL, &length);
82 return hb_blob_create_empty ();
84 buffer = g_malloc (length);
86 return hb_blob_create_empty ();
88 error = FT_Load_Sfnt_Table (info->face, tag, 0, buffer, &length);
90 return hb_blob_create_empty ();
92 return hb_blob_create ((const char *) buffer, length,
93 HB_MEMORY_MODE_WRITABLE,
100 * @face: a <type>FT_Face</type>.
102 * Returns the #PangoOTInfo structure for the given FreeType font face.
104 * Return value: the #PangoOTInfo for @face. This object will have
105 * the same lifetime as @face.
110 pango_ot_info_get (FT_Face face)
114 if (G_LIKELY (face->generic.data && face->generic.finalizer == pango_ot_info_finalizer))
115 return face->generic.data;
118 if (face->generic.finalizer)
119 face->generic.finalizer (face);
121 info = face->generic.data = g_object_new (PANGO_TYPE_OT_INFO, NULL);
122 face->generic.finalizer = pango_ot_info_finalizer;
126 if (face->stream->read == NULL) {
129 blob = hb_blob_create ((const char *) face->stream->base,
130 (unsigned int) face->stream->size,
131 HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE,
133 info->hb_face = hb_face_create_for_data (blob, face->face_index);
134 hb_blob_destroy (blob);
136 info->hb_face = hb_face_create_for_tables (_get_table, NULL, info);
140 hb_face_set_unicode_funcs (info->hb_face, hb_glib_get_unicode_funcs ());
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);
151 _pango_ot_info_get_hb_face (PangoOTInfo *info)
153 return info->hb_face;
156 typedef struct _GlyphInfo GlyphInfo;
159 unsigned short glyph;
160 unsigned short class;
164 compare_glyph_info (gconstpointer a,
167 const GlyphInfo *info_a = a;
168 const GlyphInfo *info_b = b;
170 return (info_a->glyph < info_b->glyph) ? -1 :
171 (info_a->glyph == info_b->glyph) ? 0 : 1;
174 /* Make a guess at the appropriate class for a glyph given
175 * a character code that maps to the glyph
178 get_glyph_class (gunichar charcode,
179 unsigned short *class)
181 /* For characters mapped into the Arabic Presentation forms, using properties
182 * derived as we apply GSUB substitutions will be more reliable
184 if ((charcode >= 0xFB50 && charcode <= 0xFDFF) || /* Arabic Presentation Forms-A */
185 (charcode >= 0xFE70 && charcode <= 0XFEFF)) /* Arabic Presentation Forms-B */
188 switch ((int) g_unichar_type (charcode))
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) */
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 */
200 *class = HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH; /* Base glyph (single character, spacing glyph) */
206 set_unicode_charmap (FT_Face face)
210 for (charmap = 0; charmap < face->num_charmaps; charmap++)
211 if (face->charmaps[charmap]->encoding == ft_encoding_unicode)
213 FT_Error error = FT_Set_Charmap(face, face->charmaps[charmap]);
214 return error == FT_Err_Ok;
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.
225 synthesize_class_def (PangoOTInfo *info)
228 hb_codepoint_t *glyph_indices;
229 unsigned char *classes;
233 FT_CharMap old_charmap;
235 old_charmap = info->face->charmap;
237 if (!old_charmap || old_charmap->encoding != ft_encoding_unicode)
238 if (!set_unicode_charmap (info->face))
241 glyph_infos = g_array_new (FALSE, FALSE, sizeof (GlyphInfo));
243 /* Collect all the glyphs in the charmap, and guess
244 * the appropriate classes for them
246 charcode = FT_Get_First_Char (info->face, &glyph);
249 GlyphInfo glyph_info;
253 glyph_info.glyph = glyph;
254 if (get_glyph_class (charcode, &glyph_info.class))
255 g_array_append_val (glyph_infos, glyph_info);
258 charcode = FT_Get_Next_Char (info->face, charcode, &glyph);
261 /* Sort and remove duplicates
263 g_array_sort (glyph_infos, compare_glyph_info);
265 glyph_indices = g_new (hb_codepoint_t, glyph_infos->len);
266 classes = g_new (unsigned char, glyph_infos->len);
268 for (i = 0, j = 0; i < glyph_infos->len; i++)
270 GlyphInfo *info = &g_array_index (glyph_infos, GlyphInfo, i);
272 if (j == 0 || info->glyph != glyph_indices[j - 1])
274 glyph_indices[j] = info->glyph;
275 classes[j] = info->class;
281 g_array_free (glyph_infos, TRUE);
283 hb_ot_layout_build_glyph_classes (info->hb_face, glyph_indices, classes, j);
285 g_free (glyph_indices);
288 if (old_charmap && info->face->charmap != old_charmap)
289 FT_Set_Charmap (info->face, old_charmap);
293 get_hb_table_type (PangoOTTableType table_type)
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;
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.
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
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.
319 * Return value: %TRUE if the script was found.
322 pango_ot_info_find_script (PangoOTInfo *info,
323 PangoOTTableType table_type,
324 PangoOTTag script_tag,
327 hb_tag_t tt = get_hb_table_type (table_type);
329 return hb_ot_layout_table_find_script (info->hb_face, tt,
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.
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.
353 * Return value: %TRUE if the language was found.
356 pango_ot_info_find_language (PangoOTInfo *info,
357 PangoOTTableType table_type,
359 PangoOTTag language_tag,
360 guint *language_index,
361 guint *required_feature_index)
365 hb_tag_t tt = get_hb_table_type (table_type);
367 ret = hb_ot_layout_script_find_language (info->hb_face, tt,
371 if (language_index) *language_index = l_index;
373 hb_ot_layout_language_get_required_feature_index (info->hb_face, tt,
376 required_feature_index);
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.
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.
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.
401 * Return value: %TRUE if the feature was found.
404 pango_ot_info_find_feature (PangoOTInfo *info,
405 PangoOTTableType table_type,
406 PangoOTTag feature_tag,
408 guint language_index,
409 guint *feature_index)
411 hb_tag_t tt = get_hb_table_type (table_type);
413 return hb_ot_layout_language_find_feature (info->hb_face, tt,
421 * pango_ot_info_list_scripts:
422 * @info: a #PangoOTInfo.
423 * @table_type: the table type to obtain information about.
425 * Obtains the list of available scripts.
427 * Return value: a newly-allocated zero-terminated array containing the tags of the
428 * available scripts. Should be freed using g_free().
431 pango_ot_info_list_scripts (PangoOTInfo *info,
432 PangoOTTableType table_type)
434 hb_tag_t tt = get_hb_table_type (table_type);
436 unsigned int count = 0;
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);
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.
453 * Obtains the list of available languages for a given script.
455 * Return value: a newly-allocated zero-terminated array containing the tags of the
456 * available languages. Should be freed using g_free().
459 pango_ot_info_list_languages (PangoOTInfo *info,
460 PangoOTTableType table_type,
462 PangoOTTag language_tag G_GNUC_UNUSED)
464 hb_tag_t tt = get_hb_table_type (table_type);
466 unsigned int count = 0;
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);
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.
486 * Obtains the list of features for the given language of the given script.
488 * Return value: a newly-allocated zero-terminated array containing the tags of the
489 * available features. Should be freed using g_free().
492 pango_ot_info_list_features (PangoOTInfo *info,
493 PangoOTTableType table_type,
494 PangoOTTag tag G_GNUC_UNUSED,
496 guint language_index)
498 hb_tag_t tt = get_hb_table_type (table_type);
500 unsigned int count = 0;
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);
511 _pango_ot_info_substitute (const PangoOTInfo *info,
512 const PangoOTRuleset *ruleset,
513 PangoOTBuffer *buffer)
517 for (i = 0; i < ruleset->rules->len; i++)
519 PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i);
520 unsigned int lookup_count, j;
521 unsigned int lookup_indexes[1000];
523 if (rule->table_type != PANGO_OT_TABLE_GSUB)
526 lookup_count = G_N_ELEMENTS (lookup_indexes);
527 hb_ot_layout_feature_get_lookup_indexes (info->hb_face,
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,
543 _pango_ot_info_position (const PangoOTInfo *info,
544 const PangoOTRuleset *ruleset,
545 PangoOTBuffer *buffer)
551 hb_buffer_clear_positions (buffer->buffer);
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);
563 for (i = 0; i < ruleset->rules->len; i++)
565 PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i);
567 unsigned int lookup_count, j;
568 unsigned int lookup_indexes[1000];
570 if (rule->table_type != PANGO_OT_TABLE_GPOS)
573 mask = rule->property_bit;
574 lookup_count = G_N_ELEMENTS (lookup_indexes);
575 hb_ot_layout_feature_get_lookup_indexes (info->hb_face,
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,
588 buffer->applied_gpos = TRUE;
591 if (buffer->applied_gpos)
594 unsigned int len = hb_buffer_get_len (buffer->buffer);
595 hb_glyph_position_t *positions = hb_buffer_get_glyph_positions (buffer->buffer);
597 /* First handle all left-to-right connections */
598 for (j = 0; j < len; j++)
600 if (positions[j].cursive_chain > 0)
601 positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
604 /* Then handle all right-to-left connections */
605 for (i = len; i > 0; i--)
609 if (positions[j].cursive_chain < 0)
610 positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
614 hb_font_destroy (hb_font);