2 * Copyright (C) 2007,2008,2009 Red Hat, Inc.
4 * This is part of HarfBuzz, a text shaping library.
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 * Red Hat Author(s): Behdad Esfahbod
27 #ifndef HB_OT_LAYOUT_GSUB_PRIVATE_HH
28 #define HB_OT_LAYOUT_GSUB_PRIVATE_HH
30 #include "hb-ot-layout-gsubgpos-private.hh"
34 #define BUFFER context->buffer
37 struct SingleSubstFormat1
39 friend struct SingleSubst;
43 inline bool apply (APPLY_ARG_DEF) const
46 hb_codepoint_t glyph_id = IN_CURGLYPH ();
47 unsigned int index = (this+coverage) (glyph_id);
48 if (likely (index == NOT_COVERED))
51 glyph_id += deltaGlyphID;
52 _hb_buffer_replace_glyph (context->buffer, glyph_id);
54 /* We inherit the old glyph class to the substituted glyph */
55 if (_hb_ot_layout_has_new_glyph_classes (context->layout->face))
56 _hb_ot_layout_set_glyph_property (context->layout->face, glyph_id, context->property);
61 inline bool sanitize (hb_sanitize_context_t *context) {
63 return SANITIZE_WITH_BASE (this, coverage)
64 && SANITIZE (deltaGlyphID);
68 USHORT format; /* Format identifier--format = 1 */
70 coverage; /* Offset to Coverage table--from
71 * beginning of Substitution table */
72 SHORT deltaGlyphID; /* Add to original GlyphID to get
73 * substitute GlyphID */
75 ASSERT_SIZE (SingleSubstFormat1, 6);
77 struct SingleSubstFormat2
79 friend struct SingleSubst;
83 inline bool apply (APPLY_ARG_DEF) const
86 hb_codepoint_t glyph_id = IN_CURGLYPH ();
87 unsigned int index = (this+coverage) (glyph_id);
88 if (likely (index == NOT_COVERED))
91 if (unlikely (index >= substitute.len))
94 glyph_id = substitute[index];
95 _hb_buffer_replace_glyph (context->buffer, glyph_id);
97 /* We inherit the old glyph class to the substituted glyph */
98 if (_hb_ot_layout_has_new_glyph_classes (context->layout->face))
99 _hb_ot_layout_set_glyph_property (context->layout->face, glyph_id, context->property);
104 inline bool sanitize (hb_sanitize_context_t *context) {
106 return SANITIZE_WITH_BASE (this, coverage)
107 && SANITIZE (substitute);
111 USHORT format; /* Format identifier--format = 2 */
113 coverage; /* Offset to Coverage table--from
114 * beginning of Substitution table */
116 substitute; /* Array of substitute
117 * GlyphIDs--ordered by Coverage Index */
119 ASSERT_SIZE (SingleSubstFormat2, 6);
123 friend struct SubstLookupSubTable;
127 inline bool apply (APPLY_ARG_DEF) const
131 case 1: return u.format1->apply (APPLY_ARG);
132 case 2: return u.format2->apply (APPLY_ARG);
133 default:return false;
137 inline bool sanitize (hb_sanitize_context_t *context) {
139 if (!SANITIZE (u.format)) return false;
141 case 1: return u.format1->sanitize (context);
142 case 2: return u.format2->sanitize (context);
149 USHORT format; /* Format identifier */
150 SingleSubstFormat1 format1[VAR];
151 SingleSubstFormat2 format2[VAR];
158 friend struct MultipleSubstFormat1;
161 inline bool apply (APPLY_ARG_DEF) const
164 if (unlikely (!substitute.len))
167 _hb_buffer_add_output_glyphs_be16 (context->buffer, 1,
168 substitute.len, (const uint16_t *) substitute.array(),
171 /* This is a guess only ... */
172 if (_hb_ot_layout_has_new_glyph_classes (context->layout->face))
174 unsigned int property = context->property;
175 if (property == HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)
176 property = HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
178 unsigned int count = substitute.len;
179 for (unsigned int n = 0; n < count; n++)
180 _hb_ot_layout_set_glyph_property (context->layout->face, substitute[n], property);
187 inline bool sanitize (hb_sanitize_context_t *context) {
189 return SANITIZE (substitute);
194 substitute; /* String of GlyphIDs to substitute */
196 ASSERT_SIZE (Sequence, 2);
198 struct MultipleSubstFormat1
200 friend struct MultipleSubst;
204 inline bool apply (APPLY_ARG_DEF) const
208 unsigned int index = (this+coverage) (IN_CURGLYPH ());
209 if (likely (index == NOT_COVERED))
212 return (this+sequence[index]).apply (APPLY_ARG);
215 inline bool sanitize (hb_sanitize_context_t *context) {
217 return SANITIZE_WITH_BASE (this, coverage)
218 && SANITIZE_WITH_BASE (this, sequence);
222 USHORT format; /* Format identifier--format = 1 */
224 coverage; /* Offset to Coverage table--from
225 * beginning of Substitution table */
226 OffsetArrayOf<Sequence>
227 sequence; /* Array of Sequence tables
228 * ordered by Coverage Index */
230 ASSERT_SIZE (MultipleSubstFormat1, 6);
234 friend struct SubstLookupSubTable;
238 inline bool apply (APPLY_ARG_DEF) const
242 case 1: return u.format1->apply (APPLY_ARG);
243 default:return false;
247 inline bool sanitize (hb_sanitize_context_t *context) {
249 if (!SANITIZE (u.format)) return false;
251 case 1: return u.format1->sanitize (context);
258 USHORT format; /* Format identifier */
259 MultipleSubstFormat1 format1[VAR];
264 typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in
266 ASSERT_SIZE (AlternateSet, 2);
268 struct AlternateSubstFormat1
270 friend struct AlternateSubst;
274 inline bool apply (APPLY_ARG_DEF) const
277 hb_codepoint_t glyph_id = IN_CURGLYPH ();
279 unsigned int index = (this+coverage) (glyph_id);
280 if (likely (index == NOT_COVERED))
283 const AlternateSet &alt_set = this+alternateSet[index];
285 if (unlikely (!alt_set.len))
288 unsigned int alt_index = 0;
290 /* XXX callback to user to choose alternate
291 if (context->layout->face->altfunc)
292 alt_index = (context->layout->face->altfunc)(context->layout->layout, context->buffer,
293 context->buffer->out_pos, glyph_id,
294 alt_set.len, alt_set.array);
297 if (unlikely (alt_index >= alt_set.len))
300 glyph_id = alt_set[alt_index];
302 _hb_buffer_replace_glyph (context->buffer, glyph_id);
304 /* We inherit the old glyph class to the substituted glyph */
305 if (_hb_ot_layout_has_new_glyph_classes (context->layout->face))
306 _hb_ot_layout_set_glyph_property (context->layout->face, glyph_id, context->property);
311 inline bool sanitize (hb_sanitize_context_t *context) {
313 return SANITIZE_WITH_BASE (this, coverage)
314 && SANITIZE_WITH_BASE (this, alternateSet);
318 USHORT format; /* Format identifier--format = 1 */
320 coverage; /* Offset to Coverage table--from
321 * beginning of Substitution table */
322 OffsetArrayOf<AlternateSet>
323 alternateSet; /* Array of AlternateSet tables
324 * ordered by Coverage Index */
326 ASSERT_SIZE (AlternateSubstFormat1, 6);
328 struct AlternateSubst
330 friend struct SubstLookupSubTable;
334 inline bool apply (APPLY_ARG_DEF) const
338 case 1: return u.format1->apply (APPLY_ARG);
339 default:return false;
343 inline bool sanitize (hb_sanitize_context_t *context) {
345 if (!SANITIZE (u.format)) return false;
347 case 1: return u.format1->sanitize (context);
354 USHORT format; /* Format identifier */
355 AlternateSubstFormat1 format1[VAR];
362 friend struct LigatureSet;
365 inline bool apply (APPLY_ARG_DEF, bool is_mark) const
369 unsigned int count = component.len;
370 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context_length);
371 if (unlikely (context->buffer->in_pos + count > end))
374 for (i = 1, j = context->buffer->in_pos + 1; i < count; i++, j++)
376 unsigned int property;
377 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, &property))
379 if (unlikely (j + count - i == end))
384 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
387 if (likely (IN_GLYPH (j) != component[i]))
390 /* This is just a guess ... */
391 if (_hb_ot_layout_has_new_glyph_classes (context->layout->face))
392 _hb_ot_layout_set_glyph_class (context->layout->face, ligGlyph,
393 is_mark ? HB_OT_LAYOUT_GLYPH_CLASS_MARK
394 : HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);
396 if (j == context->buffer->in_pos + i) /* No input glyphs skipped */
397 /* We don't use a new ligature ID if there are no skipped
398 glyphs and the ligature already has an ID. */
399 _hb_buffer_add_output_glyphs_be16 (context->buffer, i,
400 1, (const uint16_t *) &ligGlyph,
402 IN_LIGID (context->buffer->in_pos) && !IN_COMPONENT (context->buffer->in_pos) ?
403 0xFFFF : _hb_buffer_allocate_lig_id (context->buffer));
406 unsigned int lig_id = _hb_buffer_allocate_lig_id (context->buffer);
407 _hb_buffer_add_output_glyph (context->buffer, ligGlyph, 0xFFFF, lig_id);
409 /* Now we must do a second loop to copy the skipped glyphs to
410 `out' and assign component values to it. We start with the
411 glyph after the first component. Glyphs between component
412 i and i+1 belong to component i. Together with the lig_id
413 value it is later possible to check whether a specific
414 component value really belongs to a given ligature. */
416 for ( i = 1; i < count; i++ )
418 while (_hb_ot_layout_skip_mark (context->layout->face, IN_CURINFO (), context->lookup_flag, NULL))
419 _hb_buffer_add_output_glyph (context->buffer, IN_CURGLYPH (), i, lig_id);
421 (context->buffer->in_pos)++;
429 inline bool sanitize (hb_sanitize_context_t *context) {
431 return SANITIZE (ligGlyph) && SANITIZE (component);
435 GlyphID ligGlyph; /* GlyphID of ligature to substitute */
436 HeadlessArrayOf<GlyphID>
437 component; /* Array of component GlyphIDs--start
438 * with the second component--ordered
439 * in writing direction */
441 ASSERT_SIZE (Ligature, 4);
445 friend struct LigatureSubstFormat1;
448 inline bool apply (APPLY_ARG_DEF, bool is_mark) const
451 unsigned int num_ligs = ligature.len;
452 for (unsigned int i = 0; i < num_ligs; i++)
454 const Ligature &lig = this+ligature[i];
455 if (lig.apply (APPLY_ARG, is_mark))
463 inline bool sanitize (hb_sanitize_context_t *context) {
465 return SANITIZE_WITH_BASE (this, ligature);
469 OffsetArrayOf<Ligature>
470 ligature; /* Array LigatureSet tables
471 * ordered by preference */
473 ASSERT_SIZE (LigatureSet, 2);
475 struct LigatureSubstFormat1
477 friend struct LigatureSubst;
480 inline bool apply (APPLY_ARG_DEF) const
483 hb_codepoint_t glyph_id = IN_CURGLYPH ();
485 bool first_is_mark = !!(context->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
487 unsigned int index = (this+coverage) (glyph_id);
488 if (likely (index == NOT_COVERED))
491 const LigatureSet &lig_set = this+ligatureSet[index];
492 return lig_set.apply (APPLY_ARG, first_is_mark);
495 inline bool sanitize (hb_sanitize_context_t *context) {
497 return SANITIZE_WITH_BASE (this, coverage)
498 && SANITIZE_WITH_BASE (this, ligatureSet);
502 USHORT format; /* Format identifier--format = 1 */
504 coverage; /* Offset to Coverage table--from
505 * beginning of Substitution table */
506 OffsetArrayOf<LigatureSet>
507 ligatureSet; /* Array LigatureSet tables
508 * ordered by Coverage Index */
510 ASSERT_SIZE (LigatureSubstFormat1, 6);
514 friend struct SubstLookupSubTable;
517 inline bool apply (APPLY_ARG_DEF) const
521 case 1: return u.format1->apply (APPLY_ARG);
522 default:return false;
526 inline bool sanitize (hb_sanitize_context_t *context) {
528 if (!SANITIZE (u.format)) return false;
530 case 1: return u.format1->sanitize (context);
537 USHORT format; /* Format identifier */
538 LigatureSubstFormat1 format1[VAR];
544 static inline bool substitute_lookup (APPLY_ARG_DEF, unsigned int lookup_index);
546 struct ContextSubst : Context
548 friend struct SubstLookupSubTable;
551 inline bool apply (APPLY_ARG_DEF) const
554 return Context::apply (APPLY_ARG, substitute_lookup);
558 struct ChainContextSubst : ChainContext
560 friend struct SubstLookupSubTable;
563 inline bool apply (APPLY_ARG_DEF) const
566 return ChainContext::apply (APPLY_ARG, substitute_lookup);
571 struct ExtensionSubst : Extension
573 friend struct SubstLookupSubTable;
574 friend struct SubstLookup;
577 inline const struct SubstLookupSubTable& get_subtable (void) const
579 unsigned int offset = get_offset ();
580 if (unlikely (!offset)) return Null(SubstLookupSubTable);
581 return StructAtOffset<SubstLookupSubTable> (*this, offset);
584 inline bool apply (APPLY_ARG_DEF) const;
586 inline bool sanitize (hb_sanitize_context_t *context);
588 inline bool is_reverse (void) const;
592 struct ReverseChainSingleSubstFormat1
594 friend struct ReverseChainSingleSubst;
597 inline bool apply (APPLY_ARG_DEF) const
600 if (unlikely (context_length != NO_CONTEXT))
601 return false; /* No chaining to this type */
603 unsigned int index = (this+coverage) (IN_CURGLYPH ());
604 if (likely (index == NOT_COVERED))
607 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
608 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
610 if (match_backtrack (APPLY_ARG,
611 backtrack.len, (USHORT *) backtrack.array(),
612 match_coverage, CharP(this)) &&
613 match_lookahead (APPLY_ARG,
614 lookahead.len, (USHORT *) lookahead.array(),
615 match_coverage, CharP(this),
618 IN_CURGLYPH () = substitute[index];
619 context->buffer->in_pos--; /* Reverse! */
626 inline bool sanitize (hb_sanitize_context_t *context) {
628 if (!(SANITIZE_WITH_BASE (this, coverage)
629 && SANITIZE_WITH_BASE (this, backtrack)))
631 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
632 if (!SANITIZE_WITH_BASE (this, lookahead))
634 ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
635 return SANITIZE (substitute);
639 USHORT format; /* Format identifier--format = 1 */
641 coverage; /* Offset to Coverage table--from
642 * beginning of table */
643 OffsetArrayOf<Coverage>
644 backtrack; /* Array of coverage tables
645 * in backtracking sequence, in glyph
647 OffsetArrayOf<Coverage>
648 lookaheadX; /* Array of coverage tables
649 * in lookahead sequence, in glyph
652 substituteX; /* Array of substitute
653 * GlyphIDs--ordered by Coverage Index */
655 ASSERT_SIZE (ReverseChainSingleSubstFormat1, 10);
657 struct ReverseChainSingleSubst
659 friend struct SubstLookupSubTable;
662 inline bool apply (APPLY_ARG_DEF) const
666 case 1: return u.format1->apply (APPLY_ARG);
667 default:return false;
671 inline bool sanitize (hb_sanitize_context_t *context) {
673 if (!SANITIZE (u.format)) return false;
675 case 1: return u.format1->sanitize (context);
682 USHORT format; /* Format identifier */
683 ReverseChainSingleSubstFormat1 format1[VAR];
693 struct SubstLookupSubTable
695 friend struct SubstLookup;
705 ReverseChainSingle = 8
708 inline bool apply (APPLY_ARG_DEF, unsigned int lookup_type) const
711 switch (lookup_type) {
712 case Single: return u.single->apply (APPLY_ARG);
713 case Multiple: return u.multiple->apply (APPLY_ARG);
714 case Alternate: return u.alternate->apply (APPLY_ARG);
715 case Ligature: return u.ligature->apply (APPLY_ARG);
716 case Context: return u.context->apply (APPLY_ARG);
717 case ChainContext: return u.chainContext->apply (APPLY_ARG);
718 case Extension: return u.extension->apply (APPLY_ARG);
719 case ReverseChainSingle: return u.reverseChainContextSingle->apply (APPLY_ARG);
720 default:return false;
724 inline bool sanitize (hb_sanitize_context_t *context) {
726 if (!SANITIZE (u.format)) return false;
728 case Single: return u.single->sanitize (context);
729 case Multiple: return u.multiple->sanitize (context);
730 case Alternate: return u.alternate->sanitize (context);
731 case Ligature: return u.ligature->sanitize (context);
732 case Context: return u.context->sanitize (context);
733 case ChainContext: return u.chainContext->sanitize (context);
734 case Extension: return u.extension->sanitize (context);
735 case ReverseChainSingle: return u.reverseChainContextSingle->sanitize (context);
743 SingleSubst single[VAR];
744 MultipleSubst multiple[VAR];
745 AlternateSubst alternate[VAR];
746 LigatureSubst ligature[VAR];
747 ContextSubst context[VAR];
748 ChainContextSubst chainContext[VAR];
749 ExtensionSubst extension[VAR];
750 ReverseChainSingleSubst reverseChainContextSingle[VAR];
755 struct SubstLookup : Lookup
757 inline const SubstLookupSubTable& get_subtable (unsigned int i) const
758 { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
760 inline static bool lookup_type_is_reverse (unsigned int lookup_type)
761 { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
763 inline bool is_reverse (void) const
765 unsigned int type = get_type ();
766 if (unlikely (type == SubstLookupSubTable::Extension))
767 return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
768 return lookup_type_is_reverse (type);
772 inline bool apply_once (hb_ot_layout_context_t *layout,
774 unsigned int context_length,
775 unsigned int nesting_level_left,
776 unsigned int apply_depth) const
778 unsigned int lookup_type = get_type ();
779 hb_apply_context_t context[1];
781 context->layout = layout;
782 context->buffer = buffer;
783 context->nesting_level_left = nesting_level_left;
784 context->lookup_flag = get_flag ();
786 if (!_hb_ot_layout_check_glyph_property (context->layout->face, IN_CURINFO (), context->lookup_flag, &context->property))
789 if (unlikely (lookup_type == SubstLookupSubTable::Extension))
791 /* The spec says all subtables should have the same type.
792 * This is specially important if one has a reverse type!
794 * This is rather slow to do this here for every glyph,
795 * but it's easiest, and who uses extension lookups anyway?!*/
796 unsigned int count = get_subtable_count ();
797 unsigned int type = get_subtable(0).u.extension->get_type ();
798 for (unsigned int i = 1; i < count; i++)
799 if (get_subtable(i).u.extension->get_type () != type)
803 unsigned int count = get_subtable_count ();
804 for (unsigned int i = 0; i < count; i++)
805 if (get_subtable (i).apply (APPLY_ARG, lookup_type))
811 inline bool apply_string (hb_ot_layout_context_t *layout,
813 hb_mask_t mask) const
816 #define BUFFER buffer
819 if (unlikely (!buffer->in_length))
822 if (likely (!is_reverse ()))
824 /* in/out forward substitution */
825 _hb_buffer_clear_output (buffer);
827 while (buffer->in_pos < buffer->in_length)
829 if ((~IN_MASK (buffer->in_pos) & mask) &&
830 apply_once (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL, 0))
833 _hb_buffer_next_glyph (buffer);
837 _hb_buffer_swap (buffer);
841 /* in-place backward substitution */
842 buffer->in_pos = buffer->in_length - 1;
845 if ((~IN_MASK (buffer->in_pos) & mask) &&
846 apply_once (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL, 0))
852 while ((int) buffer->in_pos >= 0);
858 inline bool sanitize (hb_sanitize_context_t *context) {
860 if (unlikely (!Lookup::sanitize (context))) return false;
861 OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
862 return SANITIZE_WITH_BASE (this, list);
866 typedef OffsetListOf<SubstLookup> SubstLookupList;
867 ASSERT_SIZE (SubstLookupList, 2);
873 struct GSUB : GSUBGPOS
875 static const hb_tag_t Tag = HB_OT_TAG_GSUB;
877 inline const SubstLookup& get_lookup (unsigned int i) const
878 { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
880 inline bool substitute_lookup (hb_ot_layout_context_t *layout,
882 unsigned int lookup_index,
883 hb_mask_t mask) const
884 { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
887 inline bool sanitize (hb_sanitize_context_t *context) {
889 if (unlikely (!GSUBGPOS::sanitize (context))) return false;
890 OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
891 return SANITIZE_WITH_BASE (this, list);
894 ASSERT_SIZE (GSUB, 10);
897 /* Out-of-class implementation for methods recursing */
899 inline bool ExtensionSubst::apply (APPLY_ARG_DEF) const
902 return get_subtable ().apply (APPLY_ARG, get_type ());
905 inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *context)
908 if (unlikely (!Extension::sanitize (context))) return false;
909 unsigned int offset = get_offset ();
910 if (unlikely (!offset)) return true;
911 return SANITIZE (StructAtOffset<SubstLookupSubTable> (*this, offset));
914 inline bool ExtensionSubst::is_reverse (void) const
916 unsigned int type = get_type ();
917 if (unlikely (type == SubstLookupSubTable::Extension))
918 return CastR<ExtensionSubst> (get_subtable()).is_reverse ();
919 return SubstLookup::lookup_type_is_reverse (type);
922 static inline bool substitute_lookup (APPLY_ARG_DEF, unsigned int lookup_index)
924 const GSUB &gsub = *(context->layout->face->ot_layout.gsub);
925 const SubstLookup &l = gsub.get_lookup (lookup_index);
927 if (unlikely (context->nesting_level_left == 0))
930 if (unlikely (context_length < 1))
933 return l.apply_once (context->layout, context->buffer, context_length, context->nesting_level_left - 1, apply_depth + 1);
937 #endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */