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