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