hb-ot-layout.cc revision efe252e6000558f78075adadb2a3dba25ab67c04
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  if (!features)
437  {
438    /* All features */
439    unsigned int count = hb_ot_layout_language_get_feature_tags (face, table_tag, script_index, language_index, 0, NULL, NULL);
440    for (unsigned int feature_index = 0; feature_index < count; feature_index++)
441      _hb_ot_layout_collect_lookups_lookups (face, table_tag, feature_index, lookup_indexes);
442  } else {
443    for (; *features; features++)
444    {
445      unsigned int feature_index;
446      if (hb_ot_layout_language_find_feature (face, table_tag, script_index, language_index, *features, &feature_index))
447        _hb_ot_layout_collect_lookups_lookups (face, table_tag, feature_index, lookup_indexes);
448    }
449  }
450}
451
452static void
453_hb_ot_layout_collect_lookups_languages (hb_face_t      *face,
454					 hb_tag_t        table_tag,
455					 unsigned int    script_index,
456					 const hb_tag_t *languages,
457					 const hb_tag_t *features,
458					 hb_set_t       *lookup_indexes /* OUT */)
459{
460  if (!languages)
461  {
462    /* All languages */
463    unsigned int count = hb_ot_layout_script_get_language_tags (face, table_tag, script_index, 0, NULL, NULL);
464    for (unsigned int language_index = 0; language_index < count; language_index++)
465      _hb_ot_layout_collect_lookups_features (face, table_tag, script_index, language_index, features, lookup_indexes);
466  } else {
467    for (; *languages; languages++)
468    {
469      unsigned int language_index;
470      if (hb_ot_layout_script_find_language (face, table_tag, script_index, *languages, &language_index))
471        _hb_ot_layout_collect_lookups_features (face, table_tag, script_index, language_index, features, lookup_indexes);
472    }
473  }
474}
475
476void
477hb_ot_layout_collect_lookups (hb_face_t      *face,
478			      hb_tag_t        table_tag,
479			      const hb_tag_t *scripts,
480			      const hb_tag_t *languages,
481			      const hb_tag_t *features,
482			      hb_set_t       *lookup_indexes /* OUT */)
483{
484  if (!scripts)
485  {
486    /* All scripts */
487    unsigned int count = hb_ot_layout_table_get_script_tags (face, table_tag, 0, NULL, NULL);
488    for (unsigned int script_index = 0; script_index < count; script_index++)
489      _hb_ot_layout_collect_lookups_languages (face, table_tag, script_index, languages, features, lookup_indexes);
490  } else {
491    for (; *scripts; scripts++)
492    {
493      unsigned int script_index;
494      if (hb_ot_layout_table_find_script (face, table_tag, *scripts, &script_index))
495        _hb_ot_layout_collect_lookups_languages (face, table_tag, script_index, languages, features, lookup_indexes);
496    }
497  }
498}
499
500void
501hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
502				    hb_tag_t      table_tag,
503				    unsigned int  lookup_index,
504				    hb_set_t     *glyphs_before, /* OUT. May be NULL */
505				    hb_set_t     *glyphs_input,  /* OUT. May be NULL */
506				    hb_set_t     *glyphs_after,  /* OUT. May be NULL */
507				    hb_set_t     *glyphs_output  /* OUT. May be NULL */)
508{
509  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
510
511  OT::hb_collect_glyphs_context_t c (face, glyphs_before, glyphs_input, glyphs_after, glyphs_output);
512
513  switch (table_tag) {
514    case HB_OT_TAG_GSUB:
515    {
516      const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
517      l.collect_glyphs_lookup (&c);
518      return;
519    }
520    case HB_OT_TAG_GPOS:
521    {
522      const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
523      l.collect_glyphs_lookup (&c);
524      return;
525    }
526  }
527}
528
529
530/*
531 * OT::GSUB
532 */
533
534hb_bool_t
535hb_ot_layout_has_substitution (hb_face_t *face)
536{
537  return &_get_gsub (face) != &OT::Null(OT::GSUB);
538}
539
540hb_bool_t
541hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
542				      unsigned int          lookup_index,
543				      const hb_codepoint_t *glyphs,
544				      unsigned int          glyphs_length,
545				      hb_bool_t             zero_context)
546{
547  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
548  return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
549}
550
551hb_bool_t
552hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
553					   unsigned int          lookup_index,
554					   const hb_codepoint_t *glyphs,
555					   unsigned int          glyphs_length,
556					   hb_bool_t             zero_context)
557{
558  if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
559  OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context);
560
561  const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
562
563  return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_digests[lookup_index]);
564}
565
566void
567hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
568{
569  OT::GSUB::substitute_start (font, buffer);
570}
571
572hb_bool_t
573hb_ot_layout_substitute_lookup (hb_font_t    *font,
574				hb_buffer_t  *buffer,
575				unsigned int  lookup_index,
576				hb_mask_t     mask)
577{
578  if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gsub_lookup_count)) return false;
579
580  OT::hb_apply_context_t c (font, buffer, mask);
581
582  const OT::SubstLookup& l = hb_ot_layout_from_face (font->face)->gsub->get_lookup (lookup_index);
583
584  return l.apply_string (&c, &hb_ot_layout_from_face (font->face)->gsub_digests[lookup_index]);
585}
586
587void
588hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
589{
590  OT::GSUB::substitute_finish (font, buffer);
591}
592
593void
594hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
595				        unsigned int  lookup_index,
596				        hb_set_t     *glyphs)
597{
598  OT::hb_closure_context_t c (face, glyphs);
599
600  const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);
601
602  l.closure (&c);
603}
604
605/*
606 * OT::GPOS
607 */
608
609hb_bool_t
610hb_ot_layout_has_positioning (hb_face_t *face)
611{
612  return &_get_gpos (face) != &OT::Null(OT::GPOS);
613}
614
615void
616hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
617{
618  OT::GPOS::position_start (font, buffer);
619}
620
621hb_bool_t
622hb_ot_layout_position_lookup (hb_font_t    *font,
623			      hb_buffer_t  *buffer,
624			      unsigned int  lookup_index,
625			      hb_mask_t     mask)
626{
627  if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gpos_lookup_count)) return false;
628
629  OT::hb_apply_context_t c (font, buffer, mask);
630
631  const OT::PosLookup& l = hb_ot_layout_from_face (font->face)->gpos->get_lookup (lookup_index);
632
633  return l.apply_string (&c, &hb_ot_layout_from_face (font->face)->gpos_digests[lookup_index]);
634}
635
636void
637hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer, hb_bool_t zero_width_attached_marks)
638{
639  OT::GPOS::position_finish (font, buffer, zero_width_attached_marks);
640}
641
642hb_bool_t
643hb_ot_layout_get_size_params (hb_face_t    *face,
644			      unsigned int *design_size,       /* OUT.  May be NULL */
645			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
646			      unsigned int *subfamily_name_id, /* OUT.  May be NULL */
647			      unsigned int *range_start,       /* OUT.  May be NULL */
648			      unsigned int *range_end          /* OUT.  May be NULL */)
649{
650  const OT::GPOS &gpos = _get_gpos (face);
651  const hb_tag_t tag = HB_TAG ('s','i','z','e');
652
653  unsigned int num_features = gpos.get_feature_count ();
654  for (unsigned int i = 0; i < num_features; i++)
655  {
656    if (tag == gpos.get_feature_tag (i))
657    {
658      const OT::Feature &f = gpos.get_feature (i);
659      const OT::FeatureParamsSize &params = f.get_feature_params ().get_size_params (tag);
660
661      if (params.designSize)
662      {
663#define PARAM(a, A) if (a) *a = params.A
664	PARAM (design_size, designSize);
665	PARAM (subfamily_id, subfamilyID);
666	PARAM (subfamily_name_id, subfamilyNameID);
667	PARAM (range_start, rangeStart);
668	PARAM (range_end, rangeEnd);
669#undef PARAM
670
671	return true;
672      }
673    }
674  }
675
676#define PARAM(a, A) if (a) *a = 0
677  PARAM (design_size, designSize);
678  PARAM (subfamily_id, subfamilyID);
679  PARAM (subfamily_name_id, subfamilyNameID);
680  PARAM (range_start, rangeStart);
681  PARAM (range_end, rangeEnd);
682#undef PARAM
683
684  return false;
685}
686