hb-ot-layout.cc revision f0c82410dbe800cb6429ba4aa7cfd9f5a11cc70c
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
37#include <stdlib.h>
38#include <string.h>
39
40
41HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
42
43hb_ot_layout_t *
44_hb_ot_layout_create (hb_face_t *face)
45{
46  hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
47  if (unlikely (!layout))
48    return NULL;
49
50  layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF));
51  layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob);
52
53  layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB));
54  layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob);
55
56  layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
57  layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
58
59  layout->gsub_lookup_count = layout->gsub->get_lookup_count ();
60  layout->gpos_lookup_count = layout->gpos->get_lookup_count ();
61
62  layout->gsub_digests = (hb_set_digest_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_set_digest_t));
63  layout->gpos_digests = (hb_set_digest_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_set_digest_t));
64
65  if (unlikely ((layout->gsub_lookup_count && !layout->gsub_digests) ||
66		(layout->gpos_lookup_count && !layout->gpos_digests)))
67  {
68    _hb_ot_layout_destroy (layout);
69    return NULL;
70  }
71
72  for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
73    layout->gsub->get_lookup (i).add_coverage (&layout->gsub_digests[i]);
74  for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
75    layout->gpos->get_lookup (i).add_coverage (&layout->gpos_digests[i]);
76
77  return layout;
78}
79
80void
81_hb_ot_layout_destroy (hb_ot_layout_t *layout)
82{
83  hb_blob_destroy (layout->gdef_blob);
84  hb_blob_destroy (layout->gsub_blob);
85  hb_blob_destroy (layout->gpos_blob);
86
87  free (layout->gsub_digests);
88  free (layout->gpos_digests);
89
90  free (layout);
91}
92
93static inline const OT::GDEF&
94_get_gdef (hb_face_t *face)
95{
96  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF);
97  return *hb_ot_layout_from_face (face)->gdef;
98}
99static inline const OT::GSUB&
100_get_gsub (hb_face_t *face)
101{
102  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB);
103  return *hb_ot_layout_from_face (face)->gsub;
104}
105static inline const OT::GPOS&
106_get_gpos (hb_face_t *face)
107{
108  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS);
109  return *hb_ot_layout_from_face (face)->gpos;
110}
111
112
113/*
114 * GDEF
115 */
116
117hb_bool_t
118hb_ot_layout_has_glyph_classes (hb_face_t *face)
119{
120  return _get_gdef (face).has_glyph_classes ();
121}
122
123hb_ot_layout_glyph_class_t
124hb_ot_layout_get_glyph_class (hb_face_t      *face,
125			      hb_codepoint_t  glyph)
126{
127  return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph);
128}
129
130void
131hb_ot_layout_get_glyphs_in_class (hb_face_t                  *face,
132				  hb_ot_layout_glyph_class_t  klass,
133				  hb_set_t                   *glyphs /* OUT */)
134{
135  return _get_gdef (face).get_glyphs_in_class (klass, glyphs);
136}
137
138unsigned int
139hb_ot_layout_get_attach_points (hb_face_t      *face,
140				hb_codepoint_t  glyph,
141				unsigned int    start_offset,
142				unsigned int   *point_count /* IN/OUT */,
143				unsigned int   *point_array /* OUT */)
144{
145  return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
146}
147
148unsigned int
149hb_ot_layout_get_ligature_carets (hb_font_t      *font,
150				  hb_direction_t  direction,
151				  hb_codepoint_t  glyph,
152				  unsigned int    start_offset,
153				  unsigned int   *caret_count /* IN/OUT */,
154				  int            *caret_array /* OUT */)
155{
156  return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
157}
158
159
160/*
161 * GSUB/GPOS
162 */
163
164static const OT::GSUBGPOS&
165get_gsubgpos_table (hb_face_t *face,
166		    hb_tag_t   table_tag)
167{
168  switch (table_tag) {
169    case HB_OT_TAG_GSUB: return _get_gsub (face);
170    case HB_OT_TAG_GPOS: return _get_gpos (face);
171    default:             return OT::Null(OT::GSUBGPOS);
172  }
173}
174
175
176unsigned int
177hb_ot_layout_table_get_script_tags (hb_face_t    *face,
178				    hb_tag_t      table_tag,
179				    unsigned int  start_offset,
180				    unsigned int *script_count /* IN/OUT */,
181				    hb_tag_t     *script_tags /* OUT */)
182{
183  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
184
185  return g.get_script_tags (start_offset, script_count, script_tags);
186}
187
188hb_bool_t
189hb_ot_layout_table_find_script (hb_face_t    *face,
190				hb_tag_t      table_tag,
191				hb_tag_t      script_tag,
192				unsigned int *script_index)
193{
194  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
195  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
196
197  if (g.find_script_index (script_tag, script_index))
198    return true;
199
200  /* try finding 'DFLT' */
201  if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
202    return false;
203
204  /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
205   * including many versions of DejaVu Sans Mono! */
206  if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
207    return false;
208
209  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
210  return false;
211}
212
213hb_bool_t
214hb_ot_layout_table_choose_script (hb_face_t      *face,
215				  hb_tag_t        table_tag,
216				  const hb_tag_t *script_tags,
217				  unsigned int   *script_index,
218				  hb_tag_t       *chosen_script)
219{
220  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
221  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
222
223  while (*script_tags)
224  {
225    if (g.find_script_index (*script_tags, script_index)) {
226      if (chosen_script)
227        *chosen_script = *script_tags;
228      return true;
229    }
230    script_tags++;
231  }
232
233  /* try finding 'DFLT' */
234  if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
235    if (chosen_script)
236      *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
237    return false;
238  }
239
240  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
241  if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
242    if (chosen_script)
243      *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
244    return false;
245  }
246
247  /* try with 'latn'; some old fonts put their features there even though
248     they're really trying to support Thai, for example :( */
249#define HB_OT_TAG_LATIN_SCRIPT		HB_TAG ('l', 'a', 't', 'n')
250  if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
251    if (chosen_script)
252      *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
253    return false;
254  }
255
256  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
257  if (chosen_script)
258    *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
259  return false;
260}
261
262unsigned int
263hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
264				     hb_tag_t      table_tag,
265				     unsigned int  start_offset,
266				     unsigned int *feature_count /* IN/OUT */,
267				     hb_tag_t     *feature_tags /* OUT */)
268{
269  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
270
271  return g.get_feature_tags (start_offset, feature_count, feature_tags);
272}
273
274
275unsigned int
276hb_ot_layout_script_get_language_tags (hb_face_t    *face,
277				       hb_tag_t      table_tag,
278				       unsigned int  script_index,
279				       unsigned int  start_offset,
280				       unsigned int *language_count /* IN/OUT */,
281				       hb_tag_t     *language_tags /* OUT */)
282{
283  const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
284
285  return s.get_lang_sys_tags (start_offset, language_count, language_tags);
286}
287
288hb_bool_t
289hb_ot_layout_script_find_language (hb_face_t    *face,
290				   hb_tag_t      table_tag,
291				   unsigned int  script_index,
292				   hb_tag_t      language_tag,
293				   unsigned int *language_index)
294{
295  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
296  const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
297
298  if (s.find_lang_sys_index (language_tag, language_index))
299    return true;
300
301  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
302  if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
303    return false;
304
305  if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
306  return false;
307}
308
309hb_bool_t
310hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
311						  hb_tag_t      table_tag,
312						  unsigned int  script_index,
313						  unsigned int  language_index,
314						  unsigned int *feature_index)
315{
316  const OT::LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index);
317
318  if (feature_index) *feature_index = l.get_required_feature_index ();
319
320  return l.has_required_feature ();
321}
322
323unsigned int
324hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
325					   hb_tag_t      table_tag,
326					   unsigned int  script_index,
327					   unsigned int  language_index,
328					   unsigned int  start_offset,
329					   unsigned int *feature_count /* IN/OUT */,
330					   unsigned int *feature_indexes /* OUT */)
331{
332  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
333  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
334
335  return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
336}
337
338unsigned int
339hb_ot_layout_language_get_feature_tags (hb_face_t    *face,
340					hb_tag_t      table_tag,
341					unsigned int  script_index,
342					unsigned int  language_index,
343					unsigned int  start_offset,
344					unsigned int *feature_count /* IN/OUT */,
345					hb_tag_t     *feature_tags /* OUT */)
346{
347  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
348  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
349
350  ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
351  unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
352
353  if (feature_tags) {
354    unsigned int count = *feature_count;
355    for (unsigned int i = 0; i < count; i++)
356      feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
357  }
358
359  return ret;
360}
361
362
363hb_bool_t
364hb_ot_layout_language_find_feature (hb_face_t    *face,
365				    hb_tag_t      table_tag,
366				    unsigned int  script_index,
367				    unsigned int  language_index,
368				    hb_tag_t      feature_tag,
369				    unsigned int *feature_index)
370{
371  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
372  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
373  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
374
375  unsigned int num_features = l.get_feature_count ();
376  for (unsigned int i = 0; i < num_features; i++) {
377    unsigned int f_index = l.get_feature_index (i);
378
379    if (feature_tag == g.get_feature_tag (f_index)) {
380      if (feature_index) *feature_index = f_index;
381      return true;
382    }
383  }
384
385  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
386  return false;
387}
388
389unsigned int
390hb_ot_layout_feature_get_lookups (hb_face_t    *face,
391				  hb_tag_t      table_tag,
392				  unsigned int  feature_index,
393				  unsigned int  start_offset,
394				  unsigned int *lookup_count /* IN/OUT */,
395				  unsigned int *lookup_indexes /* OUT */)
396{
397  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
398  const OT::Feature &f = g.get_feature (feature_index);
399
400  return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
401}
402
403static void
404_hb_ot_layout_collect_lookups_lookups (hb_face_t      *face,
405				       hb_tag_t        table_tag,
406				       unsigned int    feature_index,
407				       hb_set_t       *lookup_indexes /* OUT */)
408{
409  unsigned int lookup_indices[32];
410  unsigned int offset, len;
411
412  offset = 0;
413  do {
414    len = ARRAY_LENGTH (lookup_indices);
415    hb_ot_layout_feature_get_lookups (face,
416				      table_tag,
417				      feature_index,
418				      offset, &len,
419				      lookup_indices);
420
421    for (unsigned int i = 0; i < len; i++)
422      lookup_indexes->add (lookup_indices[i]);
423
424    offset += len;
425  } while (len == ARRAY_LENGTH (lookup_indices));
426}
427
428static void
429_hb_ot_layout_collect_lookups_features (hb_face_t      *face,
430					hb_tag_t        table_tag,
431					unsigned int    script_index,
432					unsigned int    language_index,
433					const hb_tag_t *features,
434					hb_set_t       *lookup_indexes /* OUT */)
435{
436  unsigned int required_feature_index;
437  if (hb_ot_layout_language_get_required_feature_index (face,
438							table_tag,
439							script_index,
440							language_index,
441							&required_feature_index))
442    _hb_ot_layout_collect_lookups_lookups (face,
443					   table_tag,
444					   required_feature_index,
445					   lookup_indexes);
446
447  if (!features)
448  {
449    /* All features */
450    unsigned int feature_indices[32];
451    unsigned int offset, len;
452
453    offset = 0;
454    do {
455      len = ARRAY_LENGTH (feature_indices);
456      hb_ot_layout_language_get_feature_indexes (face,
457						 table_tag,
458						 script_index,
459						 language_index,
460						 offset, &len,
461						 feature_indices);
462
463      for (unsigned int i = 0; i < len; i++)
464	_hb_ot_layout_collect_lookups_lookups (face,
465					       table_tag,
466					       feature_indices[i],
467					       lookup_indexes);
468
469      offset += len;
470    } while (len == ARRAY_LENGTH (feature_indices));
471  }
472  else
473  {
474    for (; *features; features++)
475    {
476      unsigned int feature_index;
477      if (hb_ot_layout_language_find_feature (face,
478					      table_tag,
479					      script_index,
480					      language_index,
481					      *features,
482					      &feature_index))
483        _hb_ot_layout_collect_lookups_lookups (face,
484					       table_tag,
485					       feature_index,
486					       lookup_indexes);
487    }
488  }
489}
490
491static void
492_hb_ot_layout_collect_lookups_languages (hb_face_t      *face,
493					 hb_tag_t        table_tag,
494					 unsigned int    script_index,
495					 const hb_tag_t *languages,
496					 const hb_tag_t *features,
497					 hb_set_t       *lookup_indexes /* OUT */)
498{
499  _hb_ot_layout_collect_lookups_features (face,
500					  table_tag,
501					  script_index,
502					  HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
503					  features,
504					  lookup_indexes);
505
506  if (!languages)
507  {
508    /* All languages */
509    unsigned int count = hb_ot_layout_script_get_language_tags (face,
510								table_tag,
511								script_index,
512								0, NULL, NULL);
513    for (unsigned int language_index = 0; language_index < count; language_index++)
514      _hb_ot_layout_collect_lookups_features (face,
515					      table_tag,
516					      script_index,
517					      language_index,
518					      features,
519					      lookup_indexes);
520  }
521  else
522  {
523    for (; *languages; languages++)
524    {
525      unsigned int language_index;
526      if (hb_ot_layout_script_find_language (face,
527					     table_tag,
528					     script_index,
529					     *languages,
530					     &language_index))
531        _hb_ot_layout_collect_lookups_features (face,
532						table_tag,
533						script_index,
534						language_index,
535						features,
536						lookup_indexes);
537    }
538  }
539}
540
541void
542hb_ot_layout_collect_lookups (hb_face_t      *face,
543			      hb_tag_t        table_tag,
544			      const hb_tag_t *scripts,
545			      const hb_tag_t *languages,
546			      const hb_tag_t *features,
547			      hb_set_t       *lookup_indexes /* OUT */)
548{
549  if (!scripts)
550  {
551    /* All scripts */
552    unsigned int count = hb_ot_layout_table_get_script_tags (face,
553							     table_tag,
554							     0, NULL, NULL);
555    for (unsigned int script_index = 0; script_index < count; script_index++)
556      _hb_ot_layout_collect_lookups_languages (face,
557					       table_tag,
558					       script_index,
559					       languages,
560					       features,
561					       lookup_indexes);
562  }
563  else
564  {
565    for (; *scripts; scripts++)
566    {
567      unsigned int script_index;
568      if (hb_ot_layout_table_find_script (face,
569					  table_tag,
570					  *scripts,
571					  &script_index))
572        _hb_ot_layout_collect_lookups_languages (face,
573						 table_tag,
574						 script_index,
575						 languages,
576						 features,
577						 lookup_indexes);
578    }
579  }
580}
581
582void
583hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
584				    hb_tag_t      table_tag,
585				    unsigned int  lookup_index,
586				    hb_set_t     *glyphs_before, /* OUT. May be NULL */
587				    hb_set_t     *glyphs_input,  /* OUT. May be NULL */
588				    hb_set_t     *glyphs_after,  /* OUT. May be NULL */
589				    hb_set_t     *glyphs_output  /* OUT. May be NULL */)
590{
591  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
592
593  OT::hb_collect_glyphs_context_t c (face,
594				     glyphs_before,
595				     glyphs_input,
596				     glyphs_after,
597				     glyphs_output);
598
599  switch (table_tag)
600  {
601    case HB_OT_TAG_GSUB:
602    {
603      const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
604      l.collect_glyphs_lookup (&c);
605      return;
606    }
607    case HB_OT_TAG_GPOS:
608    {
609      const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
610      l.collect_glyphs_lookup (&c);
611      return;
612    }
613  }
614}
615
616
617/*
618 * OT::GSUB
619 */
620
621hb_bool_t
622hb_ot_layout_has_substitution (hb_face_t *face)
623{
624  return &_get_gsub (face) != &OT::Null(OT::GSUB);
625}
626
627hb_bool_t
628hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
629				      unsigned int          lookup_index,
630				      const hb_codepoint_t *glyphs,
631				      unsigned int          glyphs_length,
632				      hb_bool_t             zero_context)
633{
634  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
635  return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
636}
637
638hb_bool_t
639hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
640					   unsigned int          lookup_index,
641					   const hb_codepoint_t *glyphs,
642					   unsigned int          glyphs_length,
643					   hb_bool_t             zero_context)
644{
645  if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
646  OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context);
647
648  const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
649
650  return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_digests[lookup_index]);
651}
652
653void
654hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
655{
656  OT::GSUB::substitute_start (font, buffer);
657}
658
659hb_bool_t
660hb_ot_layout_substitute_lookup (hb_font_t    *font,
661				hb_buffer_t  *buffer,
662				unsigned int  lookup_index,
663				hb_mask_t     mask)
664{
665  if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gsub_lookup_count)) return false;
666
667  OT::hb_apply_context_t c (font, buffer, mask);
668
669  const OT::SubstLookup& l = hb_ot_layout_from_face (font->face)->gsub->get_lookup (lookup_index);
670
671  return l.apply_string (&c, &hb_ot_layout_from_face (font->face)->gsub_digests[lookup_index]);
672}
673
674void
675hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
676{
677  OT::GSUB::substitute_finish (font, buffer);
678}
679
680void
681hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
682				        unsigned int  lookup_index,
683				        hb_set_t     *glyphs)
684{
685  OT::hb_closure_context_t c (face, glyphs);
686
687  const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);
688
689  l.closure (&c);
690}
691
692/*
693 * OT::GPOS
694 */
695
696hb_bool_t
697hb_ot_layout_has_positioning (hb_face_t *face)
698{
699  return &_get_gpos (face) != &OT::Null(OT::GPOS);
700}
701
702void
703hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
704{
705  OT::GPOS::position_start (font, buffer);
706}
707
708hb_bool_t
709hb_ot_layout_position_lookup (hb_font_t    *font,
710			      hb_buffer_t  *buffer,
711			      unsigned int  lookup_index,
712			      hb_mask_t     mask)
713{
714  if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gpos_lookup_count)) return false;
715
716  OT::hb_apply_context_t c (font, buffer, mask);
717
718  const OT::PosLookup& l = hb_ot_layout_from_face (font->face)->gpos->get_lookup (lookup_index);
719
720  return l.apply_string (&c, &hb_ot_layout_from_face (font->face)->gpos_digests[lookup_index]);
721}
722
723void
724hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer, hb_bool_t zero_width_attached_marks)
725{
726  OT::GPOS::position_finish (font, buffer, zero_width_attached_marks);
727}
728
729hb_bool_t
730hb_ot_layout_get_size_params (hb_face_t    *face,
731			      unsigned int *design_size,       /* OUT.  May be NULL */
732			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
733			      unsigned int *subfamily_name_id, /* OUT.  May be NULL */
734			      unsigned int *range_start,       /* OUT.  May be NULL */
735			      unsigned int *range_end          /* OUT.  May be NULL */)
736{
737  const OT::GPOS &gpos = _get_gpos (face);
738  const hb_tag_t tag = HB_TAG ('s','i','z','e');
739
740  unsigned int num_features = gpos.get_feature_count ();
741  for (unsigned int i = 0; i < num_features; i++)
742  {
743    if (tag == gpos.get_feature_tag (i))
744    {
745      const OT::Feature &f = gpos.get_feature (i);
746      const OT::FeatureParamsSize &params = f.get_feature_params ().get_size_params (tag);
747
748      if (params.designSize)
749      {
750#define PARAM(a, A) if (a) *a = params.A
751	PARAM (design_size, designSize);
752	PARAM (subfamily_id, subfamilyID);
753	PARAM (subfamily_name_id, subfamilyNameID);
754	PARAM (range_start, rangeStart);
755	PARAM (range_end, rangeEnd);
756#undef PARAM
757
758	return true;
759      }
760    }
761  }
762
763#define PARAM(a, A) if (a) *a = 0
764  PARAM (design_size, designSize);
765  PARAM (subfamily_id, subfamilyID);
766  PARAM (subfamily_name_id, subfamilyNameID);
767  PARAM (range_start, rangeStart);
768  PARAM (range_end, rangeEnd);
769#undef PARAM
770
771  return false;
772}
773