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_all ();
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_length = 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_length,
837 match_positions,
838 &total_component_count)))
839 return_trace (false);
840
841 ligate_input (c,
842 count,
843 match_positions,
844 match_length,
845 ligGlyph,
846 total_component_count);
847
848 return_trace (true);
849 }
850
851 template <typename Iterator,
852 hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
serializeOT::Ligature853 bool serialize (hb_serialize_context_t *c,
854 hb_codepoint_t ligature,
855 Iterator components /* Starting from second */)
856 {
857 TRACE_SERIALIZE (this);
858 if (unlikely (!c->extend_min (this))) return_trace (false);
859 ligGlyph = ligature;
860 if (unlikely (!component.serialize (c, components))) return_trace (false);
861 return_trace (true);
862 }
863
subsetOT::Ligature864 bool subset (hb_subset_context_t *c, unsigned coverage_idx) const
865 {
866 TRACE_SUBSET (this);
867 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
868 const hb_map_t &glyph_map = *c->plan->glyph_map;
869
870 if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false);
871 // Ensure Coverage table is always packed after this.
872 c->serializer->add_virtual_link (coverage_idx);
873
874 auto it =
875 + hb_iter (component)
876 | hb_map (glyph_map)
877 ;
878
879 auto *out = c->serializer->start_embed (*this);
880 return_trace (out->serialize (c->serializer,
881 glyph_map[ligGlyph],
882 it));
883 }
884
885 public:
sanitizeOT::Ligature886 bool sanitize (hb_sanitize_context_t *c) const
887 {
888 TRACE_SANITIZE (this);
889 return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
890 }
891
892 protected:
893 HBGlyphID16 ligGlyph; /* GlyphID of ligature to substitute */
894 HeadlessArrayOf<HBGlyphID16>
895 component; /* Array of component GlyphIDs--start
896 * with the second component--ordered
897 * in writing direction */
898 public:
899 DEFINE_SIZE_ARRAY (4, component);
900 };
901
902 struct LigatureSet
903 {
intersectsOT::LigatureSet904 bool intersects (const hb_set_t *glyphs) const
905 {
906 return
907 + hb_iter (ligature)
908 | hb_map (hb_add (this))
909 | hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); })
910 | hb_any
911 ;
912 }
913
closureOT::LigatureSet914 void closure (hb_closure_context_t *c) const
915 {
916 + hb_iter (ligature)
917 | hb_map (hb_add (this))
918 | hb_apply ([c] (const Ligature &_) { _.closure (c); })
919 ;
920 }
921
collect_glyphsOT::LigatureSet922 void collect_glyphs (hb_collect_glyphs_context_t *c) const
923 {
924 + hb_iter (ligature)
925 | hb_map (hb_add (this))
926 | hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); })
927 ;
928 }
929
would_applyOT::LigatureSet930 bool would_apply (hb_would_apply_context_t *c) const
931 {
932 return
933 + hb_iter (ligature)
934 | hb_map (hb_add (this))
935 | hb_map ([c] (const Ligature &_) { return _.would_apply (c); })
936 | hb_any
937 ;
938 }
939
applyOT::LigatureSet940 bool apply (hb_ot_apply_context_t *c) const
941 {
942 TRACE_APPLY (this);
943 unsigned int num_ligs = ligature.len;
944 for (unsigned int i = 0; i < num_ligs; i++)
945 {
946 const Ligature &lig = this+ligature[i];
947 if (lig.apply (c)) return_trace (true);
948 }
949
950 return_trace (false);
951 }
952
serializeOT::LigatureSet953 bool serialize (hb_serialize_context_t *c,
954 hb_array_t<const HBGlyphID16> ligatures,
955 hb_array_t<const unsigned int> component_count_list,
956 hb_array_t<const HBGlyphID16> &component_list /* Starting from second for each ligature */)
957 {
958 TRACE_SERIALIZE (this);
959 if (unlikely (!c->extend_min (this))) return_trace (false);
960 if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false);
961 for (unsigned int i = 0; i < ligatures.length; i++)
962 {
963 unsigned int component_count = (unsigned) hb_max ((int) component_count_list[i] - 1, 0);
964 if (unlikely (!ligature[i].serialize_serialize (c,
965 ligatures[i],
966 component_list.sub_array (0, component_count))))
967 return_trace (false);
968 component_list += component_count;
969 }
970 return_trace (true);
971 }
972
subsetOT::LigatureSet973 bool subset (hb_subset_context_t *c, unsigned coverage_idx) const
974 {
975 TRACE_SUBSET (this);
976 auto *out = c->serializer->start_embed (*this);
977 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
978
979 + hb_iter (ligature)
980 | hb_filter (subset_offset_array (c, out->ligature, this, coverage_idx))
981 | hb_drain
982 ;
983
984 if (bool (out->ligature))
985 // Ensure Coverage table is always packed after this.
986 c->serializer->add_virtual_link (coverage_idx);
987
988 return_trace (bool (out->ligature));
989 }
990
sanitizeOT::LigatureSet991 bool sanitize (hb_sanitize_context_t *c) const
992 {
993 TRACE_SANITIZE (this);
994 return_trace (ligature.sanitize (c, this));
995 }
996
997 protected:
998 Array16OfOffset16To<Ligature>
999 ligature; /* Array LigatureSet tables
1000 * ordered by preference */
1001 public:
1002 DEFINE_SIZE_ARRAY (2, ligature);
1003 };
1004
1005 struct LigatureSubstFormat1
1006 {
intersectsOT::LigatureSubstFormat11007 bool intersects (const hb_set_t *glyphs) const
1008 {
1009 return
1010 + hb_zip (this+coverage, ligatureSet)
1011 | hb_filter (*glyphs, hb_first)
1012 | hb_map (hb_second)
1013 | hb_map ([this, glyphs] (const Offset16To<LigatureSet> &_)
1014 { return (this+_).intersects (glyphs); })
1015 | hb_any
1016 ;
1017 }
1018
may_have_non_1to1OT::LigatureSubstFormat11019 bool may_have_non_1to1 () const
1020 { return true; }
1021
closureOT::LigatureSubstFormat11022 void closure (hb_closure_context_t *c) const
1023 {
1024 + hb_zip (this+coverage, ligatureSet)
1025 | hb_filter (c->parent_active_glyphs (), hb_first)
1026 | hb_map (hb_second)
1027 | hb_map (hb_add (this))
1028 | hb_apply ([c] (const LigatureSet &_) { _.closure (c); })
1029 ;
1030
1031 }
1032
closure_lookupsOT::LigatureSubstFormat11033 void closure_lookups (hb_closure_lookups_context_t *c) const {}
1034
collect_glyphsOT::LigatureSubstFormat11035 void collect_glyphs (hb_collect_glyphs_context_t *c) const
1036 {
1037 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
1038
1039 + hb_zip (this+coverage, ligatureSet)
1040 | hb_map (hb_second)
1041 | hb_map (hb_add (this))
1042 | hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); })
1043 ;
1044 }
1045
get_coverageOT::LigatureSubstFormat11046 const Coverage &get_coverage () const { return this+coverage; }
1047
would_applyOT::LigatureSubstFormat11048 bool would_apply (hb_would_apply_context_t *c) const
1049 {
1050 unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
1051 if (likely (index == NOT_COVERED)) return false;
1052
1053 const LigatureSet &lig_set = this+ligatureSet[index];
1054 return lig_set.would_apply (c);
1055 }
1056
applyOT::LigatureSubstFormat11057 bool apply (hb_ot_apply_context_t *c) const
1058 {
1059 TRACE_APPLY (this);
1060
1061 unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
1062 if (likely (index == NOT_COVERED)) return_trace (false);
1063
1064 const LigatureSet &lig_set = this+ligatureSet[index];
1065 return_trace (lig_set.apply (c));
1066 }
1067
serializeOT::LigatureSubstFormat11068 bool serialize (hb_serialize_context_t *c,
1069 hb_sorted_array_t<const HBGlyphID16> first_glyphs,
1070 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
1071 hb_array_t<const HBGlyphID16> ligatures_list,
1072 hb_array_t<const unsigned int> component_count_list,
1073 hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
1074 {
1075 TRACE_SERIALIZE (this);
1076 if (unlikely (!c->extend_min (this))) return_trace (false);
1077 if (unlikely (!ligatureSet.serialize (c, first_glyphs.length))) return_trace (false);
1078 for (unsigned int i = 0; i < first_glyphs.length; i++)
1079 {
1080 unsigned int ligature_count = ligature_per_first_glyph_count_list[i];
1081 if (unlikely (!ligatureSet[i]
1082 .serialize_serialize (c,
1083 ligatures_list.sub_array (0, ligature_count),
1084 component_count_list.sub_array (0, ligature_count),
1085 component_list))) return_trace (false);
1086 ligatures_list += ligature_count;
1087 component_count_list += ligature_count;
1088 }
1089 return_trace (coverage.serialize_serialize (c, first_glyphs));
1090 }
1091
subsetOT::LigatureSubstFormat11092 bool subset (hb_subset_context_t *c) const
1093 {
1094 TRACE_SUBSET (this);
1095 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
1096 const hb_map_t &glyph_map = *c->plan->glyph_map;
1097
1098 auto *out = c->serializer->start_embed (*this);
1099 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
1100 out->format = format;
1101
1102 // Due to a bug in some older versions of windows 7 the Coverage table must be
1103 // packed after the LigatureSet and Ligature tables, so serialize Coverage first
1104 // which places it last in the packed order.
1105 hb_set_t new_coverage;
1106 + hb_zip (this+coverage, hb_iter (ligatureSet) | hb_map (hb_add (this)))
1107 | hb_filter (glyphset, hb_first)
1108 | hb_filter ([&] (const LigatureSet& _) {
1109 return _.intersects (&glyphset);
1110 }, hb_second)
1111 | hb_map (hb_first)
1112 | hb_sink (new_coverage);
1113
1114 if (!c->serializer->push<Coverage> ()
1115 ->serialize (c->serializer,
1116 + new_coverage.iter () | hb_map_retains_sorting (glyph_map)))
1117 {
1118 c->serializer->pop_discard ();
1119 return_trace (false);
1120 }
1121
1122 unsigned coverage_idx = c->serializer->pop_pack ();
1123 c->serializer->add_link (out->coverage, coverage_idx);
1124
1125 + hb_zip (this+coverage, ligatureSet)
1126 | hb_filter (new_coverage, hb_first)
1127 | hb_map (hb_second)
1128 // to ensure that the repacker always orders the coverage table after the LigatureSet
1129 // and LigatureSubtable's they will be linked to the Coverage table via a virtual link
1130 // the coverage table object idx is passed down to facilitate this.
1131 | hb_apply (subset_offset_array (c, out->ligatureSet, this, coverage_idx))
1132 ;
1133
1134 return_trace (bool (new_coverage));
1135 }
1136
sanitizeOT::LigatureSubstFormat11137 bool sanitize (hb_sanitize_context_t *c) const
1138 {
1139 TRACE_SANITIZE (this);
1140 return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
1141 }
1142
1143 protected:
1144 HBUINT16 format; /* Format identifier--format = 1 */
1145 Offset16To<Coverage>
1146 coverage; /* Offset to Coverage table--from
1147 * beginning of Substitution table */
1148 Array16OfOffset16To<LigatureSet>
1149 ligatureSet; /* Array LigatureSet tables
1150 * ordered by Coverage Index */
1151 public:
1152 DEFINE_SIZE_ARRAY (6, ligatureSet);
1153 };
1154
1155 struct LigatureSubst
1156 {
serializeOT::LigatureSubst1157 bool serialize (hb_serialize_context_t *c,
1158 hb_sorted_array_t<const HBGlyphID16> first_glyphs,
1159 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
1160 hb_array_t<const HBGlyphID16> ligatures_list,
1161 hb_array_t<const unsigned int> component_count_list,
1162 hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
1163 {
1164 TRACE_SERIALIZE (this);
1165 if (unlikely (!c->extend_min (u.format))) return_trace (false);
1166 unsigned int format = 1;
1167 u.format = format;
1168 switch (u.format) {
1169 case 1: return_trace (u.format1.serialize (c,
1170 first_glyphs,
1171 ligature_per_first_glyph_count_list,
1172 ligatures_list,
1173 component_count_list,
1174 component_list));
1175 default:return_trace (false);
1176 }
1177 }
1178
1179 template <typename context_t, typename ...Ts>
dispatchOT::LigatureSubst1180 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1181 {
1182 TRACE_DISPATCH (this, u.format);
1183 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
1184 switch (u.format) {
1185 case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
1186 default:return_trace (c->default_return_value ());
1187 }
1188 }
1189
1190 protected:
1191 union {
1192 HBUINT16 format; /* Format identifier */
1193 LigatureSubstFormat1 format1;
1194 } u;
1195 };
1196
1197
1198 struct ContextSubst : Context {};
1199
1200 struct ChainContextSubst : ChainContext {};
1201
1202 struct ExtensionSubst : Extension<ExtensionSubst>
1203 {
1204 typedef struct SubstLookupSubTable SubTable;
1205 bool is_reverse () const;
1206 };
1207
1208
1209 struct ReverseChainSingleSubstFormat1
1210 {
intersectsOT::ReverseChainSingleSubstFormat11211 bool intersects (const hb_set_t *glyphs) const
1212 {
1213 if (!(this+coverage).intersects (glyphs))
1214 return false;
1215
1216 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1217
1218 unsigned int count;
1219
1220 count = backtrack.len;
1221 for (unsigned int i = 0; i < count; i++)
1222 if (!(this+backtrack[i]).intersects (glyphs))
1223 return false;
1224
1225 count = lookahead.len;
1226 for (unsigned int i = 0; i < count; i++)
1227 if (!(this+lookahead[i]).intersects (glyphs))
1228 return false;
1229
1230 return true;
1231 }
1232
may_have_non_1to1OT::ReverseChainSingleSubstFormat11233 bool may_have_non_1to1 () const
1234 { return false; }
1235
closureOT::ReverseChainSingleSubstFormat11236 void closure (hb_closure_context_t *c) const
1237 {
1238 if (!intersects (c->glyphs)) return;
1239
1240 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1241 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1242
1243 + hb_zip (this+coverage, substitute)
1244 | hb_filter (c->parent_active_glyphs (), hb_first)
1245 | hb_map (hb_second)
1246 | hb_sink (c->output)
1247 ;
1248 }
1249
closure_lookupsOT::ReverseChainSingleSubstFormat11250 void closure_lookups (hb_closure_lookups_context_t *c) const {}
1251
collect_glyphsOT::ReverseChainSingleSubstFormat11252 void collect_glyphs (hb_collect_glyphs_context_t *c) const
1253 {
1254 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
1255
1256 unsigned int count;
1257
1258 count = backtrack.len;
1259 for (unsigned int i = 0; i < count; i++)
1260 if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return;
1261
1262 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1263 count = lookahead.len;
1264 for (unsigned int i = 0; i < count; i++)
1265 if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return;
1266
1267 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1268 count = substitute.len;
1269 c->output->add_array (substitute.arrayZ, substitute.len);
1270 }
1271
get_coverageOT::ReverseChainSingleSubstFormat11272 const Coverage &get_coverage () const { return this+coverage; }
1273
would_applyOT::ReverseChainSingleSubstFormat11274 bool would_apply (hb_would_apply_context_t *c) const
1275 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
1276
applyOT::ReverseChainSingleSubstFormat11277 bool apply (hb_ot_apply_context_t *c) const
1278 {
1279 TRACE_APPLY (this);
1280 if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
1281 return_trace (false); /* No chaining to this type */
1282
1283 unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
1284 if (likely (index == NOT_COVERED)) return_trace (false);
1285
1286 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1287 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1288
1289 if (unlikely (index >= substitute.len)) return_trace (false);
1290
1291 unsigned int start_index = 0, end_index = 0;
1292 if (match_backtrack (c,
1293 backtrack.len, (HBUINT16 *) backtrack.arrayZ,
1294 match_coverage, this,
1295 &start_index) &&
1296 match_lookahead (c,
1297 lookahead.len, (HBUINT16 *) lookahead.arrayZ,
1298 match_coverage, this,
1299 1, &end_index))
1300 {
1301 c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
1302 c->replace_glyph_inplace (substitute[index]);
1303 /* Note: We DON'T decrease buffer->idx. The main loop does it
1304 * for us. This is useful for preventing surprises if someone
1305 * calls us through a Context lookup. */
1306 return_trace (true);
1307 }
1308
1309 return_trace (false);
1310 }
1311
1312 template<typename Iterator,
1313 hb_requires (hb_is_iterator (Iterator))>
serialize_coverage_offset_arrayOT::ReverseChainSingleSubstFormat11314 bool serialize_coverage_offset_array (hb_subset_context_t *c, Iterator it) const
1315 {
1316 TRACE_SERIALIZE (this);
1317 auto *out = c->serializer->start_embed<Array16OfOffset16To<Coverage>> ();
1318
1319 if (unlikely (!c->serializer->allocate_size<HBUINT16> (HBUINT16::static_size)))
1320 return_trace (false);
1321
1322 for (auto& offset : it) {
1323 auto *o = out->serialize_append (c->serializer);
1324 if (unlikely (!o) || !o->serialize_subset (c, offset, this))
1325 return_trace (false);
1326 }
1327
1328 return_trace (true);
1329 }
1330
1331 template<typename Iterator, typename BacktrackIterator, typename LookaheadIterator,
1332 hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_pair_t)),
1333 hb_requires (hb_is_iterator (BacktrackIterator)),
1334 hb_requires (hb_is_iterator (LookaheadIterator))>
serializeOT::ReverseChainSingleSubstFormat11335 bool serialize (hb_subset_context_t *c,
1336 Iterator coverage_subst_iter,
1337 BacktrackIterator backtrack_iter,
1338 LookaheadIterator lookahead_iter) const
1339 {
1340 TRACE_SERIALIZE (this);
1341
1342 auto *out = c->serializer->start_embed (this);
1343 if (unlikely (!c->serializer->check_success (out))) return_trace (false);
1344 if (unlikely (!c->serializer->embed (this->format))) return_trace (false);
1345 if (unlikely (!c->serializer->embed (this->coverage))) return_trace (false);
1346
1347 if (!serialize_coverage_offset_array (c, backtrack_iter)) return_trace (false);
1348 if (!serialize_coverage_offset_array (c, lookahead_iter)) return_trace (false);
1349
1350 auto *substitute_out = c->serializer->start_embed<Array16Of<HBGlyphID16>> ();
1351 auto substitutes =
1352 + coverage_subst_iter
1353 | hb_map (hb_second)
1354 ;
1355
1356 auto glyphs =
1357 + coverage_subst_iter
1358 | hb_map_retains_sorting (hb_first)
1359 ;
1360 if (unlikely (! c->serializer->check_success (substitute_out->serialize (c->serializer, substitutes))))
1361 return_trace (false);
1362
1363 if (unlikely (!out->coverage.serialize_serialize (c->serializer, glyphs)))
1364 return_trace (false);
1365 return_trace (true);
1366 }
1367
subsetOT::ReverseChainSingleSubstFormat11368 bool subset (hb_subset_context_t *c) const
1369 {
1370 TRACE_SUBSET (this);
1371 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
1372 const hb_map_t &glyph_map = *c->plan->glyph_map;
1373
1374 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1375 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1376
1377 auto it =
1378 + hb_zip (this+coverage, substitute)
1379 | hb_filter (glyphset, hb_first)
1380 | hb_filter (glyphset, hb_second)
1381 | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const HBGlyphID16 &> p) -> hb_codepoint_pair_t
1382 { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
1383 ;
1384
1385 return_trace (bool (it) && serialize (c, it, backtrack.iter (), lookahead.iter ()));
1386 }
1387
sanitizeOT::ReverseChainSingleSubstFormat11388 bool sanitize (hb_sanitize_context_t *c) const
1389 {
1390 TRACE_SANITIZE (this);
1391 if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
1392 return_trace (false);
1393 const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
1394 if (!lookahead.sanitize (c, this))
1395 return_trace (false);
1396 const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
1397 return_trace (substitute.sanitize (c));
1398 }
1399
1400 protected:
1401 HBUINT16 format; /* Format identifier--format = 1 */
1402 Offset16To<Coverage>
1403 coverage; /* Offset to Coverage table--from
1404 * beginning of table */
1405 Array16OfOffset16To<Coverage>
1406 backtrack; /* Array of coverage tables
1407 * in backtracking sequence, in glyph
1408 * sequence order */
1409 Array16OfOffset16To<Coverage>
1410 lookaheadX; /* Array of coverage tables
1411 * in lookahead sequence, in glyph
1412 * sequence order */
1413 Array16Of<HBGlyphID16>
1414 substituteX; /* Array of substitute
1415 * GlyphIDs--ordered by Coverage Index */
1416 public:
1417 DEFINE_SIZE_MIN (10);
1418 };
1419
1420 struct ReverseChainSingleSubst
1421 {
1422 template <typename context_t, typename ...Ts>
dispatchOT::ReverseChainSingleSubst1423 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1424 {
1425 TRACE_DISPATCH (this, u.format);
1426 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
1427 switch (u.format) {
1428 case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
1429 default:return_trace (c->default_return_value ());
1430 }
1431 }
1432
1433 protected:
1434 union {
1435 HBUINT16 format; /* Format identifier */
1436 ReverseChainSingleSubstFormat1 format1;
1437 } u;
1438 };
1439
1440
1441
1442 /*
1443 * SubstLookup
1444 */
1445
1446 struct SubstLookupSubTable
1447 {
1448 friend struct Lookup;
1449 friend struct SubstLookup;
1450
1451 enum Type {
1452 Single = 1,
1453 Multiple = 2,
1454 Alternate = 3,
1455 Ligature = 4,
1456 Context = 5,
1457 ChainContext = 6,
1458 Extension = 7,
1459 ReverseChainSingle = 8
1460 };
1461
1462 template <typename context_t, typename ...Ts>
dispatchOT::SubstLookupSubTable1463 typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const
1464 {
1465 TRACE_DISPATCH (this, lookup_type);
1466 switch (lookup_type) {
1467 case Single: return_trace (u.single.dispatch (c, std::forward<Ts> (ds)...));
1468 case Multiple: return_trace (u.multiple.dispatch (c, std::forward<Ts> (ds)...));
1469 case Alternate: return_trace (u.alternate.dispatch (c, std::forward<Ts> (ds)...));
1470 case Ligature: return_trace (u.ligature.dispatch (c, std::forward<Ts> (ds)...));
1471 case Context: return_trace (u.context.dispatch (c, std::forward<Ts> (ds)...));
1472 case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward<Ts> (ds)...));
1473 case Extension: return_trace (u.extension.dispatch (c, std::forward<Ts> (ds)...));
1474 case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c, std::forward<Ts> (ds)...));
1475 default: return_trace (c->default_return_value ());
1476 }
1477 }
1478
intersectsOT::SubstLookupSubTable1479 bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const
1480 {
1481 hb_intersects_context_t c (glyphs);
1482 return dispatch (&c, lookup_type);
1483 }
1484
1485 protected:
1486 union {
1487 SingleSubst single;
1488 MultipleSubst multiple;
1489 AlternateSubst alternate;
1490 LigatureSubst ligature;
1491 ContextSubst context;
1492 ChainContextSubst chainContext;
1493 ExtensionSubst extension;
1494 ReverseChainSingleSubst reverseChainContextSingle;
1495 } u;
1496 public:
1497 DEFINE_SIZE_MIN (0);
1498 };
1499
1500
1501 struct SubstLookup : Lookup
1502 {
1503 typedef SubstLookupSubTable SubTable;
1504
get_subtableOT::SubstLookup1505 const SubTable& get_subtable (unsigned int i) const
1506 { return Lookup::get_subtable<SubTable> (i); }
1507
lookup_type_is_reverseOT::SubstLookup1508 static inline bool lookup_type_is_reverse (unsigned int lookup_type)
1509 { return lookup_type == SubTable::ReverseChainSingle; }
1510
is_reverseOT::SubstLookup1511 bool is_reverse () const
1512 {
1513 unsigned int type = get_type ();
1514 if (unlikely (type == SubTable::Extension))
1515 return reinterpret_cast<const ExtensionSubst &> (get_subtable (0)).is_reverse ();
1516 return lookup_type_is_reverse (type);
1517 }
1518
may_have_non_1to1OT::SubstLookup1519 bool may_have_non_1to1 () const
1520 {
1521 hb_have_non_1to1_context_t c;
1522 return dispatch (&c);
1523 }
1524
applyOT::SubstLookup1525 bool apply (hb_ot_apply_context_t *c) const
1526 {
1527 TRACE_APPLY (this);
1528 return_trace (dispatch (c));
1529 }
1530
intersectsOT::SubstLookup1531 bool intersects (const hb_set_t *glyphs) const
1532 {
1533 hb_intersects_context_t c (glyphs);
1534 return dispatch (&c);
1535 }
1536
closureOT::SubstLookup1537 hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
1538 {
1539 if (!c->should_visit_lookup (this_index))
1540 return hb_closure_context_t::default_return_value ();
1541
1542 c->set_recurse_func (dispatch_closure_recurse_func);
1543
1544 hb_closure_context_t::return_t ret = dispatch (c);
1545
1546 c->flush ();
1547
1548 return ret;
1549 }
1550
closure_lookupsOT::SubstLookup1551 hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const
1552 {
1553 if (c->is_lookup_visited (this_index))
1554 return hb_closure_lookups_context_t::default_return_value ();
1555
1556 c->set_lookup_visited (this_index);
1557 if (!intersects (c->glyphs))
1558 {
1559 c->set_lookup_inactive (this_index);
1560 return hb_closure_lookups_context_t::default_return_value ();
1561 }
1562
1563 c->set_recurse_func (dispatch_closure_lookups_recurse_func);
1564
1565 hb_closure_lookups_context_t::return_t ret = dispatch (c);
1566 return ret;
1567 }
1568
collect_glyphsOT::SubstLookup1569 hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
1570 {
1571 c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
1572 return dispatch (c);
1573 }
1574
1575 template <typename set_t>
collect_coverageOT::SubstLookup1576 void collect_coverage (set_t *glyphs) const
1577 {
1578 hb_collect_coverage_context_t<set_t> c (glyphs);
1579 dispatch (&c);
1580 }
1581
would_applyOT::SubstLookup1582 bool would_apply (hb_would_apply_context_t *c,
1583 const hb_ot_layout_lookup_accelerator_t *accel) const
1584 {
1585 if (unlikely (!c->len)) return false;
1586 if (!accel->may_have (c->glyphs[0])) return false;
1587 return dispatch (c);
1588 }
1589
1590 static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
1591
serialize_singleOT::SubstLookup1592 bool serialize_single (hb_serialize_context_t *c,
1593 uint32_t lookup_props,
1594 hb_sorted_array_t<const HBGlyphID16> glyphs,
1595 hb_array_t<const HBGlyphID16> substitutes)
1596 {
1597 TRACE_SERIALIZE (this);
1598 if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
1599 if (c->push<SubTable> ()->u.single.serialize (c, hb_zip (glyphs, substitutes)))
1600 {
1601 c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
1602 return_trace (true);
1603 }
1604 c->pop_discard ();
1605 return_trace (false);
1606 }
1607
serialize_multipleOT::SubstLookup1608 bool serialize_multiple (hb_serialize_context_t *c,
1609 uint32_t lookup_props,
1610 hb_sorted_array_t<const HBGlyphID16> glyphs,
1611 hb_array_t<const unsigned int> substitute_len_list,
1612 hb_array_t<const HBGlyphID16> substitute_glyphs_list)
1613 {
1614 TRACE_SERIALIZE (this);
1615 if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false);
1616 if (c->push<SubTable> ()->u.multiple.
1617 serialize (c,
1618 glyphs,
1619 substitute_len_list,
1620 substitute_glyphs_list))
1621 {
1622 c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
1623 return_trace (true);
1624 }
1625 c->pop_discard ();
1626 return_trace (false);
1627 }
1628
serialize_alternateOT::SubstLookup1629 bool serialize_alternate (hb_serialize_context_t *c,
1630 uint32_t lookup_props,
1631 hb_sorted_array_t<const HBGlyphID16> glyphs,
1632 hb_array_t<const unsigned int> alternate_len_list,
1633 hb_array_t<const HBGlyphID16> alternate_glyphs_list)
1634 {
1635 TRACE_SERIALIZE (this);
1636 if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false);
1637
1638 if (c->push<SubTable> ()->u.alternate.
1639 serialize (c,
1640 glyphs,
1641 alternate_len_list,
1642 alternate_glyphs_list))
1643 {
1644 c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
1645 return_trace (true);
1646 }
1647 c->pop_discard ();
1648 return_trace (false);
1649 }
1650
serialize_ligatureOT::SubstLookup1651 bool serialize_ligature (hb_serialize_context_t *c,
1652 uint32_t lookup_props,
1653 hb_sorted_array_t<const HBGlyphID16> first_glyphs,
1654 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
1655 hb_array_t<const HBGlyphID16> ligatures_list,
1656 hb_array_t<const unsigned int> component_count_list,
1657 hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
1658 {
1659 TRACE_SERIALIZE (this);
1660 if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false);
1661 if (c->push<SubTable> ()->u.ligature.
1662 serialize (c,
1663 first_glyphs,
1664 ligature_per_first_glyph_count_list,
1665 ligatures_list,
1666 component_count_list,
1667 component_list))
1668 {
1669 c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
1670 return_trace (true);
1671 }
1672 c->pop_discard ();
1673 return_trace (false);
1674 }
1675
1676 template <typename context_t>
1677 static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
1678
1679 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);
1680
dispatch_closure_recurse_funcOT::SubstLookup1681 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)
1682 {
1683 if (!c->should_visit_lookup (lookup_index))
1684 return hb_empty_t ();
1685
1686 hb_closure_context_t::return_t ret = closure_glyphs_recurse_func (c, lookup_index, covered_seq_indices, seq_index, end_index);
1687
1688 /* While in theory we should flush here, it will cause timeouts because a recursive
1689 * lookup can keep growing the glyph set. Skip, and outer loop will retry up to
1690 * HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */
1691 //c->flush ();
1692
1693 return ret;
1694 }
1695
1696 HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned lookup_index);
1697
1698 template <typename context_t, typename ...Ts>
dispatchOT::SubstLookup1699 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1700 { return Lookup::dispatch<SubTable> (c, std::forward<Ts> (ds)...); }
1701
subsetOT::SubstLookup1702 bool subset (hb_subset_context_t *c) const
1703 { return Lookup::subset<SubTable> (c); }
1704
sanitizeOT::SubstLookup1705 bool sanitize (hb_sanitize_context_t *c) const
1706 { return Lookup::sanitize<SubTable> (c); }
1707 };
1708
1709 /*
1710 * GSUB -- Glyph Substitution
1711 * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub
1712 */
1713
1714 struct GSUB : GSUBGPOS
1715 {
1716 static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB;
1717
get_lookupOT::GSUB1718 const SubstLookup& get_lookup (unsigned int i) const
1719 { return static_cast<const SubstLookup &> (GSUBGPOS::get_lookup (i)); }
1720
subsetOT::GSUB1721 bool subset (hb_subset_context_t *c) const
1722 {
1723 hb_subset_layout_context_t l (c, tableTag, c->plan->gsub_lookups, c->plan->gsub_langsys, c->plan->gsub_features);
1724 return GSUBGPOS::subset<SubstLookup> (&l);
1725 }
1726
sanitizeOT::GSUB1727 bool sanitize (hb_sanitize_context_t *c) const
1728 { return GSUBGPOS::sanitize<SubstLookup> (c); }
1729
1730 HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
1731 hb_face_t *face) const;
1732
closure_lookupsOT::GSUB1733 void closure_lookups (hb_face_t *face,
1734 const hb_set_t *glyphs,
1735 hb_set_t *lookup_indexes /* IN/OUT */) const
1736 { GSUBGPOS::closure_lookups<SubstLookup> (face, glyphs, lookup_indexes); }
1737
1738 typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t;
1739 };
1740
1741
1742 struct GSUB_accelerator_t : GSUB::accelerator_t {};
1743
1744
1745 /* Out-of-class implementation for methods recursing */
1746
1747 #ifndef HB_NO_OT_LAYOUT
is_reverse() const1748 /*static*/ inline bool ExtensionSubst::is_reverse () const
1749 {
1750 return SubstLookup::lookup_type_is_reverse (get_type ());
1751 }
1752 template <typename context_t>
dispatch_recurse_func(context_t * c,unsigned int lookup_index)1753 /*static*/ typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
1754 {
1755 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1756 return l.dispatch (c);
1757 }
1758
closure_glyphs_recurse_func(hb_closure_context_t * c,unsigned lookup_index,hb_set_t * covered_seq_indices,unsigned seq_index,unsigned end_index)1759 /*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)
1760 {
1761 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1762 if (l.may_have_non_1to1 ())
1763 hb_set_add_range (covered_seq_indices, seq_index, end_index);
1764 return l.dispatch (c);
1765 }
1766
dispatch_closure_lookups_recurse_func(hb_closure_lookups_context_t * c,unsigned this_index)1767 /*static*/ inline hb_closure_lookups_context_t::return_t SubstLookup::dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index)
1768 {
1769 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (this_index);
1770 return l.closure_lookups (c, this_index);
1771 }
1772
apply_recurse_func(hb_ot_apply_context_t * c,unsigned int lookup_index)1773 /*static*/ bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
1774 {
1775 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1776 unsigned int saved_lookup_props = c->lookup_props;
1777 unsigned int saved_lookup_index = c->lookup_index;
1778 c->set_lookup_index (lookup_index);
1779 c->set_lookup_props (l.get_props ());
1780 bool ret = l.dispatch (c);
1781 c->set_lookup_index (saved_lookup_index);
1782 c->set_lookup_props (saved_lookup_props);
1783 return ret;
1784 }
1785 #endif
1786
1787
1788 } /* namespace OT */
1789
1790
1791 #endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */
1792