hb-ot-layout.cc revision 05bd1b63426e07d1df7a1b40bf845dc94ab995a8
1/*
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.
6 *
7 *  This is part of HarfBuzz, a text shaping library.
8 *
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.
14 *
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
19 * DAMAGE.
20 *
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.
26 *
27 * Red Hat Author(s): Behdad Esfahbod
28 * Google Author(s): Behdad Esfahbod
29 */
30
31#include "hb-ot-layout-private.hh"
32
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"
37
38
39#include <stdlib.h>
40#include <string.h>
41
42
43HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
44
45static hb_bool_t
46hb_ot_layout_ensure (hb_face_t *face)
47{
48  return hb_ot_shaper_face_data_ensure (face);
49}
50
51
52hb_ot_layout_t *
53_hb_ot_layout_create (hb_face_t *face)
54{
55  hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
56
57  layout->gdef_blob = Sanitizer<GDEF>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GDEF));
58  layout->gdef = Sanitizer<GDEF>::lock_instance (layout->gdef_blob);
59
60  layout->gsub_blob = Sanitizer<GSUB>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GSUB));
61  layout->gsub = Sanitizer<GSUB>::lock_instance (layout->gsub_blob);
62
63  layout->gpos_blob = Sanitizer<GPOS>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GPOS));
64  layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_blob);
65
66  return layout;
67}
68
69void
70_hb_ot_layout_destroy (hb_ot_layout_t *layout)
71{
72  hb_blob_destroy (layout->gdef_blob);
73  hb_blob_destroy (layout->gsub_blob);
74  hb_blob_destroy (layout->gpos_blob);
75
76  free (layout);
77}
78
79static inline const GDEF&
80_get_gdef (hb_face_t *face)
81{
82  if (unlikely (!hb_ot_layout_ensure (face))) return Null(GDEF);
83  return *hb_ot_layout_from_face (face)->gdef;
84}
85static inline const GSUB&
86_get_gsub (hb_face_t *face)
87{
88  if (unlikely (!hb_ot_layout_ensure (face))) return Null(GSUB);
89  return *hb_ot_layout_from_face (face)->gsub;
90}
91static inline const GPOS&
92_get_gpos (hb_face_t *face)
93{
94  if (unlikely (!hb_ot_layout_ensure (face))) return Null(GPOS);
95  return *hb_ot_layout_from_face (face)->gpos;
96}
97
98
99/*
100 * GDEF
101 */
102
103hb_bool_t
104hb_ot_layout_has_glyph_classes (hb_face_t *face)
105{
106  return _get_gdef (face).has_glyph_classes ();
107}
108
109
110unsigned int
111hb_ot_layout_get_attach_points (hb_face_t      *face,
112				hb_codepoint_t  glyph,
113				unsigned int    start_offset,
114				unsigned int   *point_count /* IN/OUT */,
115				unsigned int   *point_array /* OUT */)
116{
117  return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
118}
119
120unsigned int
121hb_ot_layout_get_ligature_carets (hb_font_t      *font,
122				  hb_direction_t  direction,
123				  hb_codepoint_t  glyph,
124				  unsigned int    start_offset,
125				  unsigned int   *caret_count /* IN/OUT */,
126				  int            *caret_array /* OUT */)
127{
128  return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
129}
130
131
132/*
133 * GSUB/GPOS
134 */
135
136static const GSUBGPOS&
137get_gsubgpos_table (hb_face_t *face,
138		    hb_tag_t   table_tag)
139{
140  switch (table_tag) {
141    case HB_OT_TAG_GSUB: return _get_gsub (face);
142    case HB_OT_TAG_GPOS: return _get_gpos (face);
143    default:             return Null(GSUBGPOS);
144  }
145}
146
147
148unsigned int
149hb_ot_layout_table_get_script_tags (hb_face_t    *face,
150				    hb_tag_t      table_tag,
151				    unsigned int  start_offset,
152				    unsigned int *script_count /* IN/OUT */,
153				    hb_tag_t     *script_tags /* OUT */)
154{
155  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
156
157  return g.get_script_tags (start_offset, script_count, script_tags);
158}
159
160hb_bool_t
161hb_ot_layout_table_find_script (hb_face_t    *face,
162				hb_tag_t      table_tag,
163				hb_tag_t      script_tag,
164				unsigned int *script_index)
165{
166  ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
167  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
168
169  if (g.find_script_index (script_tag, script_index))
170    return true;
171
172  /* try finding 'DFLT' */
173  if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
174    return false;
175
176  /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
177   * including many versions of DejaVu Sans Mono! */
178  if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
179    return false;
180
181  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
182  return false;
183}
184
185hb_bool_t
186hb_ot_layout_table_choose_script (hb_face_t      *face,
187				  hb_tag_t        table_tag,
188				  const hb_tag_t *script_tags,
189				  unsigned int   *script_index,
190				  hb_tag_t       *chosen_script)
191{
192  ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
193  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
194
195  while (*script_tags)
196  {
197    if (g.find_script_index (*script_tags, script_index)) {
198      if (chosen_script)
199        *chosen_script = *script_tags;
200      return true;
201    }
202    script_tags++;
203  }
204
205  /* try finding 'DFLT' */
206  if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
207    if (chosen_script)
208      *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
209    return false;
210  }
211
212  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
213  if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
214    if (chosen_script)
215      *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
216    return false;
217  }
218
219  /* try with 'latn'; some old fonts put their features there even though
220     they're really trying to support Thai, for example :( */
221#define HB_OT_TAG_LATIN_SCRIPT		HB_TAG ('l', 'a', 't', 'n')
222  if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
223    if (chosen_script)
224      *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
225    return false;
226  }
227
228  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
229  if (chosen_script)
230    *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
231  return false;
232}
233
234unsigned int
235hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
236				     hb_tag_t      table_tag,
237				     unsigned int  start_offset,
238				     unsigned int *feature_count /* IN/OUT */,
239				     hb_tag_t     *feature_tags /* OUT */)
240{
241  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
242
243  return g.get_feature_tags (start_offset, feature_count, feature_tags);
244}
245
246
247unsigned int
248hb_ot_layout_script_get_language_tags (hb_face_t    *face,
249				       hb_tag_t      table_tag,
250				       unsigned int  script_index,
251				       unsigned int  start_offset,
252				       unsigned int *language_count /* IN/OUT */,
253				       hb_tag_t     *language_tags /* OUT */)
254{
255  const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
256
257  return s.get_lang_sys_tags (start_offset, language_count, language_tags);
258}
259
260hb_bool_t
261hb_ot_layout_script_find_language (hb_face_t    *face,
262				   hb_tag_t      table_tag,
263				   unsigned int  script_index,
264				   hb_tag_t      language_tag,
265				   unsigned int *language_index)
266{
267  ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
268  const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
269
270  if (s.find_lang_sys_index (language_tag, language_index))
271    return true;
272
273  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
274  if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
275    return false;
276
277  if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
278  return false;
279}
280
281hb_bool_t
282hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
283						  hb_tag_t      table_tag,
284						  unsigned int  script_index,
285						  unsigned int  language_index,
286						  unsigned int *feature_index)
287{
288  const LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index);
289
290  if (feature_index) *feature_index = l.get_required_feature_index ();
291
292  return l.has_required_feature ();
293}
294
295unsigned int
296hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
297					   hb_tag_t      table_tag,
298					   unsigned int  script_index,
299					   unsigned int  language_index,
300					   unsigned int  start_offset,
301					   unsigned int *feature_count /* IN/OUT */,
302					   unsigned int *feature_indexes /* OUT */)
303{
304  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
305  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
306
307  return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
308}
309
310unsigned int
311hb_ot_layout_language_get_feature_tags (hb_face_t    *face,
312					hb_tag_t      table_tag,
313					unsigned int  script_index,
314					unsigned int  language_index,
315					unsigned int  start_offset,
316					unsigned int *feature_count /* IN/OUT */,
317					hb_tag_t     *feature_tags /* OUT */)
318{
319  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
320  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
321
322  ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
323  unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
324
325  if (feature_tags) {
326    unsigned int count = *feature_count;
327    for (unsigned int i = 0; i < count; i++)
328      feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
329  }
330
331  return ret;
332}
333
334
335hb_bool_t
336hb_ot_layout_language_find_feature (hb_face_t    *face,
337				    hb_tag_t      table_tag,
338				    unsigned int  script_index,
339				    unsigned int  language_index,
340				    hb_tag_t      feature_tag,
341				    unsigned int *feature_index)
342{
343  ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
344  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
345  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
346
347  unsigned int num_features = l.get_feature_count ();
348  for (unsigned int i = 0; i < num_features; i++) {
349    unsigned int f_index = l.get_feature_index (i);
350
351    if (feature_tag == g.get_feature_tag (f_index)) {
352      if (feature_index) *feature_index = f_index;
353      return true;
354    }
355  }
356
357  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
358  return false;
359}
360
361unsigned int
362hb_ot_layout_feature_get_lookup_indexes (hb_face_t    *face,
363					 hb_tag_t      table_tag,
364					 unsigned int  feature_index,
365					 unsigned int  start_offset,
366					 unsigned int *lookup_count /* IN/OUT */,
367					 unsigned int *lookup_indexes /* OUT */)
368{
369  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
370  const Feature &f = g.get_feature (feature_index);
371
372  return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
373}
374
375
376/*
377 * GSUB
378 */
379
380hb_bool_t
381hb_ot_layout_has_substitution (hb_face_t *face)
382{
383  return &_get_gsub (face) != &Null(GSUB);
384}
385
386hb_bool_t
387hb_ot_layout_would_substitute_lookup (hb_face_t            *face,
388				      const hb_codepoint_t *glyphs,
389				      unsigned int          glyphs_length,
390				      unsigned int          lookup_index)
391{
392  if (unlikely (glyphs_length < 1 || glyphs_length > 2)) return false;
393  hb_would_apply_context_t c (face, glyphs[0], glyphs_length == 2 ? glyphs[1] : -1);
394  return _get_gsub (face).would_substitute_lookup (&c, lookup_index);
395}
396
397hb_bool_t
398hb_ot_layout_would_substitute_lookup_fast (hb_face_t            *face,
399					   const hb_codepoint_t *glyphs,
400					   unsigned int          glyphs_length,
401					   unsigned int          lookup_index)
402{
403  if (unlikely (glyphs_length < 1 || glyphs_length > 2)) return false;
404  hb_would_apply_context_t c (face, glyphs[0], glyphs_length == 2 ? glyphs[1] : -1);
405  return hb_ot_layout_from_face (face)->gsub->would_substitute_lookup (&c, lookup_index);
406}
407
408void
409hb_ot_layout_substitute_start (hb_face_t *face, hb_buffer_t *buffer)
410{
411  GSUB::substitute_start (face, buffer);
412}
413
414hb_bool_t
415hb_ot_layout_substitute_lookup (hb_face_t    *face,
416				hb_buffer_t  *buffer,
417				unsigned int  lookup_index,
418				hb_mask_t     mask)
419{
420  hb_apply_context_t c (NULL, face, buffer, mask);
421  return _get_gsub (face).substitute_lookup (&c, lookup_index);
422}
423
424hb_bool_t
425hb_ot_layout_substitute_lookup_fast (hb_face_t    *face,
426				     hb_buffer_t  *buffer,
427				     unsigned int  lookup_index,
428				     hb_mask_t     mask)
429{
430  hb_apply_context_t c (NULL, face, buffer, mask);
431  return hb_ot_layout_from_face (face)->gsub->substitute_lookup (&c, lookup_index);
432}
433
434void
435hb_ot_layout_substitute_finish (hb_face_t *face, hb_buffer_t *buffer)
436{
437  GSUB::substitute_finish (face, buffer);
438}
439
440void
441hb_ot_layout_substitute_closure_lookup (hb_face_t    *face,
442				        hb_set_t     *glyphs,
443				        unsigned int  lookup_index)
444{
445  hb_closure_context_t c (face, glyphs);
446  _get_gsub (face).closure_lookup (&c, lookup_index);
447}
448
449/*
450 * GPOS
451 */
452
453hb_bool_t
454hb_ot_layout_has_positioning (hb_face_t *face)
455{
456  return &_get_gpos (face) != &Null(GPOS);
457}
458
459void
460hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
461{
462  GPOS::position_start (font, buffer);
463}
464
465hb_bool_t
466hb_ot_layout_position_lookup (hb_font_t    *font,
467			      hb_buffer_t  *buffer,
468			      unsigned int  lookup_index,
469			      hb_mask_t     mask)
470{
471  hb_apply_context_t c (font, font->face, buffer, mask);
472  return _get_gpos (font->face).position_lookup (&c, lookup_index);
473}
474
475hb_bool_t
476hb_ot_layout_position_lookup_fast (hb_font_t    *font,
477				   hb_buffer_t  *buffer,
478				   unsigned int  lookup_index,
479				   hb_mask_t     mask)
480{
481  hb_apply_context_t c (font, font->face, buffer, mask);
482  return hb_ot_layout_from_face (font->face)->gpos->position_lookup (&c, lookup_index);
483}
484
485void
486hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer)
487{
488  GPOS::position_finish (font, buffer);
489}
490
491
492