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