1 /*
2 * Copyright © 2007,2008,2009,2010 Red Hat, Inc.
3 * Copyright © 2010,2012,2013 Google, Inc.
4 *
5 * This is part of HarfBuzz, a text shaping library.
6 *
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.
12 *
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
17 * DAMAGE.
18 *
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.
24 *
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
27 */
28
29 #ifndef HB_OT_LAYOUT_GSUB_TABLE_HH
30 #define HB_OT_LAYOUT_GSUB_TABLE_HH
31
32 #include "hb-ot-layout-gsubgpos.hh"
33
34
35 namespace OT {
36
37 typedef hb_pair_t<hb_codepoint_t, hb_codepoint_t> hb_codepoint_pair_t;
38
39 template<typename Iterator>
40 static void SingleSubst_serialize (hb_serialize_context_t *c,
41 Iterator it);
42
43
44 struct SingleSubstFormat1
45 {
intersectsOT::SingleSubstFormat146 bool intersects (const hb_set_t *glyphs) const
47 { return (this+coverage).intersects (glyphs); }
48
may_have_non_1to1OT::SingleSubstFormat149 bool may_have_non_1to1 () const
50 { return false; }
51
closureOT::SingleSubstFormat152 void closure (hb_closure_context_t *c) const
53 {
54 unsigned d = deltaGlyphID;
55
56 + hb_iter (this+coverage)
57 | hb_filter (c->parent_active_glyphs ())
58 | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; })
59 | hb_sink (c->output)
60 ;
61
62 }
63
closure_lookupsOT::SingleSubstFormat164 void closure_lookups (hb_closure_lookups_context_t *c) const {}
65
collect_glyphsOT::SingleSubstFormat166 void collect_glyphs (hb_collect_glyphs_context_t *c) const
67 {
68 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
69 unsigned d = deltaGlyphID;
70 + hb_iter (this+coverage)
71 | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; })
72 | hb_sink (c->output)
73 ;
74 }
75
get_coverageOT::SingleSubstFormat176 const Coverage &get_coverage () const { return this+coverage; }
77
would_applyOT::SingleSubstFormat178 bool would_apply (hb_would_apply_context_t *c) const
79 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
80
applyOT::SingleSubstFormat181 bool apply (hb_ot_apply_context_t *c) const
82 {
83 TRACE_APPLY (this);
84 hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
85 unsigned int index = (this+coverage).get_coverage (glyph_id);
86 if (likely (index == NOT_COVERED)) return_trace (false);
87
88 /* According to the Adobe Annotated OpenType Suite, result is always
89 * limited to 16bit. */
90 glyph_id = (glyph_id + deltaGlyphID) & 0xFFFFu;
91 c->replace_glyph (glyph_id);
92
93 return_trace (true);
94 }
95
96 template<typename Iterator,
97 hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
serializeOT::SingleSubstFormat198 bool serialize (hb_serialize_context_t *c,
99 Iterator glyphs,
100 unsigned delta)
101 {
102 TRACE_SERIALIZE (this);
103 if (unlikely (!c->extend_min (this))) return_trace (false);
104 if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false);
105 c->check_assign (deltaGlyphID, delta, HB_SERIALIZE_ERROR_INT_OVERFLOW);
106 return_trace (true);
107 }
108
subsetOT::SingleSubstFormat1109 bool subset (hb_subset_context_t *c) const
110 {
111 TRACE_SUBSET (this);
112 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
113 const hb_map_t &glyph_map = *c->plan->glyph_map;
114
115 hb_codepoint_t delta = deltaGlyphID;
116
117 auto it =
118 + hb_iter (this+coverage)
119 | hb_filter (glyphset)
120 | hb_map_retains_sorting ([&] (hb_codepoint_t g) {
121 return hb_codepoint_pair_t (g,
122 (g + delta) & 0xFFFF); })
123 | hb_filter (glyphset, hb_second)
124 | hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t
125 { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
126 ;
127
128 bool ret = bool (it);
129 SingleSubst_serialize (c->serializer, it);
130 return_trace (ret);
131 }
132
sanitizeOT::SingleSubstFormat1133 bool sanitize (hb_sanitize_context_t *c) const
134 {
135 TRACE_SANITIZE (this);
136 return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
137 }
138
139 protected:
140 HBUINT16 format; /* Format identifier--format = 1 */
141 Offset16To<Coverage>
142 coverage; /* Offset to Coverage table--from
143 * beginning of Substitution table */
144 HBUINT16 deltaGlyphID; /* Add to original GlyphID to get
145 * substitute GlyphID, modulo 0x10000 */
146 public:
147 DEFINE_SIZE_STATIC (6);
148 };
149
150 struct SingleSubstFormat2
151 {
intersectsOT::SingleSubstFormat2152 bool intersects (const hb_set_t *glyphs) const
153 { return (this+coverage).intersects (glyphs); }
154
may_have_non_1to1OT::SingleSubstFormat2155 bool may_have_non_1to1 () const
156 { return false; }
157
closureOT::SingleSubstFormat2158 void closure (hb_closure_context_t *c) const
159 {
160 + hb_zip (this+coverage, substitute)
161 | hb_filter (c->parent_active_glyphs (), hb_first)
162 | hb_map (hb_second)
163 | hb_sink (c->output)
164 ;
165
166 }
167
closure_lookupsOT::SingleSubstFormat2168 void closure_lookups (hb_closure_lookups_context_t *c) const {}
169
collect_glyphsOT::SingleSubstFormat2170 void collect_glyphs (hb_collect_glyphs_context_t *c) const
171 {
172 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
173 + hb_zip (this+coverage, substitute)
174 | hb_map (hb_second)
175 | hb_sink (c->output)
176 ;
177 }
178
get_coverageOT::SingleSubstFormat2179 const Coverage &get_coverage () const { return this+coverage; }
180
would_applyOT::SingleSubstFormat2181 bool would_apply (hb_would_apply_context_t *c) const
182 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
183
applyOT::SingleSubstFormat2184 bool apply (hb_ot_apply_context_t *c) const
185 {
186 TRACE_APPLY (this);
187 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
188 if (likely (index == NOT_COVERED)) return_trace (false);
189
190 if (unlikely (index >= substitute.len)) return_trace (false);
191
192 c->replace_glyph (substitute[index]);
193
194 return_trace (true);
195 }
196
197 template<typename Iterator,
198 hb_requires (hb_is_sorted_source_of (Iterator,
199 hb_codepoint_pair_t))>
serializeOT::SingleSubstFormat2200 bool serialize (hb_serialize_context_t *c,
201 Iterator it)
202 {
203 TRACE_SERIALIZE (this);
204 auto substitutes =
205 + it
206 | hb_map (hb_second)
207 ;
208 auto glyphs =
209 + it
210 | hb_map_retains_sorting (hb_first)
211 ;
212 if (unlikely (!c->extend_min (this))) return_trace (false);
213 if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false);
214 if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false);
215 return_trace (true);
216 }
217
subsetOT::SingleSubstFormat2218 bool subset (hb_subset_context_t *c) const
219 {
220 TRACE_SUBSET (this);
221 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
222 const hb_map_t &glyph_map = *c->plan->glyph_map;
223
224 auto it =
225 + hb_zip (this+coverage, substitute)
226 | hb_filter (glyphset, hb_first)
227 | hb_filter (glyphset, hb_second)
228 | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const HBGlyphID16 &> p) -> hb_codepoint_pair_t
229 { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
230 ;
231
232 bool ret = bool (it);
233 SingleSubst_serialize (c->serializer, it);
234 return_trace (ret);
235 }
236
sanitizeOT::SingleSubstFormat2237 bool sanitize (hb_sanitize_context_t *c) const
238 {
239 TRACE_SANITIZE (this);
240 return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
241 }
242
243 protected:
244 HBUINT16 format; /* Format identifier--format = 2 */
245 Offset16To<Coverage>
246 coverage; /* Offset to Coverage table--from
247 * beginning of Substitution table */
248 Array16Of<HBGlyphID16>
249 substitute; /* Array of substitute
250 * GlyphIDs--ordered by Coverage Index */
251 public:
252 DEFINE_SIZE_ARRAY (6, substitute);
253 };
254
255 struct SingleSubst
256 {
257
258 template<typename Iterator,
259 hb_requires (hb_is_sorted_source_of (Iterator,
260 const hb_codepoint_pair_t))>
serializeOT::SingleSubst261 bool serialize (hb_serialize_context_t *c,
262 Iterator glyphs)
263 {
264 TRACE_SERIALIZE (this);
265 if (unlikely (!c->extend_min (u.format))) return_trace (false);
266 unsigned format = 2;
267 unsigned delta = 0;
268 if (glyphs)
269 {
270 format = 1;
271 auto get_delta = [=] (hb_codepoint_pair_t _)
272 { return (unsigned) (_.second - _.first) & 0xFFFF; };
273 delta = get_delta (*glyphs);
274 if (!hb_all (++(+glyphs), delta, get_delta)) format = 2;
275 }
276 u.format = format;
277 switch (u.format) {
278 case 1: return_trace (u.format1.serialize (c,
279 + glyphs
280 | hb_map_retains_sorting (hb_first),
281 delta));
282 case 2: return_trace (u.format2.serialize (c, glyphs));
283 default:return_trace (false);
284 }
285 }
286
287 template <typename context_t, typename ...Ts>
dispatchOT::SingleSubst288 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
289 {
290 TRACE_DISPATCH (this, u.format);
291 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
292 switch (u.format) {
293 case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
294 case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
295 default:return_trace (c->default_return_value ());
296 }
297 }
298
299 protected:
300 union {
301 HBUINT16 format; /* Format identifier */
302 SingleSubstFormat1 format1;
303 SingleSubstFormat2 format2;
304 } u;
305 };
306
307 template<typename Iterator>
308 static void
SingleSubst_serialize(hb_serialize_context_t * c,Iterator it)309 SingleSubst_serialize (hb_serialize_context_t *c,
310 Iterator it)
311 { c->start_embed<SingleSubst> ()->serialize (c, it); }
312
313 struct Sequence
314 {
intersectsOT::Sequence315 bool intersects (const hb_set_t *glyphs) const
316 { return hb_all (substitute, glyphs); }
317
closureOT::Sequence318 void closure (hb_closure_context_t *c) const
319 { c->output->add_array (substitute.arrayZ, substitute.len); }
320
collect_glyphsOT::Sequence321 void collect_glyphs (hb_collect_glyphs_context_t *c) const
322 { c->output->add_array (substitute.arrayZ, substitute.len); }
323
applyOT::Sequence324 bool apply (hb_ot_apply_context_t *c) const
325 {
326 TRACE_APPLY (this);
327 unsigned int count = substitute.len;
328
329 /* Special-case to make it in-place and not consider this
330 * as a "multiplied" substitution. */
331 if (unlikely (count == 1))
332 {
333 c->replace_glyph (substitute.arrayZ[0]);
334 return_trace (true);
335 }
336 /* Spec disallows this, but Uniscribe allows it.
337 * https://github.com/harfbuzz/harfbuzz/issues/253 */
338 else if (unlikely (count == 0))
339 {
340 c->buffer->delete_glyph ();
341 return_trace (true);
342 }
343
344 unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ?
345 HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
346 unsigned lig_id = _hb_glyph_info_get_lig_id (&c->buffer->cur());
347
348 for (unsigned int i = 0; i < count; i++)
349 {
350 /* If is attached to a ligature, don't disturb that.
351 * https://github.com/harfbuzz/harfbuzz/issues/3069 */
352 if (!lig_id)
353 _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i);
354 c->output_glyph_for_component (substitute.arrayZ[i], klass);
355 }
356 c->buffer->skip_glyph ();
357
358 return_trace (true);
359 }
360
361 template <typename Iterator,
362 hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
serializeOT::Sequence363 bool serialize (hb_serialize_context_t *c,
364 Iterator subst)
365 {
366 TRACE_SERIALIZE (this);
367 return_trace (substitute.serialize (c, subst));
368 }
369
subsetOT::Sequence370 bool subset (hb_subset_context_t *c) const
371 {
372 TRACE_SUBSET (this);
373 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
374 const hb_map_t &glyph_map = *c->plan->glyph_map;
375
376 if (!intersects (&glyphset)) return_trace (false);
377
378 auto it =
379 + hb_iter (substitute)
380 | hb_map (glyph_map)
381 ;
382
383 auto *out = c->serializer->start_embed (*this);
384 return_trace (out->serialize (c->serializer, it));
385 }
386
sanitizeOT::Sequence387 bool sanitize (hb_sanitize_context_t *c) const
388 {
389 TRACE_SANITIZE (this);
390 return_trace (substitute.sanitize (c));
391 }
392
393 protected:
394 Array16Of<HBGlyphID16>
395 substitute; /* String of GlyphIDs to substitute */
396 public:
397 DEFINE_SIZE_ARRAY (2, substitute);
398 };
399
400 struct MultipleSubstFormat1
401 {
intersectsOT::MultipleSubstFormat1402 bool intersects (const hb_set_t *glyphs) const
403 { return (this+coverage).intersects (glyphs); }
404
may_have_non_1to1OT::MultipleSubstFormat1405 bool may_have_non_1to1 () const
406 { return true; }
407
closureOT::MultipleSubstFormat1408 void closure (hb_closure_context_t *c) const
409 {
410 + hb_zip (this+coverage, sequence)
411 | hb_filter (c->parent_active_glyphs (), hb_first)
412 | hb_map (hb_second)
413 | hb_map (hb_add (this))
414 | hb_apply ([c] (const Sequence &_) { _.closure (c); })
415 ;
416 }
417
closure_lookupsOT::MultipleSubstFormat1418 void closure_lookups (hb_closure_lookups_context_t *c) const {}
419
collect_glyphsOT::MultipleSubstFormat1420 void collect_glyphs (hb_collect_glyphs_context_t *c) const
421 {
422 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
423 + hb_zip (this+coverage, sequence)
424 | hb_map (hb_second)
425 | hb_map (hb_add (this))
426 | hb_apply ([c] (const Sequence &_) { _.collect_glyphs (c); })
427 ;
428 }
429
get_coverageOT::MultipleSubstFormat1430 const Coverage &get_coverage () const { return this+coverage; }
431
would_applyOT::MultipleSubstFormat1432 bool would_apply (hb_would_apply_context_t *c) const
433 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
434
applyOT::MultipleSubstFormat1435 bool apply (hb_ot_apply_context_t *c) const
436 {
437 TRACE_APPLY (this);
438
439 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
440 if (likely (index == NOT_COVERED)) return_trace (false);
441
442 return_trace ((this+sequence[index]).apply (c));
443 }
444
serializeOT::MultipleSubstFormat1445 bool serialize (hb_serialize_context_t *c,
446 hb_sorted_array_t<const HBGlyphID16> glyphs,
447 hb_array_t<const unsigned int> substitute_len_list,
448 hb_array_t<const HBGlyphID16> substitute_glyphs_list)
449 {
450 TRACE_SERIALIZE (this);
451 if (unlikely (!c->extend_min (this))) return_trace (false);
452 if (unlikely (!sequence.serialize (c, glyphs.length))) return_trace (false);
453 for (unsigned int i = 0; i < glyphs.length; i++)
454 {
455 unsigned int substitute_len = substitute_len_list[i];
456 if (unlikely (!sequence[i]
457 .serialize_serialize (c, substitute_glyphs_list.sub_array (0, substitute_len))))
458 return_trace (false);
459 substitute_glyphs_list += substitute_len;
460 }
461 return_trace (coverage.serialize_serialize (c, glyphs));
462 }
463
subsetOT::MultipleSubstFormat1464 bool subset (hb_subset_context_t *c) const
465 {
466 TRACE_SUBSET (this);
467 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
468 const hb_map_t &glyph_map = *c->plan->glyph_map;
469
470 auto *out = c->serializer->start_embed (*this);
471 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
472 out->format = format;
473
474 hb_sorted_vector_t<hb_codepoint_t> new_coverage;
475 + hb_zip (this+coverage, sequence)
476 | hb_filter (glyphset, hb_first)
477 | hb_filter (subset_offset_array (c, out->sequence, this), hb_second)
478 | hb_map (hb_first)
479 | hb_map (glyph_map)
480 | hb_sink (new_coverage)
481 ;
482 out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
483 return_trace (bool (new_coverage));
484 }
485
sanitizeOT::MultipleSubstFormat1486 bool sanitize (hb_sanitize_context_t *c) const
487 {
488 TRACE_SANITIZE (this);
489 return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this));
490 }
491
492 protected:
493 HBUINT16 format; /* Format identifier--format = 1 */
494 Offset16To<Coverage>
495 coverage; /* Offset to Coverage table--from
496 * beginning of Substitution table */
497 Array16OfOffset16To<Sequence>
498 sequence; /* Array of Sequence tables
499 * ordered by Coverage Index */
500 public:
501 DEFINE_SIZE_ARRAY (6, sequence);
502 };
503
504 struct MultipleSubst
505 {
serializeOT::MultipleSubst506 bool serialize (hb_serialize_context_t *c,
507 hb_sorted_array_t<const HBGlyphID16> glyphs,
508 hb_array_t<const unsigned int> substitute_len_list,
509 hb_array_t<const HBGlyphID16> substitute_glyphs_list)
510 {
511 TRACE_SERIALIZE (this);
512 if (unlikely (!c->extend_min (u.format))) return_trace (false);
513 unsigned int format = 1;
514 u.format = format;
515 switch (u.format) {
516 case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, substitute_glyphs_list));
517 default:return_trace (false);
518 }
519 }
520
521 template <typename context_t, typename ...Ts>
dispatchOT::MultipleSubst522 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
523 {
524 TRACE_DISPATCH (this, u.format);
525 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
526 switch (u.format) {
527 case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
528 default:return_trace (c->default_return_value ());
529 }
530 }
531
532 protected:
533 union {
534 HBUINT16 format; /* Format identifier */
535 MultipleSubstFormat1 format1;
536 } u;
537 };
538
539 struct AlternateSet
540 {
intersectsOT::AlternateSet541 bool intersects (const hb_set_t *glyphs) const
542 { return hb_any (alternates, glyphs); }
543
closureOT::AlternateSet544 void closure (hb_closure_context_t *c) const
545 { c->output->add_array (alternates.arrayZ, alternates.len); }
546
collect_glyphsOT::AlternateSet547 void collect_glyphs (hb_collect_glyphs_context_t *c) const
548 { c->output->add_array (alternates.arrayZ, alternates.len); }
549
applyOT::AlternateSet550 bool apply (hb_ot_apply_context_t *c) const
551 {
552 TRACE_APPLY (this);
553 unsigned int count = alternates.len;
554
555 if (unlikely (!count)) return_trace (false);
556
557 hb_mask_t glyph_mask = c->buffer->cur().mask;
558 hb_mask_t lookup_mask = c->lookup_mask;
559
560 /* Note: This breaks badly if two features enabled this lookup together. */
561 unsigned int shift = hb_ctz (lookup_mask);
562 unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
563
564 /* If alt_index is MAX_VALUE, randomize feature if it is the rand feature. */
565 if (alt_index == HB_OT_MAP_MAX_VALUE && c->random)
566 {
567 /* Maybe we can do better than unsafe-to-break all; but since we are
568 * changing random state, it would be hard to track that. Good 'nough. */
569 c->buffer->unsafe_to_break (0, c->buffer->len);
570 alt_index = c->random_number () % count + 1;
571 }
572
573 if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);
574
575 c->replace_glyph (alternates[alt_index - 1]);
576
577 return_trace (true);
578 }
579
580 unsigned
get_alternatesOT::AlternateSet581 get_alternates (unsigned start_offset,
582 unsigned *alternate_count /* IN/OUT. May be NULL. */,
583 hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
584 {
585 if (alternates.len && alternate_count)
586 {
587 + alternates.sub_array (start_offset, alternate_count)
588 | hb_sink (hb_array (alternate_glyphs, *alternate_count))
589 ;
590 }
591 return alternates.len;
592 }
593
594 template <typename Iterator,
595 hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
serializeOT::AlternateSet596 bool serialize (hb_serialize_context_t *c,
597 Iterator alts)
598 {
599 TRACE_SERIALIZE (this);
600 return_trace (alternates.serialize (c, alts));
601 }
602
subsetOT::AlternateSet603 bool subset (hb_subset_context_t *c) const
604 {
605 TRACE_SUBSET (this);
606 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
607 const hb_map_t &glyph_map = *c->plan->glyph_map;
608
609 auto it =
610 + hb_iter (alternates)
611 | hb_filter (glyphset)
612 | hb_map (glyph_map)
613 ;
614
615 auto *out = c->serializer->start_embed (*this);
616 return_trace (out->serialize (c->serializer, it) &&
617 out->alternates);
618 }
619
sanitizeOT::AlternateSet620 bool sanitize (hb_sanitize_context_t *c) const
621 {
622 TRACE_SANITIZE (this);
623 return_trace (alternates.sanitize (c));
624 }
625
626 protected:
627 Array16Of<HBGlyphID16>
628 alternates; /* Array of alternate GlyphIDs--in
629 * arbitrary order */
630 public:
631 DEFINE_SIZE_ARRAY (2, alternates);
632 };
633
634 struct AlternateSubstFormat1
635 {
intersectsOT::AlternateSubstFormat1636 bool intersects (const hb_set_t *glyphs) const
637 { return (this+coverage).intersects (glyphs); }
638
may_have_non_1to1OT::AlternateSubstFormat1639 bool may_have_non_1to1 () const
640 { return false; }
641
closureOT::AlternateSubstFormat1642 void closure (hb_closure_context_t *c) const
643 {
644 + hb_zip (this+coverage, alternateSet)
645 | hb_filter (c->parent_active_glyphs (), hb_first)
646 | hb_map (hb_second)
647 | hb_map (hb_add (this))
648 | hb_apply ([c] (const AlternateSet &_) { _.closure (c); })
649 ;
650
651 }
652
closure_lookupsOT::AlternateSubstFormat1653 void closure_lookups (hb_closure_lookups_context_t *c) const {}
654
collect_glyphsOT::AlternateSubstFormat1655 void collect_glyphs (hb_collect_glyphs_context_t *c) const
656 {
657 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
658 + hb_zip (this+coverage, alternateSet)
659 | hb_map (hb_second)
660 | hb_map (hb_add (this))
661 | hb_apply ([c] (const AlternateSet &_) { _.collect_glyphs (c); })
662 ;
663 }
664
get_coverageOT::AlternateSubstFormat1665 const Coverage &get_coverage () const { return this+coverage; }
666
would_applyOT::AlternateSubstFormat1667 bool would_apply (hb_would_apply_context_t *c) const
668 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
669
670 unsigned
get_glyph_alternatesOT::AlternateSubstFormat1671 get_glyph_alternates (hb_codepoint_t gid,
672 unsigned start_offset,
673 unsigned *alternate_count /* IN/OUT. May be NULL. */,
674 hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
675 { return (this+alternateSet[(this+coverage).get_coverage (gid)])
676 .get_alternates (start_offset, alternate_count, alternate_glyphs); }
677
applyOT::AlternateSubstFormat1678 bool apply (hb_ot_apply_context_t *c) const
679 {
680 TRACE_APPLY (this);
681
682 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
683 if (likely (index == NOT_COVERED)) return_trace (false);
684
685 return_trace ((this+alternateSet[index]).apply (c));
686 }
687
serializeOT::AlternateSubstFormat1688 bool serialize (hb_serialize_context_t *c,
689 hb_sorted_array_t<const HBGlyphID16> glyphs,
690 hb_array_t<const unsigned int> alternate_len_list,
691 hb_array_t<const HBGlyphID16> alternate_glyphs_list)
692 {
693 TRACE_SERIALIZE (this);
694 if (unlikely (!c->extend_min (this))) return_trace (false);
695 if (unlikely (!alternateSet.serialize (c, glyphs.length))) return_trace (false);
696 for (unsigned int i = 0; i < glyphs.length; i++)
697 {
698 unsigned int alternate_len = alternate_len_list[i];
699 if (unlikely (!alternateSet[i]
700 .serialize_serialize (c, alternate_glyphs_list.sub_array (0, alternate_len))))
701 return_trace (false);
702 alternate_glyphs_list += alternate_len;
703 }
704 return_trace (coverage.serialize_serialize (c, glyphs));
705 }
706
subsetOT::AlternateSubstFormat1707 bool subset (hb_subset_context_t *c) const
708 {
709 TRACE_SUBSET (this);
710 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
711 const hb_map_t &glyph_map = *c->plan->glyph_map;
712
713 auto *out = c->serializer->start_embed (*this);
714 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
715 out->format = format;
716
717 hb_sorted_vector_t<hb_codepoint_t> new_coverage;
718 + hb_zip (this+coverage, alternateSet)
719 | hb_filter (glyphset, hb_first)
720 | hb_filter (subset_offset_array (c, out->alternateSet, this), hb_second)
721 | hb_map (hb_first)
722 | hb_map (glyph_map)
723 | hb_sink (new_coverage)
724 ;
725 out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
726 return_trace (bool (new_coverage));
727 }
728
sanitizeOT::AlternateSubstFormat1729 bool sanitize (hb_sanitize_context_t *c) const
730 {
731 TRACE_SANITIZE (this);
732 return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
733 }
734
735 protected:
736 HBUINT16 format; /* Format identifier--format = 1 */
737 Offset16To<Coverage>
738 coverage; /* Offset to Coverage table--from
739 * beginning of Substitution table */
740 Array16OfOffset16To<AlternateSet>
741 alternateSet; /* Array of AlternateSet tables
742 * ordered by Coverage Index */
743 public:
744 DEFINE_SIZE_ARRAY (6, alternateSet);
745 };
746
747 struct AlternateSubst
748 {
serializeOT::AlternateSubst749 bool serialize (hb_serialize_context_t *c,
750 hb_sorted_array_t<const HBGlyphID16> glyphs,
751 hb_array_t<const unsigned int> alternate_len_list,
752 hb_array_t<const HBGlyphID16> alternate_glyphs_list)
753 {
754 TRACE_SERIALIZE (this);
755 if (unlikely (!c->extend_min (u.format))) return_trace (false);
756 unsigned int format = 1;
757 u.format = format;
758 switch (u.format) {
759 case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list));
760 default:return_trace (false);
761 }
762 }
763
764 template <typename context_t, typename ...Ts>
dispatchOT::AlternateSubst765 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
766 {
767 TRACE_DISPATCH (this, u.format);
768 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
769 switch (u.format) {
770 case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
771 default:return_trace (c->default_return_value ());
772 }
773 }
774
775 protected:
776 union {
777 HBUINT16 format; /* Format identifier */
778 AlternateSubstFormat1 format1;
779 } u;
780 };
781
782
783 struct Ligature
784 {
intersectsOT::Ligature785 bool intersects (const hb_set_t *glyphs) const
786 { return hb_all (component, glyphs); }
787
closureOT::Ligature788 void closure (hb_closure_context_t *c) const
789 {
790 if (!intersects (c->glyphs)) return;
791 c->output->add (ligGlyph);
792 }
793
collect_glyphsOT::Ligature794 void collect_glyphs (hb_collect_glyphs_context_t *c) const
795 {
796 c->input->add_array (component.arrayZ, component.get_length ());
797 c->output->add (ligGlyph);
798 }
799
would_applyOT::Ligature800 bool would_apply (hb_would_apply_context_t *c) const
801 {
802 if (c->len != component.lenP1)
803 return false;
804
805 for (unsigned int i = 1; i < c->len; i++)
806 if (likely (c->glyphs[i] != component[i]))
807 return false;
808
809 return true;
810 }
811
applyOT::Ligature812 bool apply (hb_ot_apply_context_t *c) const
813 {
814 TRACE_APPLY (this);
815 unsigned int count = component.lenP1;
816
817 if (unlikely (!count)) return_trace (false);
818
819 /* Special-case to make it in-place and not consider this
820 * as a "ligated" substitution. */
821 if (unlikely (count == 1))
822 {
823 c->replace_glyph (ligGlyph);
824 return_trace (true);
825 }
826
827 unsigned int total_component_count = 0;
828
829 unsigned int match_end = 0;
830 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
831
832 if (likely (!match_input (c, count,
833 &component[1],
834 match_glyph,
835 nullptr,
836 &match_end,
837 match_positions,
838 &total_component_count)))
839 {
840 c->buffer->unsafe_to_concat (c->buffer->idx, match_end);
841 return_trace (false);
842 }
843
844 ligate_input (c,
845 count,
846 match_positions,
847 match_end,
848 ligGlyph,
849 total_component_count);
850
851 return_trace (true);
852 }
853
854 template <typename Iterator,
855 hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
serializeOT::Ligature856 bool serialize (hb_serialize_context_t *c,
857 hb_codepoint_t ligature,
858 Iterator components /* Starting from second */)
859 {
860 TRACE_SERIALIZE (this);
861 if (unlikely (!c->extend_min (this))) return_trace (false);
862 ligGlyph = ligature;
863 if (unlikely (!component.serialize (c, components))) return_trace (false);
864 return_trace (true);
865 }
866
subsetOT::Ligature867 bool subset (hb_subset_context_t *c, unsigned coverage_idx) const
868 {
869 TRACE_SUBSET (this);
870 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
871 const hb_map_t &glyph_map = *c->plan->glyph_map;
872
873 if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false);
874 // Ensure Coverage table is always packed after this.
875 c->serializer->add_virtual_link (coverage_idx);
876
877 auto it =
878 + hb_iter (component)
879 | hb_map (glyph_map)
880 ;
881
882 auto *out = c->serializer->start_embed (*this);
883 return_trace (out->serialize (c->serializer,
884 glyph_map[ligGlyph],
885 it));
886 }
887
888 public:
sanitizeOT::Ligature889 bool sanitize (hb_sanitize_context_t *c) const
890 {
891 TRACE_SANITIZE (this);
892 return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
893 }
894
895 protected:
896 HBGlyphID16 ligGlyph; /* GlyphID of ligature to substitute */
897 HeadlessArrayOf<HBGlyphID16>
898 component; /* Array of component GlyphIDs--start
899 * with the second component--ordered
900 * in writing direction */
901 public:
902 DEFINE_SIZE_ARRAY (4, component);
903 };
904
905 struct LigatureSet
906 {
intersectsOT::LigatureSet907 bool intersects (const hb_set_t *glyphs) const
908 {
909 return
910 + hb_iter (ligature)
911 | hb_map (hb_add (this))
912 | hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); })
913 | hb_any
914 ;
915 }
916
closureOT::LigatureSet917 void closure (hb_closure_context_t *c) const
918 {
919 + hb_iter (ligature)
920 | hb_map (hb_add (this))
921 | hb_apply ([c] (const Ligature &_) { _.closure (c); })
922 ;
923 }
924
collect_glyphsOT::LigatureSet925 void collect_glyphs (hb_collect_glyphs_context_t *c) const
926 {
927 + hb_iter (ligature)
928 | hb_map (hb_add (this))
929 | hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); })
930 ;
931 }
932
would_applyOT::LigatureSet933 bool would_apply (hb_would_apply_context_t *c) const
934 {
935 return
936 + hb_iter (ligature)
937 | hb_map (hb_add (this))
938 | hb_map ([c] (const Ligature &_) { return _.would_apply (c); })
939 | hb_any
940 ;
941 }
942
applyOT::LigatureSet943 bool apply (hb_ot_apply_context_t *c) const
944 {
945 TRACE_APPLY (this);
946 unsigned int num_ligs = ligature.len;
947 for (unsigned int i = 0; i < num_ligs; i++)
948 {
949 const Ligature &lig = this+ligature[i];
950 if (lig.apply (c)) return_trace (true);
951 }
952
953 return_trace (false);
954 }
955
serializeOT::LigatureSet956 bool serialize (hb_serialize_context_t *c,
957 hb_array_t<const HBGlyphID16> ligatures,
958 hb_array_t<const unsigned int> component_count_list,
959 hb_array_t<const HBGlyphID16> &component_list /* Starting from second for each ligature */)
960 {
961 TRACE_SERIALIZE (this);
962 if (unlikely (!c->extend_min (this))) return_trace (false);
963 if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false);
964 for (unsigned int i = 0; i < ligatures.length; i++)
965 {
966 unsigned int component_count = (unsigned) hb_max ((int) component_count_list[i] - 1, 0);
967 if (unlikely (!ligature[i].serialize_serialize (c,
968 ligatures[i],
969 component_list.sub_array (0, component_count))))
970 return_trace (false);
971 component_list += component_count;
972 }
973 return_trace (true);
974 }
975
subsetOT::LigatureSet976 bool subset (hb_subset_context_t *c, unsigned coverage_idx) const
977 {
978 TRACE_SUBSET (this);
979 auto *out = c->serializer->start_embed (*this);
980 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
981
982 + hb_iter (ligature)
983 | hb_filter (subset_offset_array (c, out->ligature, this, coverage_idx))
984 | hb_drain
985 ;
986
987 if (bool (out->ligature))
988 // Ensure Coverage table is always packed after this.
989 c->serializer->add_virtual_link (coverage_idx);
990
991 return_trace (bool (out->ligature));
992 }
993
sanitizeOT::LigatureSet994 bool sanitize (hb_sanitize_context_t *c) const
995 {
996 TRACE_SANITIZE (this);
997 return_trace (ligature.sanitize (c, this));
998 }
999
1000 protected:
1001 Array16OfOffset16To<Ligature>
1002 ligature; /* Array LigatureSet tables
1003 * ordered by preference */
1004 public:
1005 DEFINE_SIZE_ARRAY (2, ligature);
1006 };
1007
1008 struct LigatureSubstFormat1
1009 {
intersectsOT::LigatureSubstFormat11010 bool intersects (const hb_set_t *glyphs) const
1011 {
1012 return
1013 + hb_zip (this+coverage, ligatureSet)
1014 | hb_filter (*glyphs, hb_first)
1015 | hb_map (hb_second)
1016 | hb_map ([this, glyphs] (const Offset16To<LigatureSet> &_)
1017 { return (this+_).intersects (glyphs); })
1018 | hb_any
1019 ;
1020 }
1021
may_have_non_1to1OT::LigatureSubstFormat11022 bool may_have_non_1to1 () const
1023 { return true; }
1024
closureOT::LigatureSubstFormat11025 void closure (hb_closure_context_t *c) const
1026 {
1027 + hb_zip (this+coverage, ligatureSet)
1028 | hb_filter (c->parent_active_glyphs (), hb_first)
1029 | hb_map (hb_second)
1030 | hb_map (hb_add (this))
1031 | hb_apply ([c] (const LigatureSet &_) { _.closure (c); })
1032 ;
1033
1034 }
1035
closure_lookupsOT::LigatureSubstFormat11036 void closure_lookups (hb_closure_lookups_context_t *c) const {}
1037
collect_glyphsOT::LigatureSubstFormat11038 void collect_glyphs (hb_collect_glyphs_context_t *c) const
1039 {
1040 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
1041
1042 + hb_zip (this+coverage, ligatureSet)
1043 | hb_map (hb_second)
1044 | hb_map (hb_add (this))
1045 | hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); })
1046 ;
1047 }
1048
get_coverageOT::LigatureSubstFormat11049 const Coverage &get_coverage () const { return this+coverage; }
1050
would_applyOT::LigatureSubstFormat11051 bool would_apply (hb_would_apply_context_t *c) const
1052 {
1053 unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
1054 if (likely (index == NOT_COVERED)) return false;
1055
1056 const LigatureSet &lig_set = this+ligatureSet[index];
1057 return lig_set.would_apply (c);
1058 }
1059
applyOT::LigatureSubstFormat11060 bool apply (hb_ot_apply_context_t *c) const
1061 {
1062 TRACE_APPLY (this);
1063
1064 unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
1065 if (likely (index == NOT_COVERED)) return_trace (false);
1066
1067 const LigatureSet &lig_set = this+ligatureSet[index];
1068 return_trace (lig_set.apply (c));
1069 }
1070
serializeOT::LigatureSubstFormat11071 bool serialize (hb_serialize_context_t *c,
1072 hb_sorted_array_t<const HBGlyphID16> first_glyphs,
1073 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
1074 hb_array_t<const HBGlyphID16> ligatures_list,
1075 hb_array_t<const unsigned int> component_count_list,
1076 hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
1077 {
1078 TRACE_SERIALIZE (this);
1079 if (unlikely (!c->extend_min (this))) return_trace (false);
1080 if (unlikely (!ligatureSet.serialize (c, first_glyphs.length))) return_trace (false);
1081 for (unsigned int i = 0; i < first_glyphs.length; i++)
1082 {
1083 unsigned int ligature_count = ligature_per_first_glyph_count_list[i];
1084 if (unlikely (!ligatureSet[i]
1085 .serialize_serialize (c,
1086 ligatures_list.sub_array (0, ligature_count),
1087 component_count_list.sub_array (0, ligature_count),
1088 component_list))) return_trace (false);
1089 ligatures_list += ligature_count;
1090 component_count_list += ligature_count;
1091 }
1092 return_trace (coverage.serialize_serialize (c, first_glyphs));
1093 }
1094
subsetOT::LigatureSubstFormat11095 bool subset (hb_subset_context_t *c) const
1096 {
1097 TRACE_SUBSET (this);
1098 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
1099 const hb_map_t &glyph_map = *c->plan->glyph_map;
1100
1101 auto *out = c->serializer->start_embed (*this);
1102 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
1103 out->format = format;
1104
1105 // Due to a bug in some older versions of windows 7 the Coverage table must be
1106 // packed after the LigatureSet and Ligature tables, so serialize Coverage first
1107 // which places it last in the packed order.
1108 hb_set_t new_coverage;
1109 + hb_zip (this+coverage, hb_iter (ligatureSet) | hb_map (hb_add (this)))
1110 | hb_filter (glyphset, hb_first)
1111 | hb_filter ([&] (const LigatureSet& _) {
1112 return _.intersects (&glyphset);
1113 }, hb_second)
1114 | hb_map (hb_first)
1115 | hb_sink (new_coverage);
1116
1117 if (!c->serializer->push<Coverage> ()
1118 ->serialize (c->serializer,
1119 + new_coverage.iter () | hb_map_retains_sorting (glyph_map)))
1120 {
1121 c->serializer->pop_discard ();
1122 return_trace (false);
1123 }
1124
1125 unsigned coverage_idx = c->serializer->pop_pack ();
1126 c->serializer->add_link (out->coverage, coverage_idx);
1127
1128 + hb_zip (this+coverage, ligatureSet)
1129 | hb_filter (new_coverage, hb_first)
1130 | hb_map (hb_second)
1131 // to ensure that the repacker always orders the coverage table after the LigatureSet
1132 // and LigatureSubtable's they will be linked to the Coverage table via a virtual link
1133 // the coverage table object idx is passed down to facilitate this.
1134 | hb_apply (subset_offset_array (c, out->ligatureSet, this, coverage_idx))
1135 ;
1136
1137 return_trace (bool (new_coverage));
1138 }
1139
sanitizeOT::LigatureSubstFormat11140 bool sanitize (hb_sanitize_context_t *c) const
1141 {
1142 TRACE_SANITIZE (this);
1143 return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
1144 }
1145
1146 protected:
1147 HBUINT16 format; /* Format identifier--format = 1 */
1148 Offset16To<Coverage>
1149 coverage; /* Offset to Coverage table--from
1150 * beginning of Substitution table */
1151 Array16OfOffset16To<LigatureSet>
1152 ligatureSet; /* Array LigatureSet tables
1153 * ordered by Coverage Index */
1154 public:
1155 DEFINE_SIZE_ARRAY (6, ligatureSet);
1156 };
1157
1158 struct LigatureSubst
1159 {
serializeOT::LigatureSubst1160 bool serialize (hb_serialize_context_t *c,
1161 hb_sorted_array_t<const HBGlyphID16> first_glyphs,
1162 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
1163 hb_array_t<const HBGlyphID16> ligatures_list,
1164 hb_array_t<const unsigned int> component_count_list,
1165 hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
1166 {
1167 TRACE_SERIALIZE (this);
1168 if (unlikely (!c->extend_min (u.format))) return_trace (false);
1169 unsigned int format = 1;
1170 u.format = format;
1171 switch (u.format) {
1172 case 1: return_trace (u.format1.serialize (c,
1173 first_glyphs,
1174 ligature_per_first_glyph_count_list,
1175 ligatures_list,
1176 component_count_list,
1177 component_list));
1178 default:return_trace (false);
1179 }
1180 }
1181
1182 template <typename context_t, typename ...Ts>
dispatchOT::LigatureSubst1183 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1184 {
1185 TRACE_DISPATCH (this, u.format);
1186 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
1187 switch (u.format) {
1188 case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
1189 default:return_trace (c->default_return_value ());
1190 }
1191 }
1192
1193 protected:
1194 union {
1195 HBUINT16 format; /* Format identifier */
1196 LigatureSubstFormat1 format1;
1197 } u;
1198 };
1199
1200
1201 struct ContextSubst : Context {};
1202
1203 struct ChainContextSubst : ChainContext {};
1204
1205 struct ExtensionSubst : Extension<ExtensionSubst>
1206 {
1207 typedef struct SubstLookupSubTable SubTable;
1208 bool is_reverse () const;
1209 };
1210
1211
1212 struct ReverseChainSingleSubstFormat1
1213 {
intersectsOT::ReverseChainSingleSubstFormat11214 bool intersects (const hb_set_t *glyphs) const
1215 {
1216 if (!(this+coverage).intersects (glyphs))
1217 return false;
1218
1219 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1220
1221 unsigned int count;
1222
1223 count = backtrack.len;
1224 for (unsigned int i = 0; i < count; i++)
1225 if (!(this+backtrack[i]).intersects (glyphs))
1226 return false;
1227
1228 count = lookahead.len;
1229 for (unsigned int i = 0; i < count; i++)
1230 if (!(this+lookahead[i]).intersects (glyphs))
1231 return false;
1232
1233 return true;
1234 }
1235
may_have_non_1to1OT::ReverseChainSingleSubstFormat11236 bool may_have_non_1to1 () const
1237 { return false; }
1238
closureOT::ReverseChainSingleSubstFormat11239 void closure (hb_closure_context_t *c) const
1240 {
1241 if (!intersects (c->glyphs)) return;
1242
1243 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1244 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1245
1246 + hb_zip (this+coverage, substitute)
1247 | hb_filter (c->parent_active_glyphs (), hb_first)
1248 | hb_map (hb_second)
1249 | hb_sink (c->output)
1250 ;
1251 }
1252
closure_lookupsOT::ReverseChainSingleSubstFormat11253 void closure_lookups (hb_closure_lookups_context_t *c) const {}
1254
collect_glyphsOT::ReverseChainSingleSubstFormat11255 void collect_glyphs (hb_collect_glyphs_context_t *c) const
1256 {
1257 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
1258
1259 unsigned int count;
1260
1261 count = backtrack.len;
1262 for (unsigned int i = 0; i < count; i++)
1263 if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return;
1264
1265 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1266 count = lookahead.len;
1267 for (unsigned int i = 0; i < count; i++)
1268 if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return;
1269
1270 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1271 count = substitute.len;
1272 c->output->add_array (substitute.arrayZ, substitute.len);
1273 }
1274
get_coverageOT::ReverseChainSingleSubstFormat11275 const Coverage &get_coverage () const { return this+coverage; }
1276
would_applyOT::ReverseChainSingleSubstFormat11277 bool would_apply (hb_would_apply_context_t *c) const
1278 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
1279
applyOT::ReverseChainSingleSubstFormat11280 bool apply (hb_ot_apply_context_t *c) const
1281 {
1282 TRACE_APPLY (this);
1283 if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
1284 return_trace (false); /* No chaining to this type */
1285
1286 unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
1287 if (likely (index == NOT_COVERED)) return_trace (false);
1288
1289 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1290 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1291
1292 if (unlikely (index >= substitute.len)) return_trace (false);
1293
1294 unsigned int start_index = 0, end_index = 0;
1295 if (match_backtrack (c,
1296 backtrack.len, (HBUINT16 *) backtrack.arrayZ,
1297 match_coverage, this,
1298 &start_index) &&
1299 match_lookahead (c,
1300 lookahead.len, (HBUINT16 *) lookahead.arrayZ,
1301 match_coverage, this,
1302 c->buffer->idx + 1, &end_index))
1303 {
1304 c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
1305 c->replace_glyph_inplace (substitute[index]);
1306 /* Note: We DON'T decrease buffer->idx. The main loop does it
1307 * for us. This is useful for preventing surprises if someone
1308 * calls us through a Context lookup. */
1309 return_trace (true);
1310 }
1311 else
1312 {
1313 c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index);
1314 return_trace (false);
1315 }
1316 }
1317
1318 template<typename Iterator,
1319 hb_requires (hb_is_iterator (Iterator))>
serialize_coverage_offset_arrayOT::ReverseChainSingleSubstFormat11320 bool serialize_coverage_offset_array (hb_subset_context_t *c, Iterator it) const
1321 {
1322 TRACE_SERIALIZE (this);
1323 auto *out = c->serializer->start_embed<Array16OfOffset16To<Coverage>> ();
1324
1325 if (unlikely (!c->serializer->allocate_size<HBUINT16> (HBUINT16::static_size)))
1326 return_trace (false);
1327
1328 for (auto& offset : it) {
1329 auto *o = out->serialize_append (c->serializer);
1330 if (unlikely (!o) || !o->serialize_subset (c, offset, this))
1331 return_trace (false);
1332 }
1333
1334 return_trace (true);
1335 }
1336
1337 template<typename Iterator, typename BacktrackIterator, typename LookaheadIterator,
1338 hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_pair_t)),
1339 hb_requires (hb_is_iterator (BacktrackIterator)),
1340 hb_requires (hb_is_iterator (LookaheadIterator))>
serializeOT::ReverseChainSingleSubstFormat11341 bool serialize (hb_subset_context_t *c,
1342 Iterator coverage_subst_iter,
1343 BacktrackIterator backtrack_iter,
1344 LookaheadIterator lookahead_iter) const
1345 {
1346 TRACE_SERIALIZE (this);
1347
1348 auto *out = c->serializer->start_embed (this);
1349 if (unlikely (!c->serializer->check_success (out))) return_trace (false);
1350 if (unlikely (!c->serializer->embed (this->format))) return_trace (false);
1351 if (unlikely (!c->serializer->embed (this->coverage))) return_trace (false);
1352
1353 if (!serialize_coverage_offset_array (c, backtrack_iter)) return_trace (false);
1354 if (!serialize_coverage_offset_array (c, lookahead_iter)) return_trace (false);
1355
1356 auto *substitute_out = c->serializer->start_embed<Array16Of<HBGlyphID16>> ();
1357 auto substitutes =
1358 + coverage_subst_iter
1359 | hb_map (hb_second)
1360 ;
1361
1362 auto glyphs =
1363 + coverage_subst_iter
1364 | hb_map_retains_sorting (hb_first)
1365 ;
1366 if (unlikely (! c->serializer->check_success (substitute_out->serialize (c->serializer, substitutes))))
1367 return_trace (false);
1368
1369 if (unlikely (!out->coverage.serialize_serialize (c->serializer, glyphs)))
1370 return_trace (false);
1371 return_trace (true);
1372 }
1373
subsetOT::ReverseChainSingleSubstFormat11374 bool subset (hb_subset_context_t *c) const
1375 {
1376 TRACE_SUBSET (this);
1377 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
1378 const hb_map_t &glyph_map = *c->plan->glyph_map;
1379
1380 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1381 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1382
1383 auto it =
1384 + hb_zip (this+coverage, substitute)
1385 | hb_filter (glyphset, hb_first)
1386 | hb_filter (glyphset, hb_second)
1387 | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const HBGlyphID16 &> p) -> hb_codepoint_pair_t
1388 { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
1389 ;
1390
1391 return_trace (bool (it) && serialize (c, it, backtrack.iter (), lookahead.iter ()));
1392 }
1393
sanitizeOT::ReverseChainSingleSubstFormat11394 bool sanitize (hb_sanitize_context_t *c) const
1395 {
1396 TRACE_SANITIZE (this);
1397 if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
1398 return_trace (false);
1399 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1400 if (!lookahead.sanitize (c, this))
1401 return_trace (false);
1402 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1403 return_trace (substitute.sanitize (c));
1404 }
1405
1406 protected:
1407 HBUINT16 format; /* Format identifier--format = 1 */
1408 Offset16To<Coverage>
1409 coverage; /* Offset to Coverage table--from
1410 * beginning of table */
1411 Array16OfOffset16To<Coverage>
1412 backtrack; /* Array of coverage tables
1413 * in backtracking sequence, in glyph
1414 * sequence order */
1415 Array16OfOffset16To<Coverage>
1416 lookaheadX; /* Array of coverage tables
1417 * in lookahead sequence, in glyph
1418 * sequence order */
1419 Array16Of<HBGlyphID16>
1420 substituteX; /* Array of substitute
1421 * GlyphIDs--ordered by Coverage Index */
1422 public:
1423 DEFINE_SIZE_MIN (10);
1424 };
1425
1426 struct ReverseChainSingleSubst
1427 {
1428 template <typename context_t, typename ...Ts>
dispatchOT::ReverseChainSingleSubst1429 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1430 {
1431 TRACE_DISPATCH (this, u.format);
1432 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
1433 switch (u.format) {
1434 case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
1435 default:return_trace (c->default_return_value ());
1436 }
1437 }
1438
1439 protected:
1440 union {
1441 HBUINT16 format; /* Format identifier */
1442 ReverseChainSingleSubstFormat1 format1;
1443 } u;
1444 };
1445
1446
1447
1448 /*
1449 * SubstLookup
1450 */
1451
1452 struct SubstLookupSubTable
1453 {
1454 friend struct Lookup;
1455 friend struct SubstLookup;
1456
1457 enum Type {
1458 Single = 1,
1459 Multiple = 2,
1460 Alternate = 3,
1461 Ligature = 4,
1462 Context = 5,
1463 ChainContext = 6,
1464 Extension = 7,
1465 ReverseChainSingle = 8
1466 };
1467
1468 template <typename context_t, typename ...Ts>
dispatchOT::SubstLookupSubTable1469 typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const
1470 {
1471 TRACE_DISPATCH (this, lookup_type);
1472 switch (lookup_type) {
1473 case Single: return_trace (u.single.dispatch (c, std::forward<Ts> (ds)...));
1474 case Multiple: return_trace (u.multiple.dispatch (c, std::forward<Ts> (ds)...));
1475 case Alternate: return_trace (u.alternate.dispatch (c, std::forward<Ts> (ds)...));
1476 case Ligature: return_trace (u.ligature.dispatch (c, std::forward<Ts> (ds)...));
1477 case Context: return_trace (u.context.dispatch (c, std::forward<Ts> (ds)...));
1478 case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward<Ts> (ds)...));
1479 case Extension: return_trace (u.extension.dispatch (c, std::forward<Ts> (ds)...));
1480 case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c, std::forward<Ts> (ds)...));
1481 default: return_trace (c->default_return_value ());
1482 }
1483 }
1484
intersectsOT::SubstLookupSubTable1485 bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const
1486 {
1487 hb_intersects_context_t c (glyphs);
1488 return dispatch (&c, lookup_type);
1489 }
1490
1491 protected:
1492 union {
1493 SingleSubst single;
1494 MultipleSubst multiple;
1495 AlternateSubst alternate;
1496 LigatureSubst ligature;
1497 ContextSubst context;
1498 ChainContextSubst chainContext;
1499 ExtensionSubst extension;
1500 ReverseChainSingleSubst reverseChainContextSingle;
1501 } u;
1502 public:
1503 DEFINE_SIZE_MIN (0);
1504 };
1505
1506
1507 struct SubstLookup : Lookup
1508 {
1509 typedef SubstLookupSubTable SubTable;
1510
get_subtableOT::SubstLookup1511 const SubTable& get_subtable (unsigned int i) const
1512 { return Lookup::get_subtable<SubTable> (i); }
1513
lookup_type_is_reverseOT::SubstLookup1514 static inline bool lookup_type_is_reverse (unsigned int lookup_type)
1515 { return lookup_type == SubTable::ReverseChainSingle; }
1516
is_reverseOT::SubstLookup1517 bool is_reverse () const
1518 {
1519 unsigned int type = get_type ();
1520 if (unlikely (type == SubTable::Extension))
1521 return reinterpret_cast<const ExtensionSubst &> (get_subtable (0)).is_reverse ();
1522 return lookup_type_is_reverse (type);
1523 }
1524
may_have_non_1to1OT::SubstLookup1525 bool may_have_non_1to1 () const
1526 {
1527 hb_have_non_1to1_context_t c;
1528 return dispatch (&c);
1529 }
1530
applyOT::SubstLookup1531 bool apply (hb_ot_apply_context_t *c) const
1532 {
1533 TRACE_APPLY (this);
1534 return_trace (dispatch (c));
1535 }
1536
intersectsOT::SubstLookup1537 bool intersects (const hb_set_t *glyphs) const
1538 {
1539 hb_intersects_context_t c (glyphs);
1540 return dispatch (&c);
1541 }
1542
closureOT::SubstLookup1543 hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
1544 {
1545 if (!c->should_visit_lookup (this_index))
1546 return hb_closure_context_t::default_return_value ();
1547
1548 c->set_recurse_func (dispatch_closure_recurse_func);
1549
1550 hb_closure_context_t::return_t ret = dispatch (c);
1551
1552 c->flush ();
1553
1554 return ret;
1555 }
1556
closure_lookupsOT::SubstLookup1557 hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const
1558 {
1559 if (c->is_lookup_visited (this_index))
1560 return hb_closure_lookups_context_t::default_return_value ();
1561
1562 c->set_lookup_visited (this_index);
1563 if (!intersects (c->glyphs))
1564 {
1565 c->set_lookup_inactive (this_index);
1566 return hb_closure_lookups_context_t::default_return_value ();
1567 }
1568
1569 c->set_recurse_func (dispatch_closure_lookups_recurse_func);
1570
1571 hb_closure_lookups_context_t::return_t ret = dispatch (c);
1572 return ret;
1573 }
1574
collect_glyphsOT::SubstLookup1575 hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
1576 {
1577 c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
1578 return dispatch (c);
1579 }
1580
1581 template <typename set_t>
collect_coverageOT::SubstLookup1582 void collect_coverage (set_t *glyphs) const
1583 {
1584 hb_collect_coverage_context_t<set_t> c (glyphs);
1585 dispatch (&c);
1586 }
1587
would_applyOT::SubstLookup1588 bool would_apply (hb_would_apply_context_t *c,
1589 const hb_ot_layout_lookup_accelerator_t *accel) const
1590 {
1591 if (unlikely (!c->len)) return false;
1592 if (!accel->may_have (c->glyphs[0])) return false;
1593 return dispatch (c);
1594 }
1595
1596 static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
1597
serialize_singleOT::SubstLookup1598 bool serialize_single (hb_serialize_context_t *c,
1599 uint32_t lookup_props,
1600 hb_sorted_array_t<const HBGlyphID16> glyphs,
1601 hb_array_t<const HBGlyphID16> substitutes)
1602 {
1603 TRACE_SERIALIZE (this);
1604 if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
1605 if (c->push<SubTable> ()->u.single.serialize (c, hb_zip (glyphs, substitutes)))
1606 {
1607 c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
1608 return_trace (true);
1609 }
1610 c->pop_discard ();
1611 return_trace (false);
1612 }
1613
serialize_multipleOT::SubstLookup1614 bool serialize_multiple (hb_serialize_context_t *c,
1615 uint32_t lookup_props,
1616 hb_sorted_array_t<const HBGlyphID16> glyphs,
1617 hb_array_t<const unsigned int> substitute_len_list,
1618 hb_array_t<const HBGlyphID16> substitute_glyphs_list)
1619 {
1620 TRACE_SERIALIZE (this);
1621 if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false);
1622 if (c->push<SubTable> ()->u.multiple.
1623 serialize (c,
1624 glyphs,
1625 substitute_len_list,
1626 substitute_glyphs_list))
1627 {
1628 c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
1629 return_trace (true);
1630 }
1631 c->pop_discard ();
1632 return_trace (false);
1633 }
1634
serialize_alternateOT::SubstLookup1635 bool serialize_alternate (hb_serialize_context_t *c,
1636 uint32_t lookup_props,
1637 hb_sorted_array_t<const HBGlyphID16> glyphs,
1638 hb_array_t<const unsigned int> alternate_len_list,
1639 hb_array_t<const HBGlyphID16> alternate_glyphs_list)
1640 {
1641 TRACE_SERIALIZE (this);
1642 if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false);
1643
1644 if (c->push<SubTable> ()->u.alternate.
1645 serialize (c,
1646 glyphs,
1647 alternate_len_list,
1648 alternate_glyphs_list))
1649 {
1650 c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
1651 return_trace (true);
1652 }
1653 c->pop_discard ();
1654 return_trace (false);
1655 }
1656
serialize_ligatureOT::SubstLookup1657 bool serialize_ligature (hb_serialize_context_t *c,
1658 uint32_t lookup_props,
1659 hb_sorted_array_t<const HBGlyphID16> first_glyphs,
1660 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
1661 hb_array_t<const HBGlyphID16> ligatures_list,
1662 hb_array_t<const unsigned int> component_count_list,
1663 hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
1664 {
1665 TRACE_SERIALIZE (this);
1666 if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false);
1667 if (c->push<SubTable> ()->u.ligature.
1668 serialize (c,
1669 first_glyphs,
1670 ligature_per_first_glyph_count_list,
1671 ligatures_list,
1672 component_count_list,
1673 component_list))
1674 {
1675 c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
1676 return_trace (true);
1677 }
1678 c->pop_discard ();
1679 return_trace (false);
1680 }
1681
1682 template <typename context_t>
1683 static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
1684
1685 static inline typename hb_closure_context_t::return_t closure_glyphs_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index);
1686
dispatch_closure_recurse_funcOT::SubstLookup1687 static inline hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index)
1688 {
1689 if (!c->should_visit_lookup (lookup_index))
1690 return hb_empty_t ();
1691
1692 hb_closure_context_t::return_t ret = closure_glyphs_recurse_func (c, lookup_index, covered_seq_indices, seq_index, end_index);
1693
1694 /* While in theory we should flush here, it will cause timeouts because a recursive
1695 * lookup can keep growing the glyph set. Skip, and outer loop will retry up to
1696 * HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */
1697 //c->flush ();
1698
1699 return ret;
1700 }
1701
1702 HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned lookup_index);
1703
1704 template <typename context_t, typename ...Ts>
dispatchOT::SubstLookup1705 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1706 { return Lookup::dispatch<SubTable> (c, std::forward<Ts> (ds)...); }
1707
subsetOT::SubstLookup1708 bool subset (hb_subset_context_t *c) const
1709 { return Lookup::subset<SubTable> (c); }
1710
sanitizeOT::SubstLookup1711 bool sanitize (hb_sanitize_context_t *c) const
1712 { return Lookup::sanitize<SubTable> (c); }
1713 };
1714
1715 /*
1716 * GSUB -- Glyph Substitution
1717 * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub
1718 */
1719
1720 struct GSUB : GSUBGPOS
1721 {
1722 static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB;
1723
get_lookupOT::GSUB1724 const SubstLookup& get_lookup (unsigned int i) const
1725 { return static_cast<const SubstLookup &> (GSUBGPOS::get_lookup (i)); }
1726
subsetOT::GSUB1727 bool subset (hb_subset_context_t *c) const
1728 {
1729 hb_subset_layout_context_t l (c, tableTag, c->plan->gsub_lookups, c->plan->gsub_langsys, c->plan->gsub_features);
1730 return GSUBGPOS::subset<SubstLookup> (&l);
1731 }
1732
sanitizeOT::GSUB1733 bool sanitize (hb_sanitize_context_t *c) const
1734 { return GSUBGPOS::sanitize<SubstLookup> (c); }
1735
1736 HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
1737 hb_face_t *face) const;
1738
closure_lookupsOT::GSUB1739 void closure_lookups (hb_face_t *face,
1740 const hb_set_t *glyphs,
1741 hb_set_t *lookup_indexes /* IN/OUT */) const
1742 { GSUBGPOS::closure_lookups<SubstLookup> (face, glyphs, lookup_indexes); }
1743
1744 typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t;
1745 };
1746
1747
1748 struct GSUB_accelerator_t : GSUB::accelerator_t {
GSUB_accelerator_tOT::GSUB_accelerator_t1749 GSUB_accelerator_t (hb_face_t *face) : GSUB::accelerator_t (face) {}
1750 };
1751
1752
1753 /* Out-of-class implementation for methods recursing */
1754
1755 #ifndef HB_NO_OT_LAYOUT
is_reverse() const1756 /*static*/ inline bool ExtensionSubst::is_reverse () const
1757 {
1758 return SubstLookup::lookup_type_is_reverse (get_type ());
1759 }
1760 template <typename context_t>
dispatch_recurse_func(context_t * c,unsigned int lookup_index)1761 /*static*/ typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
1762 {
1763 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1764 return l.dispatch (c);
1765 }
1766
closure_glyphs_recurse_func(hb_closure_context_t * c,unsigned lookup_index,hb_set_t * covered_seq_indices,unsigned seq_index,unsigned end_index)1767 /*static*/ typename hb_closure_context_t::return_t SubstLookup::closure_glyphs_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index)
1768 {
1769 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1770 if (l.may_have_non_1to1 ())
1771 hb_set_add_range (covered_seq_indices, seq_index, end_index);
1772 return l.dispatch (c);
1773 }
1774
dispatch_closure_lookups_recurse_func(hb_closure_lookups_context_t * c,unsigned this_index)1775 /*static*/ inline hb_closure_lookups_context_t::return_t SubstLookup::dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index)
1776 {
1777 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (this_index);
1778 return l.closure_lookups (c, this_index);
1779 }
1780
apply_recurse_func(hb_ot_apply_context_t * c,unsigned int lookup_index)1781 /*static*/ bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
1782 {
1783 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1784 unsigned int saved_lookup_props = c->lookup_props;
1785 unsigned int saved_lookup_index = c->lookup_index;
1786 c->set_lookup_index (lookup_index);
1787 c->set_lookup_props (l.get_props ());
1788 bool ret = l.dispatch (c);
1789 c->set_lookup_index (saved_lookup_index);
1790 c->set_lookup_props (saved_lookup_props);
1791 return ret;
1792 }
1793 #endif
1794
1795
1796 } /* namespace OT */
1797
1798
1799 #endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */
1800