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