2 * Copyright © 1998-2004 David Turner and Werner Lemberg
3 * Copyright © 2006 Behdad Esfahbod
4 * Copyright © 2007,2008,2009 Red Hat, Inc.
5 * Copyright © 2012 Google, Inc.
7 * This is part of HarfBuzz, a text shaping library.
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and its documentation for any purpose, provided that the
12 * above copyright notice and the following two paragraphs appear in
13 * all copies of this software.
15 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
16 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
17 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
18 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
21 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
22 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
23 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
24 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
25 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
27 * Red Hat Author(s): Behdad Esfahbod
28 * Google Author(s): Behdad Esfahbod
31 #include "hb-ot-layout-private.hh"
33 #include "hb-ot-layout-gdef-table.hh"
34 #include "hb-ot-layout-gsub-table.hh"
35 #include "hb-ot-layout-gpos-table.hh"
36 #include "hb-ot-maxp-table.hh"
43 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
46 _hb_ot_layout_create (hb_face_t *face)
48 hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
49 if (unlikely (!layout))
52 layout->gdef_blob = Sanitizer<GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF));
53 layout->gdef = Sanitizer<GDEF>::lock_instance (layout->gdef_blob);
55 layout->gsub_blob = Sanitizer<GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB));
56 layout->gsub = Sanitizer<GSUB>::lock_instance (layout->gsub_blob);
58 layout->gpos_blob = Sanitizer<GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
59 layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_blob);
61 layout->gsub_lookup_count = layout->gsub->get_lookup_count ();
62 layout->gpos_lookup_count = layout->gpos->get_lookup_count ();
64 layout->gsub_digests = (hb_set_digest_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_set_digest_t));
65 layout->gpos_digests = (hb_set_digest_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_set_digest_t));
67 if (unlikely ((layout->gsub_lookup_count && !layout->gsub_digests) ||
68 (layout->gpos_lookup_count && !layout->gpos_digests)))
70 _hb_ot_layout_destroy (layout);
74 for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
75 layout->gsub->add_coverage (&layout->gsub_digests[i], i);
76 for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
77 layout->gpos->add_coverage (&layout->gpos_digests[i], i);
83 _hb_ot_layout_destroy (hb_ot_layout_t *layout)
85 hb_blob_destroy (layout->gdef_blob);
86 hb_blob_destroy (layout->gsub_blob);
87 hb_blob_destroy (layout->gpos_blob);
89 free (layout->gsub_digests);
90 free (layout->gpos_digests);
95 static inline const GDEF&
96 _get_gdef (hb_face_t *face)
98 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(GDEF);
99 return *hb_ot_layout_from_face (face)->gdef;
101 static inline const GSUB&
102 _get_gsub (hb_face_t *face)
104 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(GSUB);
105 return *hb_ot_layout_from_face (face)->gsub;
107 static inline const GPOS&
108 _get_gpos (hb_face_t *face)
110 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(GPOS);
111 return *hb_ot_layout_from_face (face)->gpos;
120 hb_ot_layout_has_glyph_classes (hb_face_t *face)
122 return _get_gdef (face).has_glyph_classes ();
127 hb_ot_layout_get_attach_points (hb_face_t *face,
128 hb_codepoint_t glyph,
129 unsigned int start_offset,
130 unsigned int *point_count /* IN/OUT */,
131 unsigned int *point_array /* OUT */)
133 return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
137 hb_ot_layout_get_ligature_carets (hb_font_t *font,
138 hb_direction_t direction,
139 hb_codepoint_t glyph,
140 unsigned int start_offset,
141 unsigned int *caret_count /* IN/OUT */,
142 int *caret_array /* OUT */)
144 return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
152 static const GSUBGPOS&
153 get_gsubgpos_table (hb_face_t *face,
157 case HB_OT_TAG_GSUB: return _get_gsub (face);
158 case HB_OT_TAG_GPOS: return _get_gpos (face);
159 default: return Null(GSUBGPOS);
165 hb_ot_layout_table_get_script_tags (hb_face_t *face,
167 unsigned int start_offset,
168 unsigned int *script_count /* IN/OUT */,
169 hb_tag_t *script_tags /* OUT */)
171 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
173 return g.get_script_tags (start_offset, script_count, script_tags);
177 hb_ot_layout_table_find_script (hb_face_t *face,
180 unsigned int *script_index)
182 ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
183 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
185 if (g.find_script_index (script_tag, script_index))
188 /* try finding 'DFLT' */
189 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
192 /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
193 * including many versions of DejaVu Sans Mono! */
194 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
197 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
202 hb_ot_layout_table_choose_script (hb_face_t *face,
204 const hb_tag_t *script_tags,
205 unsigned int *script_index,
206 hb_tag_t *chosen_script)
208 ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
209 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
213 if (g.find_script_index (*script_tags, script_index)) {
215 *chosen_script = *script_tags;
221 /* try finding 'DFLT' */
222 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
224 *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
228 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
229 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
231 *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
235 /* try with 'latn'; some old fonts put their features there even though
236 they're really trying to support Thai, for example :( */
237 #define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n')
238 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
240 *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
244 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
246 *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
251 hb_ot_layout_table_get_feature_tags (hb_face_t *face,
253 unsigned int start_offset,
254 unsigned int *feature_count /* IN/OUT */,
255 hb_tag_t *feature_tags /* OUT */)
257 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
259 return g.get_feature_tags (start_offset, feature_count, feature_tags);
264 hb_ot_layout_script_get_language_tags (hb_face_t *face,
266 unsigned int script_index,
267 unsigned int start_offset,
268 unsigned int *language_count /* IN/OUT */,
269 hb_tag_t *language_tags /* OUT */)
271 const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
273 return s.get_lang_sys_tags (start_offset, language_count, language_tags);
277 hb_ot_layout_script_find_language (hb_face_t *face,
279 unsigned int script_index,
280 hb_tag_t language_tag,
281 unsigned int *language_index)
283 ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
284 const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
286 if (s.find_lang_sys_index (language_tag, language_index))
289 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
290 if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
293 if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
298 hb_ot_layout_language_get_required_feature_index (hb_face_t *face,
300 unsigned int script_index,
301 unsigned int language_index,
302 unsigned int *feature_index)
304 const LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index);
306 if (feature_index) *feature_index = l.get_required_feature_index ();
308 return l.has_required_feature ();
312 hb_ot_layout_language_get_feature_indexes (hb_face_t *face,
314 unsigned int script_index,
315 unsigned int language_index,
316 unsigned int start_offset,
317 unsigned int *feature_count /* IN/OUT */,
318 unsigned int *feature_indexes /* OUT */)
320 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
321 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
323 return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
327 hb_ot_layout_language_get_feature_tags (hb_face_t *face,
329 unsigned int script_index,
330 unsigned int language_index,
331 unsigned int start_offset,
332 unsigned int *feature_count /* IN/OUT */,
333 hb_tag_t *feature_tags /* OUT */)
335 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
336 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
338 ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
339 unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
342 unsigned int count = *feature_count;
343 for (unsigned int i = 0; i < count; i++)
344 feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
352 hb_ot_layout_language_find_feature (hb_face_t *face,
354 unsigned int script_index,
355 unsigned int language_index,
356 hb_tag_t feature_tag,
357 unsigned int *feature_index)
359 ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
360 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
361 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
363 unsigned int num_features = l.get_feature_count ();
364 for (unsigned int i = 0; i < num_features; i++) {
365 unsigned int f_index = l.get_feature_index (i);
367 if (feature_tag == g.get_feature_tag (f_index)) {
368 if (feature_index) *feature_index = f_index;
373 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
378 hb_ot_layout_feature_get_lookup_indexes (hb_face_t *face,
380 unsigned int feature_index,
381 unsigned int start_offset,
382 unsigned int *lookup_count /* IN/OUT */,
383 unsigned int *lookup_indexes /* OUT */)
385 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
386 const Feature &f = g.get_feature (feature_index);
388 return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
397 hb_ot_layout_has_substitution (hb_face_t *face)
399 return &_get_gsub (face) != &Null(GSUB);
403 hb_ot_layout_would_substitute_lookup (hb_face_t *face,
404 const hb_codepoint_t *glyphs,
405 unsigned int glyphs_length,
406 unsigned int lookup_index)
408 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
409 return hb_ot_layout_would_substitute_lookup_fast (face, glyphs, glyphs_length, lookup_index);
413 hb_ot_layout_would_substitute_lookup_fast (hb_face_t *face,
414 const hb_codepoint_t *glyphs,
415 unsigned int glyphs_length,
416 unsigned int lookup_index)
418 if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
419 hb_would_apply_context_t c (face, glyphs, glyphs_length, &hb_ot_layout_from_face (face)->gsub_digests[lookup_index]);
420 return hb_ot_layout_from_face (face)->gsub->would_substitute_lookup (&c, lookup_index);
424 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
426 GSUB::substitute_start (font, buffer);
430 hb_ot_layout_substitute_lookup (hb_font_t *font,
432 unsigned int lookup_index,
435 if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gsub_lookup_count)) return false;
436 hb_apply_context_t c (font, buffer, mask, &hb_ot_layout_from_face (font->face)->gsub_digests[lookup_index]);
437 return hb_ot_layout_from_face (font->face)->gsub->substitute_lookup (&c, lookup_index);
441 hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
443 GSUB::substitute_finish (font, buffer);
447 hb_ot_layout_substitute_closure_lookup (hb_face_t *face,
449 unsigned int lookup_index)
451 hb_closure_context_t c (face, glyphs);
452 _get_gsub (face).closure_lookup (&c, lookup_index);
460 hb_ot_layout_has_positioning (hb_face_t *face)
462 return &_get_gpos (face) != &Null(GPOS);
466 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
468 GPOS::position_start (font, buffer);
472 hb_ot_layout_position_lookup (hb_font_t *font,
474 unsigned int lookup_index,
477 if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gpos_lookup_count)) return false;
478 hb_apply_context_t c (font, buffer, mask, &hb_ot_layout_from_face (font->face)->gpos_digests[lookup_index]);
479 return hb_ot_layout_from_face (font->face)->gpos->position_lookup (&c, lookup_index);
483 hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer, hb_bool_t zero_width_attached_marks)
485 GPOS::position_finish (font, buffer, zero_width_attached_marks);