2 * Copyright © 2007,2008,2009,2010 Red Hat, Inc.
3 * Copyright © 2010,2012 Google, Inc.
5 * This is part of HarfBuzz, a text shaping library.
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
29 #ifndef HB_OT_LAYOUT_GSUBGPOS_HH
30 #define HB_OT_LAYOUT_GSUBGPOS_HH
33 #include "hb-buffer.hh"
36 #include "hb-ot-map.hh"
37 #include "hb-ot-layout-common.hh"
38 #include "hb-ot-layout-gdef-table.hh"
44 struct hb_intersects_context_t :
45 hb_dispatch_context_t<hb_intersects_context_t, bool, 0>
47 const char *get_name () { return "INTERSECTS"; }
49 return_t dispatch (const T &obj) { return obj.intersects (this->glyphs); }
50 static return_t default_return_value () { return false; }
51 bool stop_sublookup_iteration (return_t r) const { return r; }
53 const hb_set_t *glyphs;
54 unsigned int debug_depth;
56 hb_intersects_context_t (const hb_set_t *glyphs_) :
61 struct hb_closure_context_t :
62 hb_dispatch_context_t<hb_closure_context_t, hb_empty_t, 0>
64 const char *get_name () { return "CLOSURE"; }
65 typedef return_t (*recurse_func_t) (hb_closure_context_t *c, unsigned int lookup_index);
67 return_t dispatch (const T &obj) { obj.closure (this); return hb_empty_t (); }
68 static return_t default_return_value () { return hb_empty_t (); }
69 void recurse (unsigned int lookup_index)
71 if (unlikely (nesting_level_left == 0 || !recurse_func))
75 recurse_func (this, lookup_index);
79 bool should_visit_lookup (unsigned int lookup_index)
81 if (is_lookup_done (lookup_index))
83 done_lookups->set (lookup_index, glyphs->get_population ());
87 bool is_lookup_done (unsigned int lookup_index)
89 /* Have we visited this lookup with the current set of glyphs? */
90 return done_lookups->get (lookup_index) == glyphs->get_population ();
96 recurse_func_t recurse_func;
97 unsigned int nesting_level_left;
98 unsigned int debug_depth;
100 hb_closure_context_t (hb_face_t *face_,
102 hb_map_t *done_lookups_,
103 unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
106 recurse_func (nullptr),
107 nesting_level_left (nesting_level_left_),
109 done_lookups (done_lookups_) {}
111 ~hb_closure_context_t () { flush (); }
113 void set_recurse_func (recurse_func_t func) { recurse_func = func; }
117 hb_set_union (glyphs, output);
118 hb_set_clear (output);
122 hb_map_t *done_lookups;
126 struct hb_would_apply_context_t :
127 hb_dispatch_context_t<hb_would_apply_context_t, bool, 0>
129 const char *get_name () { return "WOULD_APPLY"; }
130 template <typename T>
131 return_t dispatch (const T &obj) { return obj.would_apply (this); }
132 static return_t default_return_value () { return false; }
133 bool stop_sublookup_iteration (return_t r) const { return r; }
136 const hb_codepoint_t *glyphs;
139 unsigned int debug_depth;
141 hb_would_apply_context_t (hb_face_t *face_,
142 const hb_codepoint_t *glyphs_,
144 bool zero_context_) :
148 zero_context (zero_context_),
153 struct hb_collect_glyphs_context_t :
154 hb_dispatch_context_t<hb_collect_glyphs_context_t, hb_empty_t, 0>
156 const char *get_name () { return "COLLECT_GLYPHS"; }
157 typedef return_t (*recurse_func_t) (hb_collect_glyphs_context_t *c, unsigned int lookup_index);
158 template <typename T>
159 return_t dispatch (const T &obj) { obj.collect_glyphs (this); return hb_empty_t (); }
160 static return_t default_return_value () { return hb_empty_t (); }
161 void recurse (unsigned int lookup_index)
163 if (unlikely (nesting_level_left == 0 || !recurse_func))
166 /* Note that GPOS sets recurse_func to nullptr already, so it doesn't get
167 * past the previous check. For GSUB, we only want to collect the output
168 * glyphs in the recursion. If output is not requested, we can go home now.
170 * Note further, that the above is not exactly correct. A recursed lookup
171 * is allowed to match input that is not matched in the context, but that's
172 * not how most fonts are built. It's possible to relax that and recurse
173 * with all sets here if it proves to be an issue.
176 if (output == hb_set_get_empty ())
179 /* Return if new lookup was recursed to before. */
180 if (recursed_lookups->has (lookup_index))
183 hb_set_t *old_before = before;
184 hb_set_t *old_input = input;
185 hb_set_t *old_after = after;
186 before = input = after = hb_set_get_empty ();
188 nesting_level_left--;
189 recurse_func (this, lookup_index);
190 nesting_level_left++;
196 recursed_lookups->add (lookup_index);
204 recurse_func_t recurse_func;
205 hb_set_t *recursed_lookups;
206 unsigned int nesting_level_left;
207 unsigned int debug_depth;
209 hb_collect_glyphs_context_t (hb_face_t *face_,
210 hb_set_t *glyphs_before, /* OUT. May be NULL */
211 hb_set_t *glyphs_input, /* OUT. May be NULL */
212 hb_set_t *glyphs_after, /* OUT. May be NULL */
213 hb_set_t *glyphs_output, /* OUT. May be NULL */
214 unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
216 before (glyphs_before ? glyphs_before : hb_set_get_empty ()),
217 input (glyphs_input ? glyphs_input : hb_set_get_empty ()),
218 after (glyphs_after ? glyphs_after : hb_set_get_empty ()),
219 output (glyphs_output ? glyphs_output : hb_set_get_empty ()),
220 recurse_func (nullptr),
221 recursed_lookups (hb_set_create ()),
222 nesting_level_left (nesting_level_left_),
224 ~hb_collect_glyphs_context_t () { hb_set_destroy (recursed_lookups); }
226 void set_recurse_func (recurse_func_t func) { recurse_func = func; }
231 template <typename set_t>
232 struct hb_add_coverage_context_t :
233 hb_dispatch_context_t<hb_add_coverage_context_t<set_t>, const Coverage &, HB_DEBUG_GET_COVERAGE>
235 const char *get_name () { return "GET_COVERAGE"; }
236 typedef const Coverage &return_t;
237 template <typename T>
238 return_t dispatch (const T &obj) { return obj.get_coverage (); }
239 static return_t default_return_value () { return Null(Coverage); }
240 bool stop_sublookup_iteration (return_t r) const
242 r.add_coverage (set);
246 hb_add_coverage_context_t (set_t *set_) :
251 unsigned int debug_depth;
255 struct hb_ot_apply_context_t :
256 hb_dispatch_context_t<hb_ot_apply_context_t, bool, HB_DEBUG_APPLY>
265 #define arg1(arg) (arg) /* Remove the macro to see why it's needed! */
268 match_func (nullptr),
269 match_data (nullptr) {}
271 typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data);
273 void set_ignore_zwnj (bool ignore_zwnj_) { ignore_zwnj = ignore_zwnj_; }
274 void set_ignore_zwj (bool ignore_zwj_) { ignore_zwj = ignore_zwj_; }
275 void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; }
276 void set_mask (hb_mask_t mask_) { mask = mask_; }
277 void set_syllable (uint8_t syllable_) { syllable = syllable_; }
278 void set_match_func (match_func_t match_func_,
279 const void *match_data_)
280 { match_func = match_func_; match_data = match_data_; }
288 may_match_t may_match (const hb_glyph_info_t &info,
289 const HBUINT16 *glyph_data) const
291 if (!(info.mask & mask) ||
292 (syllable && syllable != info.syllable ()))
296 return match_func (info.codepoint, *glyph_data, match_data) ? MATCH_YES : MATCH_NO;
307 may_skip_t may_skip (const hb_ot_apply_context_t *c,
308 const hb_glyph_info_t &info) const
310 if (!c->check_glyph_property (&info, lookup_props))
313 if (unlikely (_hb_glyph_info_is_default_ignorable_and_not_hidden (&info) &&
314 (ignore_zwnj || !_hb_glyph_info_is_zwnj (&info)) &&
315 (ignore_zwj || !_hb_glyph_info_is_zwj (&info))))
322 unsigned int lookup_props;
327 match_func_t match_func;
328 const void *match_data;
331 struct skipping_iterator_t
333 void init (hb_ot_apply_context_t *c_, bool context_match = false)
336 match_glyph_data = nullptr;
337 matcher.set_match_func (nullptr, nullptr);
338 matcher.set_lookup_props (c->lookup_props);
339 /* Ignore ZWNJ if we are matching GPOS, or matching GSUB context and asked to. */
340 matcher.set_ignore_zwnj (c->table_index == 1 || (context_match && c->auto_zwnj));
341 /* Ignore ZWJ if we are matching context, or asked to. */
342 matcher.set_ignore_zwj (context_match || c->auto_zwj);
343 matcher.set_mask (context_match ? -1 : c->lookup_mask);
345 void set_lookup_props (unsigned int lookup_props)
347 matcher.set_lookup_props (lookup_props);
349 void set_match_func (matcher_t::match_func_t match_func_,
350 const void *match_data_,
351 const HBUINT16 glyph_data[])
353 matcher.set_match_func (match_func_, match_data_);
354 match_glyph_data = glyph_data;
357 void reset (unsigned int start_index_,
358 unsigned int num_items_)
361 num_items = num_items_;
362 end = c->buffer->len;
363 matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0);
366 void reject () { num_items++; match_glyph_data--; }
368 matcher_t::may_skip_t
369 may_skip (const hb_glyph_info_t &info) const
370 { return matcher.may_skip (c, info); }
374 assert (num_items > 0);
375 while (idx + num_items < end)
378 const hb_glyph_info_t &info = c->buffer->info[idx];
380 matcher_t::may_skip_t skip = matcher.may_skip (c, info);
381 if (unlikely (skip == matcher_t::SKIP_YES))
384 matcher_t::may_match_t match = matcher.may_match (info, match_glyph_data);
385 if (match == matcher_t::MATCH_YES ||
386 (match == matcher_t::MATCH_MAYBE &&
387 skip == matcher_t::SKIP_NO))
390 if (match_glyph_data) match_glyph_data++;
394 if (skip == matcher_t::SKIP_NO)
401 assert (num_items > 0);
402 while (idx > num_items - 1)
405 const hb_glyph_info_t &info = c->buffer->out_info[idx];
407 matcher_t::may_skip_t skip = matcher.may_skip (c, info);
408 if (unlikely (skip == matcher_t::SKIP_YES))
411 matcher_t::may_match_t match = matcher.may_match (info, match_glyph_data);
412 if (match == matcher_t::MATCH_YES ||
413 (match == matcher_t::MATCH_MAYBE &&
414 skip == matcher_t::SKIP_NO))
417 if (match_glyph_data) match_glyph_data++;
421 if (skip == matcher_t::SKIP_NO)
429 hb_ot_apply_context_t *c;
431 const HBUINT16 *match_glyph_data;
433 unsigned int num_items;
438 const char *get_name () { return "APPLY"; }
439 typedef return_t (*recurse_func_t) (hb_ot_apply_context_t *c, unsigned int lookup_index);
440 template <typename T>
441 return_t dispatch (const T &obj) { return obj.apply (this); }
442 static return_t default_return_value () { return false; }
443 bool stop_sublookup_iteration (return_t r) const { return r; }
444 return_t recurse (unsigned int sub_lookup_index)
446 if (unlikely (nesting_level_left == 0 || !recurse_func || buffer->max_ops-- <= 0))
447 return default_return_value ();
449 nesting_level_left--;
450 bool ret = recurse_func (this, sub_lookup_index);
451 nesting_level_left++;
455 skipping_iterator_t iter_input, iter_context;
460 recurse_func_t recurse_func;
462 const VariationStore &var_store;
464 hb_direction_t direction;
465 hb_mask_t lookup_mask;
466 unsigned int table_index; /* GSUB/GPOS */
467 unsigned int lookup_index;
468 unsigned int lookup_props;
469 unsigned int nesting_level_left;
470 unsigned int debug_depth;
472 bool has_glyph_classes;
477 uint32_t random_state;
480 signed last_base = -1; // GPOS uses
481 unsigned last_base_until = 0; // GPOS uses
483 hb_ot_apply_context_t (unsigned int table_index_,
485 hb_buffer_t *buffer_) :
486 iter_input (), iter_context (),
487 font (font_), face (font->face), buffer (buffer_),
488 recurse_func (nullptr),
490 #ifndef HB_NO_OT_LAYOUT
491 *face->table.GDEF->table
496 var_store (gdef.get_var_store ()),
497 direction (buffer_->props.direction),
499 table_index (table_index_),
500 lookup_index ((unsigned int) -1),
502 nesting_level_left (HB_MAX_NESTING_LEVEL),
504 has_glyph_classes (gdef.has_glyph_classes ()),
508 random_state (1) { init_iters (); }
512 iter_input.init (this, false);
513 iter_context.init (this, true);
516 void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; last_base = -1; last_base_until = 0; init_iters (); }
517 void set_auto_zwj (bool auto_zwj_) { auto_zwj = auto_zwj_; init_iters (); }
518 void set_auto_zwnj (bool auto_zwnj_) { auto_zwnj = auto_zwnj_; init_iters (); }
519 void set_random (bool random_) { random = random_; }
520 void set_recurse_func (recurse_func_t func) { recurse_func = func; }
521 void set_lookup_index (unsigned int lookup_index_) { lookup_index = lookup_index_; }
522 void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; init_iters (); }
524 uint32_t random_number ()
526 /* http://www.cplusplus.com/reference/random/minstd_rand/ */
527 random_state = random_state * 48271 % 2147483647;
531 bool match_properties_mark (hb_codepoint_t glyph,
532 unsigned int glyph_props,
533 unsigned int match_props) const
535 /* If using mark filtering sets, the high short of
536 * match_props has the set index.
538 if (match_props & LookupFlag::UseMarkFilteringSet)
539 return gdef.mark_set_covers (match_props >> 16, glyph);
541 /* The second byte of match_props has the meaning
542 * "ignore marks of attachment type different than
543 * the attachment type specified."
545 if (match_props & LookupFlag::MarkAttachmentType)
546 return (match_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType);
551 bool check_glyph_property (const hb_glyph_info_t *info,
552 unsigned int match_props) const
554 hb_codepoint_t glyph = info->codepoint;
555 unsigned int glyph_props = _hb_glyph_info_get_glyph_props (info);
557 /* Not covered, if, for example, glyph class is ligature and
558 * match_props includes LookupFlags::IgnoreLigatures
560 if (glyph_props & match_props & LookupFlag::IgnoreFlags)
563 if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_PROPS_MARK))
564 return match_properties_mark (glyph, glyph_props, match_props);
569 void _set_glyph_props (hb_codepoint_t glyph_index,
570 unsigned int class_guess = 0,
571 bool ligature = false,
572 bool component = false) const
574 unsigned int add_in = _hb_glyph_info_get_glyph_props (&buffer->cur()) &
575 HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE;
576 add_in |= HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED;
579 add_in |= HB_OT_LAYOUT_GLYPH_PROPS_LIGATED;
580 /* In the only place that the MULTIPLIED bit is used, Uniscribe
581 * seems to only care about the "last" transformation between
582 * Ligature and Multiple substitutions. Ie. if you ligate, expand,
583 * and ligate again, it forgives the multiplication and acts as
584 * if only ligation happened. As such, clear MULTIPLIED bit.
586 add_in &= ~HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED;
589 add_in |= HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED;
590 if (likely (has_glyph_classes))
591 _hb_glyph_info_set_glyph_props (&buffer->cur(), add_in | gdef.get_glyph_props (glyph_index));
592 else if (class_guess)
593 _hb_glyph_info_set_glyph_props (&buffer->cur(), add_in | class_guess);
596 void replace_glyph (hb_codepoint_t glyph_index) const
598 _set_glyph_props (glyph_index);
599 buffer->replace_glyph (glyph_index);
601 void replace_glyph_inplace (hb_codepoint_t glyph_index) const
603 _set_glyph_props (glyph_index);
604 buffer->cur().codepoint = glyph_index;
606 void replace_glyph_with_ligature (hb_codepoint_t glyph_index,
607 unsigned int class_guess) const
609 _set_glyph_props (glyph_index, class_guess, true);
610 buffer->replace_glyph (glyph_index);
612 void output_glyph_for_component (hb_codepoint_t glyph_index,
613 unsigned int class_guess) const
615 _set_glyph_props (glyph_index, class_guess, false, true);
616 buffer->output_glyph (glyph_index);
621 struct hb_get_subtables_context_t :
622 hb_dispatch_context_t<hb_get_subtables_context_t, hb_empty_t, HB_DEBUG_APPLY>
624 template <typename Type>
625 HB_INTERNAL static bool apply_to (const void *obj, OT::hb_ot_apply_context_t *c)
627 const Type *typed_obj = (const Type *) obj;
628 return typed_obj->apply (c);
631 typedef bool (*hb_apply_func_t) (const void *obj, OT::hb_ot_apply_context_t *c);
633 struct hb_applicable_t
635 template <typename T>
636 void init (const T &obj_, hb_apply_func_t apply_func_)
639 apply_func = apply_func_;
641 obj_.get_coverage ().add_coverage (&digest);
644 bool apply (OT::hb_ot_apply_context_t *c) const
646 return digest.may_have (c->buffer->cur().codepoint) && apply_func (obj, c);
651 hb_apply_func_t apply_func;
652 hb_set_digest_t digest;
655 typedef hb_vector_t<hb_applicable_t> array_t;
657 /* Dispatch interface. */
658 const char *get_name () { return "GET_SUBTABLES"; }
659 template <typename T>
660 return_t dispatch (const T &obj)
662 hb_applicable_t *entry = array.push();
663 entry->init (obj, apply_to<T>);
664 return hb_empty_t ();
666 static return_t default_return_value () { return hb_empty_t (); }
668 hb_get_subtables_context_t (array_t &array_) :
673 unsigned int debug_depth;
679 typedef bool (*intersects_func_t) (const hb_set_t *glyphs, const HBUINT16 &value, const void *data);
680 typedef void (*collect_glyphs_func_t) (hb_set_t *glyphs, const HBUINT16 &value, const void *data);
681 typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data);
683 struct ContextClosureFuncs
685 intersects_func_t intersects;
687 struct ContextCollectGlyphsFuncs
689 collect_glyphs_func_t collect;
691 struct ContextApplyFuncs
697 static inline bool intersects_glyph (const hb_set_t *glyphs, const HBUINT16 &value, const void *data HB_UNUSED)
699 return glyphs->has (value);
701 static inline bool intersects_class (const hb_set_t *glyphs, const HBUINT16 &value, const void *data)
703 const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
704 return class_def.intersects_class (glyphs, value);
706 static inline bool intersects_coverage (const hb_set_t *glyphs, const HBUINT16 &value, const void *data)
708 const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
709 return (data+coverage).intersects (glyphs);
712 static inline bool intersects_array (const hb_set_t *glyphs,
714 const HBUINT16 values[],
715 intersects_func_t intersects_func,
716 const void *intersects_data)
718 for (const HBUINT16 &_ : + hb_iter (values, count))
719 if (intersects_func (glyphs, _, intersects_data)) return true;
724 static inline void collect_glyph (hb_set_t *glyphs, const HBUINT16 &value, const void *data HB_UNUSED)
728 static inline void collect_class (hb_set_t *glyphs, const HBUINT16 &value, const void *data)
730 const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
731 class_def.add_class (glyphs, value);
733 static inline void collect_coverage (hb_set_t *glyphs, const HBUINT16 &value, const void *data)
735 const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
736 (data+coverage).add_coverage (glyphs);
738 static inline void collect_array (hb_collect_glyphs_context_t *c HB_UNUSED,
741 const HBUINT16 values[],
742 collect_glyphs_func_t collect_func,
743 const void *collect_data)
746 + hb_iter (values, count)
747 | hb_apply ([&] (const HBUINT16 &_) { collect_func (glyphs, _, collect_data); })
752 static inline bool match_glyph (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data HB_UNUSED)
754 return glyph_id == value;
756 static inline bool match_class (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data)
758 const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
759 return class_def.get_class (glyph_id) == value;
761 static inline bool match_coverage (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data)
763 const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
764 return (data+coverage).get_coverage (glyph_id) != NOT_COVERED;
767 static inline bool would_match_input (hb_would_apply_context_t *c,
768 unsigned int count, /* Including the first glyph (not matched) */
769 const HBUINT16 input[], /* Array of input values--start with second glyph */
770 match_func_t match_func,
771 const void *match_data)
776 for (unsigned int i = 1; i < count; i++)
777 if (likely (!match_func (c->glyphs[i], input[i - 1], match_data)))
782 static inline bool match_input (hb_ot_apply_context_t *c,
783 unsigned int count, /* Including the first glyph (not matched) */
784 const HBUINT16 input[], /* Array of input values--start with second glyph */
785 match_func_t match_func,
786 const void *match_data,
787 unsigned int *end_offset,
788 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH],
789 unsigned int *p_total_component_count = nullptr)
791 TRACE_APPLY (nullptr);
793 if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return_trace (false);
795 hb_buffer_t *buffer = c->buffer;
797 hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
798 skippy_iter.reset (buffer->idx, count - 1);
799 skippy_iter.set_match_func (match_func, match_data, input);
802 * This is perhaps the trickiest part of OpenType... Remarks:
804 * - If all components of the ligature were marks, we call this a mark ligature.
806 * - If there is no GDEF, and the ligature is NOT a mark ligature, we categorize
807 * it as a ligature glyph.
809 * - Ligatures cannot be formed across glyphs attached to different components
810 * of previous ligatures. Eg. the sequence is LAM,SHADDA,LAM,FATHA,HEH, and
811 * LAM,LAM,HEH form a ligature, leaving SHADDA,FATHA next to eachother.
812 * However, it would be wrong to ligate that SHADDA,FATHA sequence.
813 * There are a couple of exceptions to this:
815 * o If a ligature tries ligating with marks that belong to it itself, go ahead,
816 * assuming that the font designer knows what they are doing (otherwise it can
817 * break Indic stuff when a matra wants to ligate with a conjunct,
819 * o If two marks want to ligate and they belong to different components of the
820 * same ligature glyph, and said ligature glyph is to be ignored according to
821 * mark-filtering rules, then allow.
822 * https://github.com/harfbuzz/harfbuzz/issues/545
825 unsigned int total_component_count = 0;
826 total_component_count += _hb_glyph_info_get_lig_num_comps (&buffer->cur());
828 unsigned int first_lig_id = _hb_glyph_info_get_lig_id (&buffer->cur());
829 unsigned int first_lig_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
833 LIGBASE_MAY_NOT_SKIP,
835 } ligbase = LIGBASE_NOT_CHECKED;
837 match_positions[0] = buffer->idx;
838 for (unsigned int i = 1; i < count; i++)
840 if (!skippy_iter.next ()) return_trace (false);
842 match_positions[i] = skippy_iter.idx;
844 unsigned int this_lig_id = _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]);
845 unsigned int this_lig_comp = _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]);
847 if (first_lig_id && first_lig_comp)
849 /* If first component was attached to a previous ligature component,
850 * all subsequent components should be attached to the same ligature
851 * component, otherwise we shouldn't ligate them... */
852 if (first_lig_id != this_lig_id || first_lig_comp != this_lig_comp)
854 /* ...unless, we are attached to a base ligature and that base
855 * ligature is ignorable. */
856 if (ligbase == LIGBASE_NOT_CHECKED)
859 const auto *out = buffer->out_info;
860 unsigned int j = buffer->out_len;
861 while (j && _hb_glyph_info_get_lig_id (&out[j - 1]) == first_lig_id)
863 if (_hb_glyph_info_get_lig_comp (&out[j - 1]) == 0)
872 if (found && skippy_iter.may_skip (out[j]) == hb_ot_apply_context_t::matcher_t::SKIP_YES)
873 ligbase = LIGBASE_MAY_SKIP;
875 ligbase = LIGBASE_MAY_NOT_SKIP;
878 if (ligbase == LIGBASE_MAY_NOT_SKIP)
879 return_trace (false);
884 /* If first component was NOT attached to a previous ligature component,
885 * all subsequent components should also NOT be attached to any ligature
886 * component, unless they are attached to the first component itself! */
887 if (this_lig_id && this_lig_comp && (this_lig_id != first_lig_id))
888 return_trace (false);
891 total_component_count += _hb_glyph_info_get_lig_num_comps (&buffer->info[skippy_iter.idx]);
894 *end_offset = skippy_iter.idx - buffer->idx + 1;
896 if (p_total_component_count)
897 *p_total_component_count = total_component_count;
901 static inline bool ligate_input (hb_ot_apply_context_t *c,
902 unsigned int count, /* Including the first glyph */
903 const unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], /* Including the first glyph */
904 unsigned int match_length,
905 hb_codepoint_t lig_glyph,
906 unsigned int total_component_count)
908 TRACE_APPLY (nullptr);
910 hb_buffer_t *buffer = c->buffer;
912 buffer->merge_clusters (buffer->idx, buffer->idx + match_length);
914 /* - If a base and one or more marks ligate, consider that as a base, NOT
915 * ligature, such that all following marks can still attach to it.
916 * https://github.com/harfbuzz/harfbuzz/issues/1109
918 * - If all components of the ligature were marks, we call this a mark ligature.
919 * If it *is* a mark ligature, we don't allocate a new ligature id, and leave
920 * the ligature to keep its old ligature id. This will allow it to attach to
921 * a base ligature in GPOS. Eg. if the sequence is: LAM,LAM,SHADDA,FATHA,HEH,
922 * and LAM,LAM,HEH for a ligature, they will leave SHADDA and FATHA with a
923 * ligature id and component value of 2. Then if SHADDA,FATHA form a ligature
924 * later, we don't want them to lose their ligature id/component, otherwise
925 * GPOS will fail to correctly position the mark ligature on top of the
926 * LAM,LAM,HEH ligature. See:
927 * https://bugzilla.gnome.org/show_bug.cgi?id=676343
929 * - If a ligature is formed of components that some of which are also ligatures
930 * themselves, and those ligature components had marks attached to *their*
931 * components, we have to attach the marks to the new ligature component
932 * positions! Now *that*'s tricky! And these marks may be following the
933 * last component of the whole sequence, so we should loop forward looking
934 * for them and update them.
936 * Eg. the sequence is LAM,LAM,SHADDA,FATHA,HEH, and the font first forms a
937 * 'calt' ligature of LAM,HEH, leaving the SHADDA and FATHA with a ligature
938 * id and component == 1. Now, during 'liga', the LAM and the LAM-HEH ligature
939 * form a LAM-LAM-HEH ligature. We need to reassign the SHADDA and FATHA to
940 * the new ligature with a component value of 2.
942 * This in fact happened to a font... See:
943 * https://bugzilla.gnome.org/show_bug.cgi?id=437633
946 bool is_base_ligature = _hb_glyph_info_is_base_glyph (&buffer->info[match_positions[0]]);
947 bool is_mark_ligature = _hb_glyph_info_is_mark (&buffer->info[match_positions[0]]);
948 for (unsigned int i = 1; i < count; i++)
949 if (!_hb_glyph_info_is_mark (&buffer->info[match_positions[i]]))
951 is_base_ligature = false;
952 is_mark_ligature = false;
955 bool is_ligature = !is_base_ligature && !is_mark_ligature;
957 unsigned int klass = is_ligature ? HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE : 0;
958 unsigned int lig_id = is_ligature ? _hb_allocate_lig_id (buffer) : 0;
959 unsigned int last_lig_id = _hb_glyph_info_get_lig_id (&buffer->cur());
960 unsigned int last_num_components = _hb_glyph_info_get_lig_num_comps (&buffer->cur());
961 unsigned int components_so_far = last_num_components;
965 _hb_glyph_info_set_lig_props_for_ligature (&buffer->cur(), lig_id, total_component_count);
966 if (_hb_glyph_info_get_general_category (&buffer->cur()) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
968 _hb_glyph_info_set_general_category (&buffer->cur(), HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER);
971 c->replace_glyph_with_ligature (lig_glyph, klass);
973 for (unsigned int i = 1; i < count; i++)
975 while (buffer->idx < match_positions[i] && buffer->successful)
979 unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
981 this_comp = last_num_components;
982 unsigned int new_lig_comp = components_so_far - last_num_components +
983 hb_min (this_comp, last_num_components);
984 _hb_glyph_info_set_lig_props_for_mark (&buffer->cur(), lig_id, new_lig_comp);
986 buffer->next_glyph ();
989 last_lig_id = _hb_glyph_info_get_lig_id (&buffer->cur());
990 last_num_components = _hb_glyph_info_get_lig_num_comps (&buffer->cur());
991 components_so_far += last_num_components;
993 /* Skip the base glyph */
997 if (!is_mark_ligature && last_lig_id) {
998 /* Re-adjust components for any marks following. */
999 for (unsigned int i = buffer->idx; i < buffer->len; i++) {
1000 if (last_lig_id == _hb_glyph_info_get_lig_id (&buffer->info[i])) {
1001 unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->info[i]);
1004 unsigned int new_lig_comp = components_so_far - last_num_components +
1005 hb_min (this_comp, last_num_components);
1006 _hb_glyph_info_set_lig_props_for_mark (&buffer->info[i], lig_id, new_lig_comp);
1011 return_trace (true);
1014 static inline bool match_backtrack (hb_ot_apply_context_t *c,
1016 const HBUINT16 backtrack[],
1017 match_func_t match_func,
1018 const void *match_data,
1019 unsigned int *match_start)
1021 TRACE_APPLY (nullptr);
1023 hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
1024 skippy_iter.reset (c->buffer->backtrack_len (), count);
1025 skippy_iter.set_match_func (match_func, match_data, backtrack);
1027 for (unsigned int i = 0; i < count; i++)
1028 if (!skippy_iter.prev ())
1029 return_trace (false);
1031 *match_start = skippy_iter.idx;
1033 return_trace (true);
1036 static inline bool match_lookahead (hb_ot_apply_context_t *c,
1038 const HBUINT16 lookahead[],
1039 match_func_t match_func,
1040 const void *match_data,
1041 unsigned int offset,
1042 unsigned int *end_index)
1044 TRACE_APPLY (nullptr);
1046 hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
1047 skippy_iter.reset (c->buffer->idx + offset - 1, count);
1048 skippy_iter.set_match_func (match_func, match_data, lookahead);
1050 for (unsigned int i = 0; i < count; i++)
1051 if (!skippy_iter.next ())
1052 return_trace (false);
1054 *end_index = skippy_iter.idx + 1;
1056 return_trace (true);
1063 bool sanitize (hb_sanitize_context_t *c) const
1065 TRACE_SANITIZE (this);
1066 return_trace (c->check_struct (this));
1069 HBUINT16 sequenceIndex; /* Index into current glyph
1070 * sequence--first glyph = 0 */
1071 HBUINT16 lookupListIndex; /* Lookup to apply to that
1072 * position--zero--based */
1074 DEFINE_SIZE_STATIC (4);
1077 template <typename context_t>
1078 static inline void recurse_lookups (context_t *c,
1079 unsigned int lookupCount,
1080 const LookupRecord lookupRecord[] /* Array of LookupRecords--in design order */)
1082 for (unsigned int i = 0; i < lookupCount; i++)
1083 c->recurse (lookupRecord[i].lookupListIndex);
1086 static inline bool apply_lookup (hb_ot_apply_context_t *c,
1087 unsigned int count, /* Including the first glyph */
1088 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], /* Including the first glyph */
1089 unsigned int lookupCount,
1090 const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
1091 unsigned int match_length)
1093 TRACE_APPLY (nullptr);
1095 hb_buffer_t *buffer = c->buffer;
1098 /* All positions are distance from beginning of *output* buffer.
1101 unsigned int bl = buffer->backtrack_len ();
1102 end = bl + match_length;
1104 int delta = bl - buffer->idx;
1105 /* Convert positions to new indexing. */
1106 for (unsigned int j = 0; j < count; j++)
1107 match_positions[j] += delta;
1110 for (unsigned int i = 0; i < lookupCount && buffer->successful; i++)
1112 unsigned int idx = lookupRecord[i].sequenceIndex;
1116 /* Don't recurse to ourself at same position.
1117 * Note that this test is too naive, it doesn't catch longer loops. */
1118 if (idx == 0 && lookupRecord[i].lookupListIndex == c->lookup_index)
1121 if (unlikely (!buffer->move_to (match_positions[idx])))
1124 if (unlikely (buffer->max_ops <= 0))
1127 unsigned int orig_len = buffer->backtrack_len () + buffer->lookahead_len ();
1128 if (!c->recurse (lookupRecord[i].lookupListIndex))
1131 unsigned int new_len = buffer->backtrack_len () + buffer->lookahead_len ();
1132 int delta = new_len - orig_len;
1137 /* Recursed lookup changed buffer len. Adjust.
1141 * Right now, if buffer length increased by n, we assume n new glyphs
1142 * were added right after the current position, and if buffer length
1143 * was decreased by n, we assume n match positions after the current
1144 * one where removed. The former (buffer length increased) case is
1145 * fine, but the decrease case can be improved in at least two ways,
1146 * both of which are significant:
1148 * - If recursed-to lookup is MultipleSubst and buffer length
1149 * decreased, then it's current match position that was deleted,
1150 * NOT the one after it.
1152 * - If buffer length was decreased by n, it does not necessarily
1153 * mean that n match positions where removed, as there might
1154 * have been marks and default-ignorables in the sequence. We
1155 * should instead drop match positions between current-position
1156 * and current-position + n instead.
1158 * It should be possible to construct tests for both of these cases.
1162 if (end <= int (match_positions[idx]))
1164 /* End might end up being smaller than match_positions[idx] if the recursed
1165 * lookup ended up removing many items, more than we have had matched.
1166 * Just never rewind end back and get out of here.
1167 * https://bugs.chromium.org/p/chromium/issues/detail?id=659496 */
1168 end = match_positions[idx];
1169 /* There can't be any further changes. */
1173 unsigned int next = idx + 1; /* next now is the position after the recursed lookup. */
1177 if (unlikely (delta + count > HB_MAX_CONTEXT_LENGTH))
1182 /* NOTE: delta is negative. */
1183 delta = hb_max (delta, (int) next - (int) count);
1188 memmove (match_positions + next + delta, match_positions + next,
1189 (count - next) * sizeof (match_positions[0]));
1193 /* Fill in new entries. */
1194 for (unsigned int j = idx + 1; j < next; j++)
1195 match_positions[j] = match_positions[j - 1] + 1;
1197 /* And fixup the rest. */
1198 for (; next < count; next++)
1199 match_positions[next] += delta;
1202 buffer->move_to (end);
1204 return_trace (true);
1209 /* Contextual lookups */
1211 struct ContextClosureLookupContext
1213 ContextClosureFuncs funcs;
1214 const void *intersects_data;
1217 struct ContextCollectGlyphsLookupContext
1219 ContextCollectGlyphsFuncs funcs;
1220 const void *collect_data;
1223 struct ContextApplyLookupContext
1225 ContextApplyFuncs funcs;
1226 const void *match_data;
1229 static inline bool context_intersects (const hb_set_t *glyphs,
1230 unsigned int inputCount, /* Including the first glyph (not matched) */
1231 const HBUINT16 input[], /* Array of input values--start with second glyph */
1232 ContextClosureLookupContext &lookup_context)
1234 return intersects_array (glyphs,
1235 inputCount ? inputCount - 1 : 0, input,
1236 lookup_context.funcs.intersects, lookup_context.intersects_data);
1239 static inline void context_closure_lookup (hb_closure_context_t *c,
1240 unsigned int inputCount, /* Including the first glyph (not matched) */
1241 const HBUINT16 input[], /* Array of input values--start with second glyph */
1242 unsigned int lookupCount,
1243 const LookupRecord lookupRecord[],
1244 ContextClosureLookupContext &lookup_context)
1246 if (context_intersects (c->glyphs,
1250 lookupCount, lookupRecord);
1253 static inline void context_collect_glyphs_lookup (hb_collect_glyphs_context_t *c,
1254 unsigned int inputCount, /* Including the first glyph (not matched) */
1255 const HBUINT16 input[], /* Array of input values--start with second glyph */
1256 unsigned int lookupCount,
1257 const LookupRecord lookupRecord[],
1258 ContextCollectGlyphsLookupContext &lookup_context)
1260 collect_array (c, c->input,
1261 inputCount ? inputCount - 1 : 0, input,
1262 lookup_context.funcs.collect, lookup_context.collect_data);
1264 lookupCount, lookupRecord);
1267 static inline bool context_would_apply_lookup (hb_would_apply_context_t *c,
1268 unsigned int inputCount, /* Including the first glyph (not matched) */
1269 const HBUINT16 input[], /* Array of input values--start with second glyph */
1270 unsigned int lookupCount HB_UNUSED,
1271 const LookupRecord lookupRecord[] HB_UNUSED,
1272 ContextApplyLookupContext &lookup_context)
1274 return would_match_input (c,
1276 lookup_context.funcs.match, lookup_context.match_data);
1278 static inline bool context_apply_lookup (hb_ot_apply_context_t *c,
1279 unsigned int inputCount, /* Including the first glyph (not matched) */
1280 const HBUINT16 input[], /* Array of input values--start with second glyph */
1281 unsigned int lookupCount,
1282 const LookupRecord lookupRecord[],
1283 ContextApplyLookupContext &lookup_context)
1285 unsigned int match_length = 0;
1286 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
1287 return match_input (c,
1289 lookup_context.funcs.match, lookup_context.match_data,
1290 &match_length, match_positions)
1291 && (c->buffer->unsafe_to_break (c->buffer->idx, c->buffer->idx + match_length),
1293 inputCount, match_positions,
1294 lookupCount, lookupRecord,
1300 bool intersects (const hb_set_t *glyphs, ContextClosureLookupContext &lookup_context) const
1302 return context_intersects (glyphs,
1303 inputCount, inputZ.arrayZ,
1307 void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
1309 const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>>
1310 (inputZ.as_array ((inputCount ? inputCount - 1 : 0)));
1311 context_closure_lookup (c,
1312 inputCount, inputZ.arrayZ,
1313 lookupCount, lookupRecord.arrayZ,
1317 void collect_glyphs (hb_collect_glyphs_context_t *c,
1318 ContextCollectGlyphsLookupContext &lookup_context) const
1320 const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>>
1321 (inputZ.as_array (inputCount ? inputCount - 1 : 0));
1322 context_collect_glyphs_lookup (c,
1323 inputCount, inputZ.arrayZ,
1324 lookupCount, lookupRecord.arrayZ,
1328 bool would_apply (hb_would_apply_context_t *c,
1329 ContextApplyLookupContext &lookup_context) const
1331 const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>>
1332 (inputZ.as_array (inputCount ? inputCount - 1 : 0));
1333 return context_would_apply_lookup (c,
1334 inputCount, inputZ.arrayZ,
1335 lookupCount, lookupRecord.arrayZ,
1339 bool apply (hb_ot_apply_context_t *c,
1340 ContextApplyLookupContext &lookup_context) const
1343 const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>>
1344 (inputZ.as_array (inputCount ? inputCount - 1 : 0));
1345 return_trace (context_apply_lookup (c, inputCount, inputZ.arrayZ, lookupCount, lookupRecord.arrayZ, lookup_context));
1349 bool sanitize (hb_sanitize_context_t *c) const
1351 TRACE_SANITIZE (this);
1352 return_trace (inputCount.sanitize (c) &&
1353 lookupCount.sanitize (c) &&
1354 c->check_range (inputZ.arrayZ,
1355 inputZ.item_size * (inputCount ? inputCount - 1 : 0) +
1356 LookupRecord::static_size * lookupCount));
1360 HBUINT16 inputCount; /* Total number of glyphs in input
1361 * glyph sequence--includes the first
1363 HBUINT16 lookupCount; /* Number of LookupRecords */
1364 UnsizedArrayOf<HBUINT16>
1365 inputZ; /* Array of match inputs--start with
1367 /*UnsizedArrayOf<LookupRecord>
1368 lookupRecordX;*/ /* Array of LookupRecords--in
1371 DEFINE_SIZE_ARRAY (4, inputZ);
1376 bool intersects (const hb_set_t *glyphs,
1377 ContextClosureLookupContext &lookup_context) const
1381 | hb_map (hb_add (this))
1382 | hb_map ([&] (const Rule &_) { return _.intersects (glyphs, lookup_context); })
1387 void closure (hb_closure_context_t *c,
1388 ContextClosureLookupContext &lookup_context) const
1392 | hb_map (hb_add (this))
1393 | hb_apply ([&] (const Rule &_) { _.closure (c, lookup_context); })
1397 void collect_glyphs (hb_collect_glyphs_context_t *c,
1398 ContextCollectGlyphsLookupContext &lookup_context) const
1402 | hb_map (hb_add (this))
1403 | hb_apply ([&] (const Rule &_) { _.collect_glyphs (c, lookup_context); })
1407 bool would_apply (hb_would_apply_context_t *c,
1408 ContextApplyLookupContext &lookup_context) const
1412 | hb_map (hb_add (this))
1413 | hb_map ([&] (const Rule &_) { return _.would_apply (c, lookup_context); })
1418 bool apply (hb_ot_apply_context_t *c,
1419 ContextApplyLookupContext &lookup_context) const
1424 | hb_map (hb_add (this))
1425 | hb_map ([&] (const Rule &_) { return _.apply (c, lookup_context); })
1431 bool sanitize (hb_sanitize_context_t *c) const
1433 TRACE_SANITIZE (this);
1434 return_trace (rule.sanitize (c, this));
1439 rule; /* Array of Rule tables
1440 * ordered by preference */
1442 DEFINE_SIZE_ARRAY (2, rule);
1446 struct ContextFormat1
1448 bool intersects (const hb_set_t *glyphs) const
1450 struct ContextClosureLookupContext lookup_context = {
1456 + hb_zip (this+coverage, ruleSet)
1457 | hb_filter (*glyphs, hb_first)
1458 | hb_map (hb_second)
1459 | hb_map (hb_add (this))
1460 | hb_map ([&] (const RuleSet &_) { return _.intersects (glyphs, lookup_context); })
1465 void closure (hb_closure_context_t *c) const
1467 struct ContextClosureLookupContext lookup_context = {
1472 + hb_zip (this+coverage, ruleSet)
1473 | hb_filter (*c->glyphs, hb_first)
1474 | hb_map (hb_second)
1475 | hb_map (hb_add (this))
1476 | hb_apply ([&] (const RuleSet &_) { _.closure (c, lookup_context); })
1480 void collect_glyphs (hb_collect_glyphs_context_t *c) const
1482 (this+coverage).add_coverage (c->input);
1484 struct ContextCollectGlyphsLookupContext lookup_context = {
1490 | hb_map (hb_add (this))
1491 | hb_apply ([&] (const RuleSet &_) { _.collect_glyphs (c, lookup_context); })
1495 bool would_apply (hb_would_apply_context_t *c) const
1497 const RuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])];
1498 struct ContextApplyLookupContext lookup_context = {
1502 return rule_set.would_apply (c, lookup_context);
1505 const Coverage &get_coverage () const { return this+coverage; }
1507 bool apply (hb_ot_apply_context_t *c) const
1510 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
1511 if (likely (index == NOT_COVERED))
1512 return_trace (false);
1514 const RuleSet &rule_set = this+ruleSet[index];
1515 struct ContextApplyLookupContext lookup_context = {
1519 return_trace (rule_set.apply (c, lookup_context));
1522 bool subset (hb_subset_context_t *c) const
1524 TRACE_SUBSET (this);
1526 return_trace (false);
1529 bool sanitize (hb_sanitize_context_t *c) const
1531 TRACE_SANITIZE (this);
1532 return_trace (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
1536 HBUINT16 format; /* Format identifier--format = 1 */
1538 coverage; /* Offset to Coverage table--from
1539 * beginning of table */
1540 OffsetArrayOf<RuleSet>
1541 ruleSet; /* Array of RuleSet tables
1542 * ordered by Coverage Index */
1544 DEFINE_SIZE_ARRAY (6, ruleSet);
1548 struct ContextFormat2
1550 bool intersects (const hb_set_t *glyphs) const
1552 if (!(this+coverage).intersects (glyphs))
1555 const ClassDef &class_def = this+classDef;
1557 struct ContextClosureLookupContext lookup_context = {
1563 + hb_enumerate (ruleSet)
1564 | hb_map ([&] (const hb_pair_t<unsigned, const OffsetTo<RuleSet> &> p)
1565 { return class_def.intersects_class (glyphs, p.first) &&
1566 (this+p.second).intersects (glyphs, lookup_context); })
1571 void closure (hb_closure_context_t *c) const
1573 if (!(this+coverage).intersects (c->glyphs))
1576 const ClassDef &class_def = this+classDef;
1578 struct ContextClosureLookupContext lookup_context = {
1584 + hb_enumerate (ruleSet)
1585 | hb_filter ([&] (unsigned _)
1586 { return class_def.intersects_class (c->glyphs, _); },
1588 | hb_map (hb_second)
1589 | hb_map (hb_add (this))
1590 | hb_apply ([&] (const RuleSet &_) { _.closure (c, lookup_context); })
1594 void collect_glyphs (hb_collect_glyphs_context_t *c) const
1596 (this+coverage).add_coverage (c->input);
1598 const ClassDef &class_def = this+classDef;
1599 struct ContextCollectGlyphsLookupContext lookup_context = {
1605 | hb_map (hb_add (this))
1606 | hb_apply ([&] (const RuleSet &_) { _.collect_glyphs (c, lookup_context); })
1610 bool would_apply (hb_would_apply_context_t *c) const
1612 const ClassDef &class_def = this+classDef;
1613 unsigned int index = class_def.get_class (c->glyphs[0]);
1614 const RuleSet &rule_set = this+ruleSet[index];
1615 struct ContextApplyLookupContext lookup_context = {
1619 return rule_set.would_apply (c, lookup_context);
1622 const Coverage &get_coverage () const { return this+coverage; }
1624 bool apply (hb_ot_apply_context_t *c) const
1627 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
1628 if (likely (index == NOT_COVERED)) return_trace (false);
1630 const ClassDef &class_def = this+classDef;
1631 index = class_def.get_class (c->buffer->cur().codepoint);
1632 const RuleSet &rule_set = this+ruleSet[index];
1633 struct ContextApplyLookupContext lookup_context = {
1637 return_trace (rule_set.apply (c, lookup_context));
1640 bool subset (hb_subset_context_t *c) const
1642 TRACE_SUBSET (this);
1644 return_trace (false);
1647 bool sanitize (hb_sanitize_context_t *c) const
1649 TRACE_SANITIZE (this);
1650 return_trace (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this));
1654 HBUINT16 format; /* Format identifier--format = 2 */
1656 coverage; /* Offset to Coverage table--from
1657 * beginning of table */
1659 classDef; /* Offset to glyph ClassDef table--from
1660 * beginning of table */
1661 OffsetArrayOf<RuleSet>
1662 ruleSet; /* Array of RuleSet tables
1663 * ordered by class */
1665 DEFINE_SIZE_ARRAY (8, ruleSet);
1669 struct ContextFormat3
1671 bool intersects (const hb_set_t *glyphs) const
1673 if (!(this+coverageZ[0]).intersects (glyphs))
1676 struct ContextClosureLookupContext lookup_context = {
1677 {intersects_coverage},
1680 return context_intersects (glyphs,
1681 glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1),
1685 void closure (hb_closure_context_t *c) const
1687 if (!(this+coverageZ[0]).intersects (c->glyphs))
1690 const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
1691 struct ContextClosureLookupContext lookup_context = {
1692 {intersects_coverage},
1695 context_closure_lookup (c,
1696 glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1),
1697 lookupCount, lookupRecord,
1701 void collect_glyphs (hb_collect_glyphs_context_t *c) const
1703 (this+coverageZ[0]).add_coverage (c->input);
1705 const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
1706 struct ContextCollectGlyphsLookupContext lookup_context = {
1711 context_collect_glyphs_lookup (c,
1712 glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1),
1713 lookupCount, lookupRecord,
1717 bool would_apply (hb_would_apply_context_t *c) const
1719 const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
1720 struct ContextApplyLookupContext lookup_context = {
1724 return context_would_apply_lookup (c,
1725 glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1),
1726 lookupCount, lookupRecord,
1730 const Coverage &get_coverage () const { return this+coverageZ[0]; }
1732 bool apply (hb_ot_apply_context_t *c) const
1735 unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint);
1736 if (likely (index == NOT_COVERED)) return_trace (false);
1738 const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
1739 struct ContextApplyLookupContext lookup_context = {
1743 return_trace (context_apply_lookup (c, glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1), lookupCount, lookupRecord, lookup_context));
1746 bool subset (hb_subset_context_t *c) const
1748 TRACE_SUBSET (this);
1750 return_trace (false);
1753 bool sanitize (hb_sanitize_context_t *c) const
1755 TRACE_SANITIZE (this);
1756 if (!c->check_struct (this)) return_trace (false);
1757 unsigned int count = glyphCount;
1758 if (!count) return_trace (false); /* We want to access coverageZ[0] freely. */
1759 if (!c->check_array (coverageZ.arrayZ, count)) return_trace (false);
1760 for (unsigned int i = 0; i < count; i++)
1761 if (!coverageZ[i].sanitize (c, this)) return_trace (false);
1762 const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
1763 return_trace (c->check_array (lookupRecord, lookupCount));
1767 HBUINT16 format; /* Format identifier--format = 3 */
1768 HBUINT16 glyphCount; /* Number of glyphs in the input glyph
1770 HBUINT16 lookupCount; /* Number of LookupRecords */
1771 UnsizedArrayOf<OffsetTo<Coverage>>
1772 coverageZ; /* Array of offsets to Coverage
1773 * table in glyph sequence order */
1774 /*UnsizedArrayOf<LookupRecord>
1775 lookupRecordX;*/ /* Array of LookupRecords--in
1778 DEFINE_SIZE_ARRAY (6, coverageZ);
1783 template <typename context_t, typename ...Ts>
1784 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1786 TRACE_DISPATCH (this, u.format);
1787 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
1789 case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
1790 case 2: return_trace (c->dispatch (u.format2, hb_forward<Ts> (ds)...));
1791 case 3: return_trace (c->dispatch (u.format3, hb_forward<Ts> (ds)...));
1792 default:return_trace (c->default_return_value ());
1798 HBUINT16 format; /* Format identifier */
1799 ContextFormat1 format1;
1800 ContextFormat2 format2;
1801 ContextFormat3 format3;
1806 /* Chaining Contextual lookups */
1808 struct ChainContextClosureLookupContext
1810 ContextClosureFuncs funcs;
1811 const void *intersects_data[3];
1814 struct ChainContextCollectGlyphsLookupContext
1816 ContextCollectGlyphsFuncs funcs;
1817 const void *collect_data[3];
1820 struct ChainContextApplyLookupContext
1822 ContextApplyFuncs funcs;
1823 const void *match_data[3];
1826 static inline bool chain_context_intersects (const hb_set_t *glyphs,
1827 unsigned int backtrackCount,
1828 const HBUINT16 backtrack[],
1829 unsigned int inputCount, /* Including the first glyph (not matched) */
1830 const HBUINT16 input[], /* Array of input values--start with second glyph */
1831 unsigned int lookaheadCount,
1832 const HBUINT16 lookahead[],
1833 ChainContextClosureLookupContext &lookup_context)
1835 return intersects_array (glyphs,
1836 backtrackCount, backtrack,
1837 lookup_context.funcs.intersects, lookup_context.intersects_data[0])
1838 && intersects_array (glyphs,
1839 inputCount ? inputCount - 1 : 0, input,
1840 lookup_context.funcs.intersects, lookup_context.intersects_data[1])
1841 && intersects_array (glyphs,
1842 lookaheadCount, lookahead,
1843 lookup_context.funcs.intersects, lookup_context.intersects_data[2]);
1846 static inline void chain_context_closure_lookup (hb_closure_context_t *c,
1847 unsigned int backtrackCount,
1848 const HBUINT16 backtrack[],
1849 unsigned int inputCount, /* Including the first glyph (not matched) */
1850 const HBUINT16 input[], /* Array of input values--start with second glyph */
1851 unsigned int lookaheadCount,
1852 const HBUINT16 lookahead[],
1853 unsigned int lookupCount,
1854 const LookupRecord lookupRecord[],
1855 ChainContextClosureLookupContext &lookup_context)
1857 if (chain_context_intersects (c->glyphs,
1858 backtrackCount, backtrack,
1860 lookaheadCount, lookahead,
1863 lookupCount, lookupRecord);
1866 static inline void chain_context_collect_glyphs_lookup (hb_collect_glyphs_context_t *c,
1867 unsigned int backtrackCount,
1868 const HBUINT16 backtrack[],
1869 unsigned int inputCount, /* Including the first glyph (not matched) */
1870 const HBUINT16 input[], /* Array of input values--start with second glyph */
1871 unsigned int lookaheadCount,
1872 const HBUINT16 lookahead[],
1873 unsigned int lookupCount,
1874 const LookupRecord lookupRecord[],
1875 ChainContextCollectGlyphsLookupContext &lookup_context)
1877 collect_array (c, c->before,
1878 backtrackCount, backtrack,
1879 lookup_context.funcs.collect, lookup_context.collect_data[0]);
1880 collect_array (c, c->input,
1881 inputCount ? inputCount - 1 : 0, input,
1882 lookup_context.funcs.collect, lookup_context.collect_data[1]);
1883 collect_array (c, c->after,
1884 lookaheadCount, lookahead,
1885 lookup_context.funcs.collect, lookup_context.collect_data[2]);
1887 lookupCount, lookupRecord);
1890 static inline bool chain_context_would_apply_lookup (hb_would_apply_context_t *c,
1891 unsigned int backtrackCount,
1892 const HBUINT16 backtrack[] HB_UNUSED,
1893 unsigned int inputCount, /* Including the first glyph (not matched) */
1894 const HBUINT16 input[], /* Array of input values--start with second glyph */
1895 unsigned int lookaheadCount,
1896 const HBUINT16 lookahead[] HB_UNUSED,
1897 unsigned int lookupCount HB_UNUSED,
1898 const LookupRecord lookupRecord[] HB_UNUSED,
1899 ChainContextApplyLookupContext &lookup_context)
1901 return (c->zero_context ? !backtrackCount && !lookaheadCount : true)
1902 && would_match_input (c,
1904 lookup_context.funcs.match, lookup_context.match_data[1]);
1907 static inline bool chain_context_apply_lookup (hb_ot_apply_context_t *c,
1908 unsigned int backtrackCount,
1909 const HBUINT16 backtrack[],
1910 unsigned int inputCount, /* Including the first glyph (not matched) */
1911 const HBUINT16 input[], /* Array of input values--start with second glyph */
1912 unsigned int lookaheadCount,
1913 const HBUINT16 lookahead[],
1914 unsigned int lookupCount,
1915 const LookupRecord lookupRecord[],
1916 ChainContextApplyLookupContext &lookup_context)
1918 unsigned int start_index = 0, match_length = 0, end_index = 0;
1919 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
1920 return match_input (c,
1922 lookup_context.funcs.match, lookup_context.match_data[1],
1923 &match_length, match_positions)
1924 && match_backtrack (c,
1925 backtrackCount, backtrack,
1926 lookup_context.funcs.match, lookup_context.match_data[0],
1928 && match_lookahead (c,
1929 lookaheadCount, lookahead,
1930 lookup_context.funcs.match, lookup_context.match_data[2],
1931 match_length, &end_index)
1932 && (c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index),
1934 inputCount, match_positions,
1935 lookupCount, lookupRecord,
1941 bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const
1943 const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
1944 const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
1945 return chain_context_intersects (glyphs,
1946 backtrack.len, backtrack.arrayZ,
1947 input.lenP1, input.arrayZ,
1948 lookahead.len, lookahead.arrayZ,
1952 void closure (hb_closure_context_t *c,
1953 ChainContextClosureLookupContext &lookup_context) const
1955 const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
1956 const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
1957 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
1958 chain_context_closure_lookup (c,
1959 backtrack.len, backtrack.arrayZ,
1960 input.lenP1, input.arrayZ,
1961 lookahead.len, lookahead.arrayZ,
1962 lookup.len, lookup.arrayZ,
1966 void collect_glyphs (hb_collect_glyphs_context_t *c,
1967 ChainContextCollectGlyphsLookupContext &lookup_context) const
1969 const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
1970 const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
1971 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
1972 chain_context_collect_glyphs_lookup (c,
1973 backtrack.len, backtrack.arrayZ,
1974 input.lenP1, input.arrayZ,
1975 lookahead.len, lookahead.arrayZ,
1976 lookup.len, lookup.arrayZ,
1980 bool would_apply (hb_would_apply_context_t *c,
1981 ChainContextApplyLookupContext &lookup_context) const
1983 const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
1984 const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
1985 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
1986 return chain_context_would_apply_lookup (c,
1987 backtrack.len, backtrack.arrayZ,
1988 input.lenP1, input.arrayZ,
1989 lookahead.len, lookahead.arrayZ, lookup.len,
1990 lookup.arrayZ, lookup_context);
1993 bool apply (hb_ot_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
1996 const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
1997 const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
1998 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
1999 return_trace (chain_context_apply_lookup (c,
2000 backtrack.len, backtrack.arrayZ,
2001 input.lenP1, input.arrayZ,
2002 lookahead.len, lookahead.arrayZ, lookup.len,
2003 lookup.arrayZ, lookup_context));
2006 template<typename Iterator,
2007 hb_requires (hb_is_iterator (Iterator))>
2008 void serialize_array (hb_serialize_context_t *c,
2013 for (const auto g : it)
2021 ChainRule* copy (hb_serialize_context_t *c,
2022 const hb_map_t *backtrack_map,
2023 const hb_map_t *input_map = nullptr,
2024 const hb_map_t *lookahead_map = nullptr) const
2026 TRACE_SERIALIZE (this);
2027 auto *out = c->start_embed (this);
2028 if (unlikely (!out)) return_trace (nullptr);
2030 const hb_map_t *mapping = backtrack_map;
2031 serialize_array (c, backtrack.len, + backtrack.iter ()
2032 | hb_map (mapping));
2034 const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
2035 if (input_map) mapping = input_map;
2036 serialize_array (c, input.lenP1, + input.iter ()
2037 | hb_map (mapping));
2039 const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
2040 if (lookahead_map) mapping = lookahead_map;
2041 serialize_array (c, lookahead.len, + lookahead.iter ()
2042 | hb_map (mapping));
2044 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
2050 bool subset (hb_subset_context_t *c,
2051 const hb_map_t *backtrack_map = nullptr,
2052 const hb_map_t *input_map = nullptr,
2053 const hb_map_t *lookahead_map = nullptr) const
2055 TRACE_SUBSET (this);
2057 const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
2058 const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
2062 const hb_set_t &glyphset = *c->plan->glyphset ();
2063 if (!hb_all (backtrack, glyphset) ||
2064 !hb_all (input, glyphset) ||
2065 !hb_all (lookahead, glyphset))
2066 return_trace (false);
2068 copy (c->serializer, c->plan->glyph_map);
2072 if (!hb_all (backtrack, backtrack_map) ||
2073 !hb_all (input, input_map) ||
2074 !hb_all (lookahead, lookahead_map))
2075 return_trace (false);
2077 copy (c->serializer, backtrack_map, input_map, lookahead_map);
2080 return_trace (true);
2083 bool sanitize (hb_sanitize_context_t *c) const
2085 TRACE_SANITIZE (this);
2086 if (!backtrack.sanitize (c)) return_trace (false);
2087 const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
2088 if (!input.sanitize (c)) return_trace (false);
2089 const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
2090 if (!lookahead.sanitize (c)) return_trace (false);
2091 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
2092 return_trace (lookup.sanitize (c));
2097 backtrack; /* Array of backtracking values
2098 * (to be matched before the input
2100 HeadlessArrayOf<HBUINT16>
2101 inputX; /* Array of input values (start with
2104 lookaheadX; /* Array of lookahead values's (to be
2105 * matched after the input sequence) */
2106 ArrayOf<LookupRecord>
2107 lookupX; /* Array of LookupRecords--in
2110 DEFINE_SIZE_MIN (8);
2115 bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const
2119 | hb_map (hb_add (this))
2120 | hb_map ([&] (const ChainRule &_) { return _.intersects (glyphs, lookup_context); })
2124 void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
2128 | hb_map (hb_add (this))
2129 | hb_apply ([&] (const ChainRule &_) { _.closure (c, lookup_context); })
2133 void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const
2137 | hb_map (hb_add (this))
2138 | hb_apply ([&] (const ChainRule &_) { _.collect_glyphs (c, lookup_context); })
2142 bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
2146 | hb_map (hb_add (this))
2147 | hb_map ([&] (const ChainRule &_) { return _.would_apply (c, lookup_context); })
2152 bool apply (hb_ot_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
2157 | hb_map (hb_add (this))
2158 | hb_map ([&] (const ChainRule &_) { return _.apply (c, lookup_context); })
2164 bool subset (hb_subset_context_t *c,
2165 const hb_map_t *backtrack_klass_map = nullptr,
2166 const hb_map_t *input_klass_map = nullptr,
2167 const hb_map_t *lookahead_klass_map = nullptr) const
2169 TRACE_SUBSET (this);
2171 auto snap = c->serializer->snapshot ();
2172 auto *out = c->serializer->start_embed (*this);
2173 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
2175 for (const OffsetTo<ChainRule>& _ : rule)
2178 auto *o = out->rule.serialize_append (c->serializer);
2179 if (unlikely (!o)) continue;
2181 auto o_snap = c->serializer->snapshot ();
2182 if (!o->serialize_subset (c, _, this, out,
2183 backtrack_klass_map,
2185 lookahead_klass_map))
2188 c->serializer->revert (o_snap);
2192 bool ret = bool (out->rule);
2193 if (!ret) c->serializer->revert (snap);
2198 bool sanitize (hb_sanitize_context_t *c) const
2200 TRACE_SANITIZE (this);
2201 return_trace (rule.sanitize (c, this));
2205 OffsetArrayOf<ChainRule>
2206 rule; /* Array of ChainRule tables
2207 * ordered by preference */
2209 DEFINE_SIZE_ARRAY (2, rule);
2212 struct ChainContextFormat1
2214 bool intersects (const hb_set_t *glyphs) const
2216 struct ChainContextClosureLookupContext lookup_context = {
2218 {nullptr, nullptr, nullptr}
2222 + hb_zip (this+coverage, ruleSet)
2223 | hb_filter (*glyphs, hb_first)
2224 | hb_map (hb_second)
2225 | hb_map (hb_add (this))
2226 | hb_map ([&] (const ChainRuleSet &_) { return _.intersects (glyphs, lookup_context); })
2231 void closure (hb_closure_context_t *c) const
2233 struct ChainContextClosureLookupContext lookup_context = {
2235 {nullptr, nullptr, nullptr}
2238 + hb_zip (this+coverage, ruleSet)
2239 | hb_filter (*c->glyphs, hb_first)
2240 | hb_map (hb_second)
2241 | hb_map (hb_add (this))
2242 | hb_apply ([&] (const ChainRuleSet &_) { _.closure (c, lookup_context); })
2246 void collect_glyphs (hb_collect_glyphs_context_t *c) const
2248 (this+coverage).add_coverage (c->input);
2250 struct ChainContextCollectGlyphsLookupContext lookup_context = {
2252 {nullptr, nullptr, nullptr}
2256 | hb_map (hb_add (this))
2257 | hb_apply ([&] (const ChainRuleSet &_) { _.collect_glyphs (c, lookup_context); })
2261 bool would_apply (hb_would_apply_context_t *c) const
2263 const ChainRuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])];
2264 struct ChainContextApplyLookupContext lookup_context = {
2266 {nullptr, nullptr, nullptr}
2268 return rule_set.would_apply (c, lookup_context);
2271 const Coverage &get_coverage () const { return this+coverage; }
2273 bool apply (hb_ot_apply_context_t *c) const
2276 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
2277 if (likely (index == NOT_COVERED)) return_trace (false);
2279 const ChainRuleSet &rule_set = this+ruleSet[index];
2280 struct ChainContextApplyLookupContext lookup_context = {
2282 {nullptr, nullptr, nullptr}
2284 return_trace (rule_set.apply (c, lookup_context));
2287 bool subset (hb_subset_context_t *c) const
2289 TRACE_SUBSET (this);
2290 const hb_set_t &glyphset = *c->plan->glyphset ();
2291 const hb_map_t &glyph_map = *c->plan->glyph_map;
2293 auto *out = c->serializer->start_embed (*this);
2294 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
2295 out->format = format;
2297 hb_sorted_vector_t<hb_codepoint_t> new_coverage;
2298 + hb_zip (this+coverage, ruleSet)
2299 | hb_filter (glyphset, hb_first)
2300 | hb_filter (subset_offset_array (c, out->ruleSet, this, out), hb_second)
2302 | hb_map (glyph_map)
2303 | hb_sink (new_coverage)
2306 out->coverage.serialize (c->serializer, out)
2307 .serialize (c->serializer, new_coverage.iter ());
2308 return_trace (bool (new_coverage));
2311 bool sanitize (hb_sanitize_context_t *c) const
2313 TRACE_SANITIZE (this);
2314 return_trace (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
2318 HBUINT16 format; /* Format identifier--format = 1 */
2320 coverage; /* Offset to Coverage table--from
2321 * beginning of table */
2322 OffsetArrayOf<ChainRuleSet>
2323 ruleSet; /* Array of ChainRuleSet tables
2324 * ordered by Coverage Index */
2326 DEFINE_SIZE_ARRAY (6, ruleSet);
2329 struct ChainContextFormat2
2331 bool intersects (const hb_set_t *glyphs) const
2333 if (!(this+coverage).intersects (glyphs))
2336 const ClassDef &backtrack_class_def = this+backtrackClassDef;
2337 const ClassDef &input_class_def = this+inputClassDef;
2338 const ClassDef &lookahead_class_def = this+lookaheadClassDef;
2340 struct ChainContextClosureLookupContext lookup_context = {
2342 {&backtrack_class_def,
2344 &lookahead_class_def}
2348 + hb_enumerate (ruleSet)
2349 | hb_map ([&] (const hb_pair_t<unsigned, const OffsetTo<ChainRuleSet> &> p)
2350 { return input_class_def.intersects_class (glyphs, p.first) &&
2351 (this+p.second).intersects (glyphs, lookup_context); })
2355 void closure (hb_closure_context_t *c) const
2357 if (!(this+coverage).intersects (c->glyphs))
2360 const ClassDef &backtrack_class_def = this+backtrackClassDef;
2361 const ClassDef &input_class_def = this+inputClassDef;
2362 const ClassDef &lookahead_class_def = this+lookaheadClassDef;
2364 struct ChainContextClosureLookupContext lookup_context = {
2366 {&backtrack_class_def,
2368 &lookahead_class_def}
2372 + hb_enumerate (ruleSet)
2373 | hb_filter ([&] (unsigned _)
2374 { return input_class_def.intersects_class (c->glyphs, _); },
2376 | hb_map (hb_second)
2377 | hb_map (hb_add (this))
2378 | hb_apply ([&] (const ChainRuleSet &_) { _.closure (c, lookup_context); })
2382 void collect_glyphs (hb_collect_glyphs_context_t *c) const
2384 (this+coverage).add_coverage (c->input);
2386 const ClassDef &backtrack_class_def = this+backtrackClassDef;
2387 const ClassDef &input_class_def = this+inputClassDef;
2388 const ClassDef &lookahead_class_def = this+lookaheadClassDef;
2390 struct ChainContextCollectGlyphsLookupContext lookup_context = {
2392 {&backtrack_class_def,
2394 &lookahead_class_def}
2398 | hb_map (hb_add (this))
2399 | hb_apply ([&] (const ChainRuleSet &_) { _.collect_glyphs (c, lookup_context); })
2403 bool would_apply (hb_would_apply_context_t *c) const
2405 const ClassDef &backtrack_class_def = this+backtrackClassDef;
2406 const ClassDef &input_class_def = this+inputClassDef;
2407 const ClassDef &lookahead_class_def = this+lookaheadClassDef;
2409 unsigned int index = input_class_def.get_class (c->glyphs[0]);
2410 const ChainRuleSet &rule_set = this+ruleSet[index];
2411 struct ChainContextApplyLookupContext lookup_context = {
2413 {&backtrack_class_def,
2415 &lookahead_class_def}
2417 return rule_set.would_apply (c, lookup_context);
2420 const Coverage &get_coverage () const { return this+coverage; }
2422 bool apply (hb_ot_apply_context_t *c) const
2425 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
2426 if (likely (index == NOT_COVERED)) return_trace (false);
2428 const ClassDef &backtrack_class_def = this+backtrackClassDef;
2429 const ClassDef &input_class_def = this+inputClassDef;
2430 const ClassDef &lookahead_class_def = this+lookaheadClassDef;
2432 index = input_class_def.get_class (c->buffer->cur().codepoint);
2433 const ChainRuleSet &rule_set = this+ruleSet[index];
2434 struct ChainContextApplyLookupContext lookup_context = {
2436 {&backtrack_class_def,
2438 &lookahead_class_def}
2440 return_trace (rule_set.apply (c, lookup_context));
2443 bool subset (hb_subset_context_t *c) const
2445 TRACE_SUBSET (this);
2446 auto *out = c->serializer->start_embed (*this);
2447 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
2448 out->format = format;
2449 out->coverage.serialize_subset (c, coverage, this, out);
2451 hb_map_t backtrack_klass_map;
2452 out->backtrackClassDef.serialize_subset (c, backtrackClassDef, this, out, &backtrack_klass_map);
2454 // subset inputClassDef based on glyphs survived in Coverage subsetting
2455 hb_map_t input_klass_map;
2456 out->inputClassDef.serialize_subset (c, inputClassDef, this, out, &input_klass_map);
2458 hb_map_t lookahead_klass_map;
2459 out->lookaheadClassDef.serialize_subset (c, lookaheadClassDef, this, out, &lookahead_klass_map);
2461 hb_vector_t<unsigned> rulesets;
2463 for (const OffsetTo<ChainRuleSet>& _ : + hb_enumerate (ruleSet)
2464 | hb_filter (input_klass_map, hb_first)
2465 | hb_map (hb_second))
2467 auto *o = out->ruleSet.serialize_append (c->serializer);
2473 if (!o->serialize_subset (c, _, this, out,
2474 &backtrack_klass_map,
2476 &lookahead_klass_map))
2480 else rulesets.push (1);
2483 if (!ret) return_trace (ret);
2485 //prune empty trailing ruleSets
2486 unsigned count = rulesets.length;
2487 while (count > 0 && rulesets[count-1] == 0)
2489 out->ruleSet.pop ();
2493 return_trace (bool (out->ruleSet));
2496 bool sanitize (hb_sanitize_context_t *c) const
2498 TRACE_SANITIZE (this);
2499 return_trace (coverage.sanitize (c, this) &&
2500 backtrackClassDef.sanitize (c, this) &&
2501 inputClassDef.sanitize (c, this) &&
2502 lookaheadClassDef.sanitize (c, this) &&
2503 ruleSet.sanitize (c, this));
2507 HBUINT16 format; /* Format identifier--format = 2 */
2509 coverage; /* Offset to Coverage table--from
2510 * beginning of table */
2512 backtrackClassDef; /* Offset to glyph ClassDef table
2513 * containing backtrack sequence
2514 * data--from beginning of table */
2516 inputClassDef; /* Offset to glyph ClassDef
2517 * table containing input sequence
2518 * data--from beginning of table */
2520 lookaheadClassDef; /* Offset to glyph ClassDef table
2521 * containing lookahead sequence
2522 * data--from beginning of table */
2523 OffsetArrayOf<ChainRuleSet>
2524 ruleSet; /* Array of ChainRuleSet tables
2525 * ordered by class */
2527 DEFINE_SIZE_ARRAY (12, ruleSet);
2530 struct ChainContextFormat3
2532 bool intersects (const hb_set_t *glyphs) const
2534 const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
2536 if (!(this+input[0]).intersects (glyphs))
2539 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
2540 struct ChainContextClosureLookupContext lookup_context = {
2541 {intersects_coverage},
2544 return chain_context_intersects (glyphs,
2545 backtrack.len, (const HBUINT16 *) backtrack.arrayZ,
2546 input.len, (const HBUINT16 *) input.arrayZ + 1,
2547 lookahead.len, (const HBUINT16 *) lookahead.arrayZ,
2551 void closure (hb_closure_context_t *c) const
2553 const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
2555 if (!(this+input[0]).intersects (c->glyphs))
2558 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
2559 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
2560 struct ChainContextClosureLookupContext lookup_context = {
2561 {intersects_coverage},
2564 chain_context_closure_lookup (c,
2565 backtrack.len, (const HBUINT16 *) backtrack.arrayZ,
2566 input.len, (const HBUINT16 *) input.arrayZ + 1,
2567 lookahead.len, (const HBUINT16 *) lookahead.arrayZ,
2568 lookup.len, lookup.arrayZ,
2572 void collect_glyphs (hb_collect_glyphs_context_t *c) const
2574 const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
2576 (this+input[0]).add_coverage (c->input);
2578 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
2579 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
2580 struct ChainContextCollectGlyphsLookupContext lookup_context = {
2584 chain_context_collect_glyphs_lookup (c,
2585 backtrack.len, (const HBUINT16 *) backtrack.arrayZ,
2586 input.len, (const HBUINT16 *) input.arrayZ + 1,
2587 lookahead.len, (const HBUINT16 *) lookahead.arrayZ,
2588 lookup.len, lookup.arrayZ,
2592 bool would_apply (hb_would_apply_context_t *c) const
2594 const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
2595 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
2596 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
2597 struct ChainContextApplyLookupContext lookup_context = {
2601 return chain_context_would_apply_lookup (c,
2602 backtrack.len, (const HBUINT16 *) backtrack.arrayZ,
2603 input.len, (const HBUINT16 *) input.arrayZ + 1,
2604 lookahead.len, (const HBUINT16 *) lookahead.arrayZ,
2605 lookup.len, lookup.arrayZ, lookup_context);
2608 const Coverage &get_coverage () const
2610 const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
2611 return this+input[0];
2614 bool apply (hb_ot_apply_context_t *c) const
2617 const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
2619 unsigned int index = (this+input[0]).get_coverage (c->buffer->cur().codepoint);
2620 if (likely (index == NOT_COVERED)) return_trace (false);
2622 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
2623 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
2624 struct ChainContextApplyLookupContext lookup_context = {
2628 return_trace (chain_context_apply_lookup (c,
2629 backtrack.len, (const HBUINT16 *) backtrack.arrayZ,
2630 input.len, (const HBUINT16 *) input.arrayZ + 1,
2631 lookahead.len, (const HBUINT16 *) lookahead.arrayZ,
2632 lookup.len, lookup.arrayZ, lookup_context));
2635 template<typename Iterator,
2636 hb_requires (hb_is_iterator (Iterator))>
2637 bool serialize_coverage_offsets (hb_subset_context_t *c,
2639 const void* src_base,
2640 const void* dst_base) const
2642 TRACE_SERIALIZE (this);
2643 auto *out = c->serializer->start_embed<OffsetArrayOf<Coverage>> ();
2645 if (unlikely (!c->serializer->allocate_size<HBUINT16> (HBUINT16::static_size))) return_trace (false);
2648 | hb_apply (subset_offset_array (c, *out, src_base, dst_base))
2651 return_trace (out->len);
2654 bool subset (hb_subset_context_t *c) const
2656 TRACE_SUBSET (this);
2658 auto *out = c->serializer->start_embed (this);
2659 if (unlikely (!out)) return_trace (false);
2660 if (unlikely (!c->serializer->embed (this->format))) return_trace (false);
2662 if (!serialize_coverage_offsets (c, backtrack.iter (), this, out))
2663 return_trace (false);
2665 const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
2666 if (!serialize_coverage_offsets (c, input.iter (), this, out))
2667 return_trace (false);
2669 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
2670 if (!serialize_coverage_offsets (c, lookahead.iter (), this, out))
2671 return_trace (false);
2673 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
2674 return_trace (c->serializer->copy (lookup));
2677 bool sanitize (hb_sanitize_context_t *c) const
2679 TRACE_SANITIZE (this);
2680 if (!backtrack.sanitize (c, this)) return_trace (false);
2681 const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
2682 if (!input.sanitize (c, this)) return_trace (false);
2683 if (!input.len) return_trace (false); /* To be consistent with Context. */
2684 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
2685 if (!lookahead.sanitize (c, this)) return_trace (false);
2686 const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
2687 return_trace (lookup.sanitize (c));
2691 HBUINT16 format; /* Format identifier--format = 3 */
2692 OffsetArrayOf<Coverage>
2693 backtrack; /* Array of coverage tables
2694 * in backtracking sequence, in glyph
2696 OffsetArrayOf<Coverage>
2697 inputX ; /* Array of coverage
2698 * tables in input sequence, in glyph
2700 OffsetArrayOf<Coverage>
2701 lookaheadX; /* Array of coverage tables
2702 * in lookahead sequence, in glyph
2704 ArrayOf<LookupRecord>
2705 lookupX; /* Array of LookupRecords--in
2708 DEFINE_SIZE_MIN (10);
2713 template <typename context_t, typename ...Ts>
2714 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
2716 TRACE_DISPATCH (this, u.format);
2717 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
2719 case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
2720 case 2: return_trace (c->dispatch (u.format2, hb_forward<Ts> (ds)...));
2721 case 3: return_trace (c->dispatch (u.format3, hb_forward<Ts> (ds)...));
2722 default:return_trace (c->default_return_value ());
2728 HBUINT16 format; /* Format identifier */
2729 ChainContextFormat1 format1;
2730 ChainContextFormat2 format2;
2731 ChainContextFormat3 format3;
2736 template <typename T>
2737 struct ExtensionFormat1
2739 unsigned int get_type () const { return extensionLookupType; }
2741 template <typename X>
2742 const X& get_subtable () const
2743 { return this + CastR<LOffsetTo<typename T::SubTable>> (extensionOffset); }
2745 template <typename context_t, typename ...Ts>
2746 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
2748 TRACE_DISPATCH (this, format);
2749 if (unlikely (!c->may_dispatch (this, this))) return_trace (c->no_dispatch_return_value ());
2750 return_trace (get_subtable<typename T::SubTable> ().dispatch (c, get_type (), hb_forward<Ts> (ds)...));
2753 /* This is called from may_dispatch() above with hb_sanitize_context_t. */
2754 bool sanitize (hb_sanitize_context_t *c) const
2756 TRACE_SANITIZE (this);
2757 return_trace (c->check_struct (this) &&
2758 extensionLookupType != T::SubTable::Extension);
2762 HBUINT16 format; /* Format identifier. Set to 1. */
2763 HBUINT16 extensionLookupType; /* Lookup type of subtable referenced
2764 * by ExtensionOffset (i.e. the
2765 * extension subtable). */
2766 Offset32 extensionOffset; /* Offset to the extension subtable,
2767 * of lookup type subtable. */
2769 DEFINE_SIZE_STATIC (8);
2772 template <typename T>
2775 unsigned int get_type () const
2778 case 1: return u.format1.get_type ();
2782 template <typename X>
2783 const X& get_subtable () const
2786 case 1: return u.format1.template get_subtable<typename T::SubTable> ();
2787 default:return Null(typename T::SubTable);
2791 template <typename context_t, typename ...Ts>
2792 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
2794 TRACE_DISPATCH (this, u.format);
2795 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
2797 case 1: return_trace (u.format1.dispatch (c, hb_forward<Ts> (ds)...));
2798 default:return_trace (c->default_return_value ());
2804 HBUINT16 format; /* Format identifier */
2805 ExtensionFormat1<T> format1;
2814 struct hb_ot_layout_lookup_accelerator_t
2816 template <typename TLookup>
2817 void init (const TLookup &lookup)
2820 lookup.add_coverage (&digest);
2823 OT::hb_get_subtables_context_t c_get_subtables (subtables);
2824 lookup.dispatch (&c_get_subtables);
2826 void fini () { subtables.fini (); }
2828 bool may_have (hb_codepoint_t g) const
2829 { return digest.may_have (g); }
2831 bool apply (hb_ot_apply_context_t *c) const
2833 for (unsigned int i = 0; i < subtables.length; i++)
2834 if (subtables[i].apply (c))
2840 hb_set_digest_t digest;
2841 hb_get_subtables_context_t::array_t subtables;
2846 bool has_data () const { return version.to_int (); }
2847 unsigned int get_script_count () const
2848 { return (this+scriptList).len; }
2849 const Tag& get_script_tag (unsigned int i) const
2850 { return (this+scriptList).get_tag (i); }
2851 unsigned int get_script_tags (unsigned int start_offset,
2852 unsigned int *script_count /* IN/OUT */,
2853 hb_tag_t *script_tags /* OUT */) const
2854 { return (this+scriptList).get_tags (start_offset, script_count, script_tags); }
2855 const Script& get_script (unsigned int i) const
2856 { return (this+scriptList)[i]; }
2857 bool find_script_index (hb_tag_t tag, unsigned int *index) const
2858 { return (this+scriptList).find_index (tag, index); }
2860 unsigned int get_feature_count () const
2861 { return (this+featureList).len; }
2862 hb_tag_t get_feature_tag (unsigned int i) const
2863 { return i == Index::NOT_FOUND_INDEX ? HB_TAG_NONE : (this+featureList).get_tag (i); }
2864 unsigned int get_feature_tags (unsigned int start_offset,
2865 unsigned int *feature_count /* IN/OUT */,
2866 hb_tag_t *feature_tags /* OUT */) const
2867 { return (this+featureList).get_tags (start_offset, feature_count, feature_tags); }
2868 const Feature& get_feature (unsigned int i) const
2869 { return (this+featureList)[i]; }
2870 bool find_feature_index (hb_tag_t tag, unsigned int *index) const
2871 { return (this+featureList).find_index (tag, index); }
2873 unsigned int get_lookup_count () const
2874 { return (this+lookupList).len; }
2875 const Lookup& get_lookup (unsigned int i) const
2876 { return (this+lookupList)[i]; }
2878 bool find_variations_index (const int *coords, unsigned int num_coords,
2879 unsigned int *index) const
2884 return (version.to_int () >= 0x00010001u ? this+featureVars : Null(FeatureVariations))
2885 .find_index (coords, num_coords, index);
2887 const Feature& get_feature_variation (unsigned int feature_index,
2888 unsigned int variations_index) const
2891 if (FeatureVariations::NOT_FOUND_INDEX != variations_index &&
2892 version.to_int () >= 0x00010001u)
2894 const Feature *feature = (this+featureVars).find_substitute (variations_index,
2900 return get_feature (feature_index);
2903 template <typename TLookup>
2904 bool subset (hb_subset_context_t *c) const
2906 TRACE_SUBSET (this);
2907 auto *out = c->serializer->embed (*this);
2908 if (unlikely (!out)) return_trace (false);
2910 out->scriptList.serialize_subset (c, scriptList, this, out);
2911 out->featureList.serialize_subset (c, featureList, this, out);
2913 typedef OffsetListOf<TLookup> TLookupList;
2914 /* TODO Use intersects() to count how many subtables survive? */
2915 CastR<OffsetTo<TLookupList>> (out->lookupList)
2916 .serialize_subset (c,
2917 CastR<OffsetTo<TLookupList>> (lookupList),
2922 if (version.to_int () >= 0x00010001u)
2923 out->featureVars.serialize_copy (c->serializer, featureVars, this, out);
2926 return_trace (true);
2929 unsigned int get_size () const
2932 (version.to_int () >= 0x00010001u ? featureVars.static_size : 0);
2935 template <typename TLookup>
2936 bool sanitize (hb_sanitize_context_t *c) const
2938 TRACE_SANITIZE (this);
2939 typedef OffsetListOf<TLookup> TLookupList;
2940 if (unlikely (!(version.sanitize (c) &&
2941 likely (version.major == 1) &&
2942 scriptList.sanitize (c, this) &&
2943 featureList.sanitize (c, this) &&
2944 CastR<OffsetTo<TLookupList>> (lookupList).sanitize (c, this))))
2945 return_trace (false);
2948 if (unlikely (!(version.to_int () < 0x00010001u || featureVars.sanitize (c, this))))
2949 return_trace (false);
2952 return_trace (true);
2955 template <typename T>
2956 struct accelerator_t
2958 void init (hb_face_t *face)
2960 this->table = hb_sanitize_context_t().reference_table<T> (face);
2961 if (unlikely (this->table->is_blacklisted (this->table.get_blob (), face)))
2963 hb_blob_destroy (this->table.get_blob ());
2964 this->table = hb_blob_get_empty ();
2967 this->lookup_count = table->get_lookup_count ();
2969 this->accels = (hb_ot_layout_lookup_accelerator_t *) calloc (this->lookup_count, sizeof (hb_ot_layout_lookup_accelerator_t));
2970 if (unlikely (!this->accels))
2971 this->lookup_count = 0;
2973 for (unsigned int i = 0; i < this->lookup_count; i++)
2974 this->accels[i].init (table->get_lookup (i));
2979 for (unsigned int i = 0; i < this->lookup_count; i++)
2980 this->accels[i].fini ();
2981 free (this->accels);
2982 this->table.destroy ();
2985 hb_blob_ptr_t<T> table;
2986 unsigned int lookup_count;
2987 hb_ot_layout_lookup_accelerator_t *accels;
2991 FixedVersion<>version; /* Version of the GSUB/GPOS table--initially set
2993 OffsetTo<ScriptList>
2994 scriptList; /* ScriptList table */
2995 OffsetTo<FeatureList>
2996 featureList; /* FeatureList table */
2997 OffsetTo<LookupList>
2998 lookupList; /* LookupList table */
2999 LOffsetTo<FeatureVariations>
3000 featureVars; /* Offset to Feature Variations
3001 table--from beginning of table
3002 * (may be NULL). Introduced
3003 * in version 0x00010001. */
3005 DEFINE_SIZE_MIN (10);
3009 } /* namespace OT */
3012 #endif /* HB_OT_LAYOUT_GSUBGPOS_HH */