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
35namespace OT {
36
37typedef hb_pair_t<hb_codepoint_t, hb_codepoint_t> hb_codepoint_pair_t;
38
39template<typename Iterator>
40static void SingleSubst_serialize (hb_serialize_context_t *c,
41 Iterator it);
42
43
44struct SingleSubstFormat1
45{
46 bool intersects (const hb_set_t *glyphs) const
47 { return (this+coverage).intersects (glyphs); }
48
49 void closure (hb_closure_context_t *c) const
50 {
51 unsigned d = deltaGlyphID;
52 + hb_iter (this+coverage)
53 | hb_filter (*c->glyphs)
54 | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; })
55 | hb_sink (c->output)
56 ;
57 }
58
59 void closure_lookups (hb_closure_lookups_context_t *c) const {}
60
61 void collect_glyphs (hb_collect_glyphs_context_t *c) const
62 {
63 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
64 unsigned d = deltaGlyphID;
65 + hb_iter (this+coverage)
66 | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; })
67 | hb_sink (c->output)
68 ;
69 }
70
71 const Coverage &get_coverage () const { return this+coverage; }
72
73 bool would_apply (hb_would_apply_context_t *c) const
74 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
75
76 bool apply (hb_ot_apply_context_t *c) const
77 {
78 TRACE_APPLY (this);
79 hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
80 unsigned int index = (this+coverage).get_coverage (glyph_id);
81 if (likely (index == NOT_COVERED)) return_trace (false);
82
83 /* According to the Adobe Annotated OpenType Suite, result is always
84 * limited to 16bit. */
85 glyph_id = (glyph_id + deltaGlyphID) & 0xFFFFu;
86 c->replace_glyph (glyph_id);
87
88 return_trace (true);
89 }
90
91 template<typename Iterator,
92 hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
93 bool serialize (hb_serialize_context_t *c,
94 Iterator glyphs,
95 unsigned delta)
96 {
97 TRACE_SERIALIZE (this);
98 if (unlikely (!c->extend_min (*this))) return_trace (false);
99 if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false);
100 c->check_assign (deltaGlyphID, delta);
101 return_trace (true);
102 }
103
104 bool subset (hb_subset_context_t *c) const
105 {
106 TRACE_SUBSET (this);
107 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
108 const hb_map_t &glyph_map = *c->plan->glyph_map;
109
110 hb_codepoint_t delta = deltaGlyphID;
111
112 auto it =
113 + hb_iter (this+coverage)
114 | hb_filter (glyphset)
115 | hb_map_retains_sorting ([&] (hb_codepoint_t g) {
116 return hb_codepoint_pair_t (g,
117 (g + delta) & 0xFFFF); })
118 | hb_filter (glyphset, hb_second)
119 | hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t
120 { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
121 ;
122
123 bool ret = bool (it);
124 SingleSubst_serialize (c->serializer, it);
125 return_trace (ret);
126 }
127
128 bool sanitize (hb_sanitize_context_t *c) const
129 {
130 TRACE_SANITIZE (this);
131 return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
132 }
133
134 protected:
135 HBUINT16 format; /* Format identifier--format = 1 */
136 OffsetTo<Coverage>
137 coverage; /* Offset to Coverage table--from
138 * beginning of Substitution table */
139 HBUINT16 deltaGlyphID; /* Add to original GlyphID to get
140 * substitute GlyphID, modulo 0x10000 */
141 public:
142 DEFINE_SIZE_STATIC (6);
143};
144
145struct SingleSubstFormat2
146{
147 bool intersects (const hb_set_t *glyphs) const
148 { return (this+coverage).intersects (glyphs); }
149
150 void closure (hb_closure_context_t *c) const
151 {
152 + hb_zip (this+coverage, substitute)
153 | hb_filter (*c->glyphs, hb_first)
154 | hb_map (hb_second)
155 | hb_sink (c->output)
156 ;
157 }
158
159 void closure_lookups (hb_closure_lookups_context_t *c) const {}
160
161 void collect_glyphs (hb_collect_glyphs_context_t *c) const
162 {
163 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
164 + hb_zip (this+coverage, substitute)
165 | hb_map (hb_second)
166 | hb_sink (c->output)
167 ;
168 }
169
170 const Coverage &get_coverage () const { return this+coverage; }
171
172 bool would_apply (hb_would_apply_context_t *c) const
173 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
174
175 bool apply (hb_ot_apply_context_t *c) const
176 {
177 TRACE_APPLY (this);
178 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
179 if (likely (index == NOT_COVERED)) return_trace (false);
180
181 if (unlikely (index >= substitute.len)) return_trace (false);
182
183 c->replace_glyph (substitute[index]);
184
185 return_trace (true);
186 }
187
188 template<typename Iterator,
189 hb_requires (hb_is_sorted_source_of (Iterator,
190 hb_codepoint_pair_t))>
191 bool serialize (hb_serialize_context_t *c,
192 Iterator it)
193 {
194 TRACE_SERIALIZE (this);
195 auto substitutes =
196 + it
197 | hb_map (hb_second)
198 ;
199 auto glyphs =
200 + it
201 | hb_map_retains_sorting (hb_first)
202 ;
203 if (unlikely (!c->extend_min (*this))) return_trace (false);
204 if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false);
205 if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false);
206 return_trace (true);
207 }
208
209 bool subset (hb_subset_context_t *c) const
210 {
211 TRACE_SUBSET (this);
212 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
213 const hb_map_t &glyph_map = *c->plan->glyph_map;
214
215 auto it =
216 + hb_zip (this+coverage, substitute)
217 | hb_filter (glyphset, hb_first)
218 | hb_filter (glyphset, hb_second)
219 | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const HBGlyphID &> p) -> hb_codepoint_pair_t
220 { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
221 ;
222
223 bool ret = bool (it);
224 SingleSubst_serialize (c->serializer, it);
225 return_trace (ret);
226 }
227
228 bool sanitize (hb_sanitize_context_t *c) const
229 {
230 TRACE_SANITIZE (this);
231 return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
232 }
233
234 protected:
235 HBUINT16 format; /* Format identifier--format = 2 */
236 OffsetTo<Coverage>
237 coverage; /* Offset to Coverage table--from
238 * beginning of Substitution table */
239 ArrayOf<HBGlyphID>
240 substitute; /* Array of substitute
241 * GlyphIDs--ordered by Coverage Index */
242 public:
243 DEFINE_SIZE_ARRAY (6, substitute);
244};
245
246struct SingleSubst
247{
248
249 template<typename Iterator,
250 hb_requires (hb_is_sorted_source_of (Iterator,
251 const hb_codepoint_pair_t))>
252 bool serialize (hb_serialize_context_t *c,
253 Iterator glyphs)
254 {
255 TRACE_SERIALIZE (this);
256 if (unlikely (!c->extend_min (u.format))) return_trace (false);
257 unsigned format = 2;
258 unsigned delta = 0;
259 if (glyphs)
260 {
261 format = 1;
262 auto get_delta = [=] (hb_codepoint_pair_t _)
263 { return (unsigned) (_.second - _.first) & 0xFFFF; };
264 delta = get_delta (*glyphs);
265 if (!hb_all (++(+glyphs), delta, get_delta)) format = 2;
266 }
267 u.format = format;
268 switch (u.format) {
269 case 1: return_trace (u.format1.serialize (c,
270 + glyphs
271 | hb_map_retains_sorting (hb_first),
272 delta));
273 case 2: return_trace (u.format2.serialize (c, glyphs));
274 default:return_trace (false);
275 }
276 }
277
278 template <typename context_t, typename ...Ts>
279 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
280 {
281 TRACE_DISPATCH (this, u.format);
282 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
283 switch (u.format) {
284 case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
285 case 2: return_trace (c->dispatch (u.format2, hb_forward<Ts> (ds)...));
286 default:return_trace (c->default_return_value ());
287 }
288 }
289
290 protected:
291 union {
292 HBUINT16 format; /* Format identifier */
293 SingleSubstFormat1 format1;
294 SingleSubstFormat2 format2;
295 } u;
296};
297
298template<typename Iterator>
299static void
300SingleSubst_serialize (hb_serialize_context_t *c,
301 Iterator it)
302{ c->start_embed<SingleSubst> ()->serialize (c, it); }
303
304struct Sequence
305{
306 bool intersects (const hb_set_t *glyphs) const
307 { return hb_all (substitute, glyphs); }
308
309 void closure (hb_closure_context_t *c) const
310 { c->output->add_array (substitute.arrayZ, substitute.len); }
311
312 void collect_glyphs (hb_collect_glyphs_context_t *c) const
313 { c->output->add_array (substitute.arrayZ, substitute.len); }
314
315 bool apply (hb_ot_apply_context_t *c) const
316 {
317 TRACE_APPLY (this);
318 unsigned int count = substitute.len;
319
320 /* Special-case to make it in-place and not consider this
321 * as a "multiplied" substitution. */
322 if (unlikely (count == 1))
323 {
324 c->replace_glyph (substitute.arrayZ[0]);
325 return_trace (true);
326 }
327 /* Spec disallows this, but Uniscribe allows it.
328 * https://github.com/harfbuzz/harfbuzz/issues/253 */
329 else if (unlikely (count == 0))
330 {
331 c->buffer->delete_glyph ();
332 return_trace (true);
333 }
334
335 unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ?
336 HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
337
338 for (unsigned int i = 0; i < count; i++) {
339 _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i);
340 c->output_glyph_for_component (substitute.arrayZ[i], klass);
341 }
342 c->buffer->skip_glyph ();
343
344 return_trace (true);
345 }
346
347 template <typename Iterator,
348 hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
349 bool serialize (hb_serialize_context_t *c,
350 Iterator subst)
351 {
352 TRACE_SERIALIZE (this);
353 return_trace (substitute.serialize (c, subst));
354 }
355
356 bool subset (hb_subset_context_t *c) const
357 {
358 TRACE_SUBSET (this);
359 const hb_set_t &glyphset = *c->plan->glyphset ();
360 const hb_map_t &glyph_map = *c->plan->glyph_map;
361
362 if (!intersects (&glyphset)) return_trace (false);
363
364 auto it =
365 + hb_iter (substitute)
366 | hb_map (glyph_map)
367 ;
368
369 auto *out = c->serializer->start_embed (*this);
370 return_trace (out->serialize (c->serializer, it));
371 }
372
373 bool sanitize (hb_sanitize_context_t *c) const
374 {
375 TRACE_SANITIZE (this);
376 return_trace (substitute.sanitize (c));
377 }
378
379 protected:
380 ArrayOf<HBGlyphID>
381 substitute; /* String of GlyphIDs to substitute */
382 public:
383 DEFINE_SIZE_ARRAY (2, substitute);
384};
385
386struct MultipleSubstFormat1
387{
388 bool intersects (const hb_set_t *glyphs) const
389 { return (this+coverage).intersects (glyphs); }
390
391 void closure (hb_closure_context_t *c) const
392 {
393 + hb_zip (this+coverage, sequence)
394 | hb_filter (*c->glyphs, hb_first)
395 | hb_map (hb_second)
396 | hb_map (hb_add (this))
397 | hb_apply ([c] (const Sequence &_) { _.closure (c); })
398 ;
399 }
400
401 void closure_lookups (hb_closure_lookups_context_t *c) const {}
402
403 void collect_glyphs (hb_collect_glyphs_context_t *c) const
404 {
405 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
406 + hb_zip (this+coverage, sequence)
407 | hb_map (hb_second)
408 | hb_map (hb_add (this))
409 | hb_apply ([c] (const Sequence &_) { _.collect_glyphs (c); })
410 ;
411 }
412
413 const Coverage &get_coverage () const { return this+coverage; }
414
415 bool would_apply (hb_would_apply_context_t *c) const
416 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
417
418 bool apply (hb_ot_apply_context_t *c) const
419 {
420 TRACE_APPLY (this);
421
422 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
423 if (likely (index == NOT_COVERED)) return_trace (false);
424
425 return_trace ((this+sequence[index]).apply (c));
426 }
427
428 bool serialize (hb_serialize_context_t *c,
429 hb_sorted_array_t<const HBGlyphID> glyphs,
430 hb_array_t<const unsigned int> substitute_len_list,
431 hb_array_t<const HBGlyphID> substitute_glyphs_list)
432 {
433 TRACE_SERIALIZE (this);
434 if (unlikely (!c->extend_min (*this))) return_trace (false);
435 if (unlikely (!sequence.serialize (c, glyphs.length))) return_trace (false);
436 for (unsigned int i = 0; i < glyphs.length; i++)
437 {
438 unsigned int substitute_len = substitute_len_list[i];
439 if (unlikely (!sequence[i].serialize (c, this)
440 .serialize (c, substitute_glyphs_list.sub_array (0, substitute_len))))
441 return_trace (false);
442 substitute_glyphs_list += substitute_len;
443 }
444 return_trace (coverage.serialize (c, this).serialize (c, glyphs));
445 }
446
447 bool subset (hb_subset_context_t *c) const
448 {
449 TRACE_SUBSET (this);
450 const hb_set_t &glyphset = *c->plan->glyphset ();
451 const hb_map_t &glyph_map = *c->plan->glyph_map;
452
453 auto *out = c->serializer->start_embed (*this);
454 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
455 out->format = format;
456
457 hb_sorted_vector_t<hb_codepoint_t> new_coverage;
458 + hb_zip (this+coverage, sequence)
459 | hb_filter (glyphset, hb_first)
460 | hb_filter (subset_offset_array (c, out->sequence, this), hb_second)
461 | hb_map (hb_first)
462 | hb_map (glyph_map)
463 | hb_sink (new_coverage)
464 ;
465 out->coverage.serialize (c->serializer, out)
466 .serialize (c->serializer, new_coverage.iter ());
467 return_trace (bool (new_coverage));
468 }
469
470 bool sanitize (hb_sanitize_context_t *c) const
471 {
472 TRACE_SANITIZE (this);
473 return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this));
474 }
475
476 protected:
477 HBUINT16 format; /* Format identifier--format = 1 */
478 OffsetTo<Coverage>
479 coverage; /* Offset to Coverage table--from
480 * beginning of Substitution table */
481 OffsetArrayOf<Sequence>
482 sequence; /* Array of Sequence tables
483 * ordered by Coverage Index */
484 public:
485 DEFINE_SIZE_ARRAY (6, sequence);
486};
487
488struct MultipleSubst
489{
490 bool serialize (hb_serialize_context_t *c,
491 hb_sorted_array_t<const HBGlyphID> glyphs,
492 hb_array_t<const unsigned int> substitute_len_list,
493 hb_array_t<const HBGlyphID> substitute_glyphs_list)
494 {
495 TRACE_SERIALIZE (this);
496 if (unlikely (!c->extend_min (u.format))) return_trace (false);
497 unsigned int format = 1;
498 u.format = format;
499 switch (u.format) {
500 case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, substitute_glyphs_list));
501 default:return_trace (false);
502 }
503 }
504
505 template <typename context_t, typename ...Ts>
506 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
507 {
508 TRACE_DISPATCH (this, u.format);
509 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
510 switch (u.format) {
511 case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
512 default:return_trace (c->default_return_value ());
513 }
514 }
515
516 protected:
517 union {
518 HBUINT16 format; /* Format identifier */
519 MultipleSubstFormat1 format1;
520 } u;
521};
522
523struct AlternateSet
524{
525 bool intersects (const hb_set_t *glyphs) const
526 { return hb_any (alternates, glyphs); }
527
528 void closure (hb_closure_context_t *c) const
529 { c->output->add_array (alternates.arrayZ, alternates.len); }
530
531 void collect_glyphs (hb_collect_glyphs_context_t *c) const
532 { c->output->add_array (alternates.arrayZ, alternates.len); }
533
534 bool apply (hb_ot_apply_context_t *c) const
535 {
536 TRACE_APPLY (this);
537 unsigned int count = alternates.len;
538
539 if (unlikely (!count)) return_trace (false);
540
541 hb_mask_t glyph_mask = c->buffer->cur().mask;
542 hb_mask_t lookup_mask = c->lookup_mask;
543
544 /* Note: This breaks badly if two features enabled this lookup together. */
545 unsigned int shift = hb_ctz (lookup_mask);
546 unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
547
548 /* If alt_index is MAX_VALUE, randomize feature if it is the rand feature. */
549 if (alt_index == HB_OT_MAP_MAX_VALUE && c->random)
550 alt_index = c->random_number () % count + 1;
551
552 if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);
553
554 c->replace_glyph (alternates[alt_index - 1]);
555
556 return_trace (true);
557 }
558
559 unsigned
560 get_alternates (unsigned start_offset,
561 unsigned *alternate_count /* IN/OUT. May be NULL. */,
562 hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
563 {
564 if (alternates.len && alternate_count)
565 {
566 + alternates.sub_array (start_offset, alternate_count)
567 | hb_sink (hb_array (alternate_glyphs, *alternate_count))
568 ;
569 }
570 return alternates.len;
571 }
572
573 template <typename Iterator,
574 hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
575 bool serialize (hb_serialize_context_t *c,
576 Iterator alts)
577 {
578 TRACE_SERIALIZE (this);
579 return_trace (alternates.serialize (c, alts));
580 }
581
582 bool subset (hb_subset_context_t *c) const
583 {
584 TRACE_SUBSET (this);
585 const hb_set_t &glyphset = *c->plan->glyphset ();
586 const hb_map_t &glyph_map = *c->plan->glyph_map;
587
588 auto it =
589 + hb_iter (alternates)
590 | hb_filter (glyphset)
591 | hb_map (glyph_map)
592 ;
593
594 auto *out = c->serializer->start_embed (*this);
595 return_trace (out->serialize (c->serializer, it) &&
596 out->alternates);
597 }
598
599 bool sanitize (hb_sanitize_context_t *c) const
600 {
601 TRACE_SANITIZE (this);
602 return_trace (alternates.sanitize (c));
603 }
604
605 protected:
606 ArrayOf<HBGlyphID>
607 alternates; /* Array of alternate GlyphIDs--in
608 * arbitrary order */
609 public:
610 DEFINE_SIZE_ARRAY (2, alternates);
611};
612
613struct AlternateSubstFormat1
614{
615 bool intersects (const hb_set_t *glyphs) const
616 { return (this+coverage).intersects (glyphs); }
617
618 void closure (hb_closure_context_t *c) const
619 {
620 + hb_zip (this+coverage, alternateSet)
621 | hb_filter (c->glyphs, hb_first)
622 | hb_map (hb_second)
623 | hb_map (hb_add (this))
624 | hb_apply ([c] (const AlternateSet &_) { _.closure (c); })
625 ;
626 }
627
628 void closure_lookups (hb_closure_lookups_context_t *c) const {}
629
630 void collect_glyphs (hb_collect_glyphs_context_t *c) const
631 {
632 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
633 + hb_zip (this+coverage, alternateSet)
634 | hb_map (hb_second)
635 | hb_map (hb_add (this))
636 | hb_apply ([c] (const AlternateSet &_) { _.collect_glyphs (c); })
637 ;
638 }
639
640 const Coverage &get_coverage () const { return this+coverage; }
641
642 bool would_apply (hb_would_apply_context_t *c) const
643 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
644
645 unsigned
646 get_glyph_alternates (hb_codepoint_t gid,
647 unsigned start_offset,
648 unsigned *alternate_count /* IN/OUT. May be NULL. */,
649 hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
650 { return (this+alternateSet[(this+coverage).get_coverage (gid)])
651 .get_alternates (start_offset, alternate_count, alternate_glyphs); }
652
653 bool apply (hb_ot_apply_context_t *c) const
654 {
655 TRACE_APPLY (this);
656
657 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
658 if (likely (index == NOT_COVERED)) return_trace (false);
659
660 return_trace ((this+alternateSet[index]).apply (c));
661 }
662
663 bool serialize (hb_serialize_context_t *c,
664 hb_sorted_array_t<const HBGlyphID> glyphs,
665 hb_array_t<const unsigned int> alternate_len_list,
666 hb_array_t<const HBGlyphID> alternate_glyphs_list)
667 {
668 TRACE_SERIALIZE (this);
669 if (unlikely (!c->extend_min (*this))) return_trace (false);
670 if (unlikely (!alternateSet.serialize (c, glyphs.length))) return_trace (false);
671 for (unsigned int i = 0; i < glyphs.length; i++)
672 {
673 unsigned int alternate_len = alternate_len_list[i];
674 if (unlikely (!alternateSet[i].serialize (c, this)
675 .serialize (c, alternate_glyphs_list.sub_array (0, alternate_len))))
676 return_trace (false);
677 alternate_glyphs_list += alternate_len;
678 }
679 return_trace (coverage.serialize (c, this).serialize (c, glyphs));
680 }
681
682 bool subset (hb_subset_context_t *c) const
683 {
684 TRACE_SUBSET (this);
685 const hb_set_t &glyphset = *c->plan->glyphset ();
686 const hb_map_t &glyph_map = *c->plan->glyph_map;
687
688 auto *out = c->serializer->start_embed (*this);
689 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
690 out->format = format;
691
692 hb_sorted_vector_t<hb_codepoint_t> new_coverage;
693 + hb_zip (this+coverage, alternateSet)
694 | hb_filter (glyphset, hb_first)
695 | hb_filter (subset_offset_array (c, out->alternateSet, this), hb_second)
696 | hb_map (hb_first)
697 | hb_map (glyph_map)
698 | hb_sink (new_coverage)
699 ;
700 out->coverage.serialize (c->serializer, out)
701 .serialize (c->serializer, new_coverage.iter ());
702 return_trace (bool (new_coverage));
703 }
704
705 bool sanitize (hb_sanitize_context_t *c) const
706 {
707 TRACE_SANITIZE (this);
708 return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
709 }
710
711 protected:
712 HBUINT16 format; /* Format identifier--format = 1 */
713 OffsetTo<Coverage>
714 coverage; /* Offset to Coverage table--from
715 * beginning of Substitution table */
716 OffsetArrayOf<AlternateSet>
717 alternateSet; /* Array of AlternateSet tables
718 * ordered by Coverage Index */
719 public:
720 DEFINE_SIZE_ARRAY (6, alternateSet);
721};
722
723struct AlternateSubst
724{
725 bool serialize (hb_serialize_context_t *c,
726 hb_sorted_array_t<const HBGlyphID> glyphs,
727 hb_array_t<const unsigned int> alternate_len_list,
728 hb_array_t<const HBGlyphID> alternate_glyphs_list)
729 {
730 TRACE_SERIALIZE (this);
731 if (unlikely (!c->extend_min (u.format))) return_trace (false);
732 unsigned int format = 1;
733 u.format = format;
734 switch (u.format) {
735 case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list));
736 default:return_trace (false);
737 }
738 }
739
740 template <typename context_t, typename ...Ts>
741 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
742 {
743 TRACE_DISPATCH (this, u.format);
744 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
745 switch (u.format) {
746 case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
747 default:return_trace (c->default_return_value ());
748 }
749 }
750
751 protected:
752 union {
753 HBUINT16 format; /* Format identifier */
754 AlternateSubstFormat1 format1;
755 } u;
756};
757
758
759struct Ligature
760{
761 bool intersects (const hb_set_t *glyphs) const
762 { return hb_all (component, glyphs); }
763
764 void closure (hb_closure_context_t *c) const
765 {
766 if (!intersects (c->glyphs)) return;
767 c->output->add (ligGlyph);
768 }
769
770 void collect_glyphs (hb_collect_glyphs_context_t *c) const
771 {
772 c->input->add_array (component.arrayZ, component.get_length ());
773 c->output->add (ligGlyph);
774 }
775
776 bool would_apply (hb_would_apply_context_t *c) const
777 {
778 if (c->len != component.lenP1)
779 return false;
780
781 for (unsigned int i = 1; i < c->len; i++)
782 if (likely (c->glyphs[i] != component[i]))
783 return false;
784
785 return true;
786 }
787
788 bool apply (hb_ot_apply_context_t *c) const
789 {
790 TRACE_APPLY (this);
791 unsigned int count = component.lenP1;
792
793 if (unlikely (!count)) return_trace (false);
794
795 /* Special-case to make it in-place and not consider this
796 * as a "ligated" substitution. */
797 if (unlikely (count == 1))
798 {
799 c->replace_glyph (ligGlyph);
800 return_trace (true);
801 }
802
803 unsigned int total_component_count = 0;
804
805 unsigned int match_length = 0;
806 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
807
808 if (likely (!match_input (c, count,
809 &component[1],
810 match_glyph,
811 nullptr,
812 &match_length,
813 match_positions,
814 &total_component_count)))
815 return_trace (false);
816
817 ligate_input (c,
818 count,
819 match_positions,
820 match_length,
821 ligGlyph,
822 total_component_count);
823
824 return_trace (true);
825 }
826
827 template <typename Iterator,
828 hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
829 bool serialize (hb_serialize_context_t *c,
830 hb_codepoint_t ligature,
831 Iterator components /* Starting from second */)
832 {
833 TRACE_SERIALIZE (this);
834 if (unlikely (!c->extend_min (*this))) return_trace (false);
835 ligGlyph = ligature;
836 if (unlikely (!component.serialize (c, components))) return_trace (false);
837 return_trace (true);
838 }
839
840 bool subset (hb_subset_context_t *c) const
841 {
842 TRACE_SUBSET (this);
843 const hb_set_t &glyphset = *c->plan->glyphset ();
844 const hb_map_t &glyph_map = *c->plan->glyph_map;
845
846 if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false);
847
848 auto it =
849 + hb_iter (component)
850 | hb_map (glyph_map)
851 ;
852
853 auto *out = c->serializer->start_embed (*this);
854 return_trace (out->serialize (c->serializer,
855 glyph_map[ligGlyph],
856 it));
857 }
858
859 public:
860 bool sanitize (hb_sanitize_context_t *c) const
861 {
862 TRACE_SANITIZE (this);
863 return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
864 }
865
866 protected:
867 HBGlyphID ligGlyph; /* GlyphID of ligature to substitute */
868 HeadlessArrayOf<HBGlyphID>
869 component; /* Array of component GlyphIDs--start
870 * with the second component--ordered
871 * in writing direction */
872 public:
873 DEFINE_SIZE_ARRAY (4, component);
874};
875
876struct LigatureSet
877{
878 bool intersects (const hb_set_t *glyphs) const
879 {
880 return
881 + hb_iter (ligature)
882 | hb_map (hb_add (this))
883 | hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); })
884 | hb_any
885 ;
886 }
887
888 void closure (hb_closure_context_t *c) const
889 {
890 + hb_iter (ligature)
891 | hb_map (hb_add (this))
892 | hb_apply ([c] (const Ligature &_) { _.closure (c); })
893 ;
894 }
895
896 void collect_glyphs (hb_collect_glyphs_context_t *c) const
897 {
898 + hb_iter (ligature)
899 | hb_map (hb_add (this))
900 | hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); })
901 ;
902 }
903
904 bool would_apply (hb_would_apply_context_t *c) const
905 {
906 return
907 + hb_iter (ligature)
908 | hb_map (hb_add (this))
909 | hb_map ([c] (const Ligature &_) { return _.would_apply (c); })
910 | hb_any
911 ;
912 }
913
914 bool apply (hb_ot_apply_context_t *c) const
915 {
916 TRACE_APPLY (this);
917 unsigned int num_ligs = ligature.len;
918 for (unsigned int i = 0; i < num_ligs; i++)
919 {
920 const Ligature &lig = this+ligature[i];
921 if (lig.apply (c)) return_trace (true);
922 }
923
924 return_trace (false);
925 }
926
927 bool serialize (hb_serialize_context_t *c,
928 hb_array_t<const HBGlyphID> ligatures,
929 hb_array_t<const unsigned int> component_count_list,
930 hb_array_t<const HBGlyphID> &component_list /* Starting from second for each ligature */)
931 {
932 TRACE_SERIALIZE (this);
933 if (unlikely (!c->extend_min (*this))) return_trace (false);
934 if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false);
935 for (unsigned int i = 0; i < ligatures.length; i++)
936 {
937 unsigned int component_count = (unsigned) hb_max ((int) component_count_list[i] - 1, 0);
938 if (unlikely (!ligature[i].serialize (c, this)
939 .serialize (c,
940 ligatures[i],
941 component_list.sub_array (0, component_count))))
942 return_trace (false);
943 component_list += component_count;
944 }
945 return_trace (true);
946 }
947
948 bool subset (hb_subset_context_t *c) const
949 {
950 TRACE_SUBSET (this);
951 auto *out = c->serializer->start_embed (*this);
952 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
953
954 + hb_iter (ligature)
955 | hb_filter (subset_offset_array (c, out->ligature, this))
956 | hb_drain
957 ;
958 return_trace (bool (out->ligature));
959 }
960
961 bool sanitize (hb_sanitize_context_t *c) const
962 {
963 TRACE_SANITIZE (this);
964 return_trace (ligature.sanitize (c, this));
965 }
966
967 protected:
968 OffsetArrayOf<Ligature>
969 ligature; /* Array LigatureSet tables
970 * ordered by preference */
971 public:
972 DEFINE_SIZE_ARRAY (2, ligature);
973};
974
975struct LigatureSubstFormat1
976{
977 bool intersects (const hb_set_t *glyphs) const
978 {
979 return
980 + hb_zip (this+coverage, ligatureSet)
981 | hb_filter (*glyphs, hb_first)
982 | hb_map (hb_second)
983 | hb_map ([this, glyphs] (const OffsetTo<LigatureSet> &_)
984 { return (this+_).intersects (glyphs); })
985 | hb_any
986 ;
987 }
988
989 void closure (hb_closure_context_t *c) const
990 {
991 + hb_zip (this+coverage, ligatureSet)
992 | hb_filter (*c->glyphs, hb_first)
993 | hb_map (hb_second)
994 | hb_map (hb_add (this))
995 | hb_apply ([c] (const LigatureSet &_) { _.closure (c); })
996 ;
997 }
998
999 void closure_lookups (hb_closure_lookups_context_t *c) const {}
1000
1001 void collect_glyphs (hb_collect_glyphs_context_t *c) const
1002 {
1003 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
1004
1005 + hb_zip (this+coverage, ligatureSet)
1006 | hb_map (hb_second)
1007 | hb_map (hb_add (this))
1008 | hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); })
1009 ;
1010 }
1011
1012 const Coverage &get_coverage () const { return this+coverage; }
1013
1014 bool would_apply (hb_would_apply_context_t *c) const
1015 {
1016 unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
1017 if (likely (index == NOT_COVERED)) return false;
1018
1019 const LigatureSet &lig_set = this+ligatureSet[index];
1020 return lig_set.would_apply (c);
1021 }
1022
1023 bool apply (hb_ot_apply_context_t *c) const
1024 {
1025 TRACE_APPLY (this);
1026
1027 unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
1028 if (likely (index == NOT_COVERED)) return_trace (false);
1029
1030 const LigatureSet &lig_set = this+ligatureSet[index];
1031 return_trace (lig_set.apply (c));
1032 }
1033
1034 bool serialize (hb_serialize_context_t *c,
1035 hb_sorted_array_t<const HBGlyphID> first_glyphs,
1036 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
1037 hb_array_t<const HBGlyphID> ligatures_list,
1038 hb_array_t<const unsigned int> component_count_list,
1039 hb_array_t<const HBGlyphID> component_list /* Starting from second for each ligature */)
1040 {
1041 TRACE_SERIALIZE (this);
1042 if (unlikely (!c->extend_min (*this))) return_trace (false);
1043 if (unlikely (!ligatureSet.serialize (c, first_glyphs.length))) return_trace (false);
1044 for (unsigned int i = 0; i < first_glyphs.length; i++)
1045 {
1046 unsigned int ligature_count = ligature_per_first_glyph_count_list[i];
1047 if (unlikely (!ligatureSet[i].serialize (c, this)
1048 .serialize (c,
1049 ligatures_list.sub_array (0, ligature_count),
1050 component_count_list.sub_array (0, ligature_count),
1051 component_list))) return_trace (false);
1052 ligatures_list += ligature_count;
1053 component_count_list += ligature_count;
1054 }
1055 return_trace (coverage.serialize (c, this).serialize (c, first_glyphs));
1056 }
1057
1058 bool subset (hb_subset_context_t *c) const
1059 {
1060 TRACE_SUBSET (this);
1061 const hb_set_t &glyphset = *c->plan->glyphset ();
1062 const hb_map_t &glyph_map = *c->plan->glyph_map;
1063
1064 auto *out = c->serializer->start_embed (*this);
1065 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
1066 out->format = format;
1067
1068 hb_sorted_vector_t<hb_codepoint_t> new_coverage;
1069 + hb_zip (this+coverage, ligatureSet)
1070 | hb_filter (glyphset, hb_first)
1071 | hb_filter (subset_offset_array (c, out->ligatureSet, this), hb_second)
1072 | hb_map (hb_first)
1073 | hb_map (glyph_map)
1074 | hb_sink (new_coverage)
1075 ;
1076 out->coverage.serialize (c->serializer, out)
1077 .serialize (c->serializer, new_coverage.iter ());
1078 return_trace (bool (new_coverage));
1079 }
1080
1081 bool sanitize (hb_sanitize_context_t *c) const
1082 {
1083 TRACE_SANITIZE (this);
1084 return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
1085 }
1086
1087 protected:
1088 HBUINT16 format; /* Format identifier--format = 1 */
1089 OffsetTo<Coverage>
1090 coverage; /* Offset to Coverage table--from
1091 * beginning of Substitution table */
1092 OffsetArrayOf<LigatureSet>
1093 ligatureSet; /* Array LigatureSet tables
1094 * ordered by Coverage Index */
1095 public:
1096 DEFINE_SIZE_ARRAY (6, ligatureSet);
1097};
1098
1099struct LigatureSubst
1100{
1101 bool serialize (hb_serialize_context_t *c,
1102 hb_sorted_array_t<const HBGlyphID> first_glyphs,
1103 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
1104 hb_array_t<const HBGlyphID> ligatures_list,
1105 hb_array_t<const unsigned int> component_count_list,
1106 hb_array_t<const HBGlyphID> component_list /* Starting from second for each ligature */)
1107 {
1108 TRACE_SERIALIZE (this);
1109 if (unlikely (!c->extend_min (u.format))) return_trace (false);
1110 unsigned int format = 1;
1111 u.format = format;
1112 switch (u.format) {
1113 case 1: return_trace (u.format1.serialize (c,
1114 first_glyphs,
1115 ligature_per_first_glyph_count_list,
1116 ligatures_list,
1117 component_count_list,
1118 component_list));
1119 default:return_trace (false);
1120 }
1121 }
1122
1123 template <typename context_t, typename ...Ts>
1124 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1125 {
1126 TRACE_DISPATCH (this, u.format);
1127 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
1128 switch (u.format) {
1129 case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
1130 default:return_trace (c->default_return_value ());
1131 }
1132 }
1133
1134 protected:
1135 union {
1136 HBUINT16 format; /* Format identifier */
1137 LigatureSubstFormat1 format1;
1138 } u;
1139};
1140
1141
1142struct ContextSubst : Context {};
1143
1144struct ChainContextSubst : ChainContext {};
1145
1146struct ExtensionSubst : Extension<ExtensionSubst>
1147{
1148 typedef struct SubstLookupSubTable SubTable;
1149 bool is_reverse () const;
1150};
1151
1152
1153struct ReverseChainSingleSubstFormat1
1154{
1155 bool intersects (const hb_set_t *glyphs) const
1156 {
1157 if (!(this+coverage).intersects (glyphs))
1158 return false;
1159
1160 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
1161
1162 unsigned int count;
1163
1164 count = backtrack.len;
1165 for (unsigned int i = 0; i < count; i++)
1166 if (!(this+backtrack[i]).intersects (glyphs))
1167 return false;
1168
1169 count = lookahead.len;
1170 for (unsigned int i = 0; i < count; i++)
1171 if (!(this+lookahead[i]).intersects (glyphs))
1172 return false;
1173
1174 return true;
1175 }
1176
1177 void closure (hb_closure_context_t *c) const
1178 {
1179 if (!intersects (c->glyphs)) return;
1180
1181 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
1182 const ArrayOf<HBGlyphID> &substitute = StructAfter<ArrayOf<HBGlyphID>> (lookahead);
1183
1184 + hb_zip (this+coverage, substitute)
1185 | hb_filter (*c->glyphs, hb_first)
1186 | hb_map (hb_second)
1187 | hb_sink (c->output)
1188 ;
1189 }
1190
1191 void closure_lookups (hb_closure_lookups_context_t *c) const {}
1192
1193 void collect_glyphs (hb_collect_glyphs_context_t *c) const
1194 {
1195 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
1196
1197 unsigned int count;
1198
1199 count = backtrack.len;
1200 for (unsigned int i = 0; i < count; i++)
1201 if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return;
1202
1203 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
1204 count = lookahead.len;
1205 for (unsigned int i = 0; i < count; i++)
1206 if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return;
1207
1208 const ArrayOf<HBGlyphID> &substitute = StructAfter<ArrayOf<HBGlyphID>> (lookahead);
1209 count = substitute.len;
1210 c->output->add_array (substitute.arrayZ, substitute.len);
1211 }
1212
1213 const Coverage &get_coverage () const { return this+coverage; }
1214
1215 bool would_apply (hb_would_apply_context_t *c) const
1216 { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
1217
1218 bool apply (hb_ot_apply_context_t *c) const
1219 {
1220 TRACE_APPLY (this);
1221 if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
1222 return_trace (false); /* No chaining to this type */
1223
1224 unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
1225 if (likely (index == NOT_COVERED)) return_trace (false);
1226
1227 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
1228 const ArrayOf<HBGlyphID> &substitute = StructAfter<ArrayOf<HBGlyphID>> (lookahead);
1229
1230 if (unlikely (index >= substitute.len)) return_trace (false);
1231
1232 unsigned int start_index = 0, end_index = 0;
1233 if (match_backtrack (c,
1234 backtrack.len, (HBUINT16 *) backtrack.arrayZ,
1235 match_coverage, this,
1236 &start_index) &&
1237 match_lookahead (c,
1238 lookahead.len, (HBUINT16 *) lookahead.arrayZ,
1239 match_coverage, this,
1240 1, &end_index))
1241 {
1242 c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
1243 c->replace_glyph_inplace (substitute[index]);
1244 /* Note: We DON'T decrease buffer->idx. The main loop does it
1245 * for us. This is useful for preventing surprises if someone
1246 * calls us through a Context lookup. */
1247 return_trace (true);
1248 }
1249
1250 return_trace (false);
1251 }
1252
1253 bool subset (hb_subset_context_t *c) const
1254 {
1255 TRACE_SUBSET (this);
1256 // TODO(subset)
1257 return_trace (false);
1258 }
1259
1260 bool sanitize (hb_sanitize_context_t *c) const
1261 {
1262 TRACE_SANITIZE (this);
1263 if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
1264 return_trace (false);
1265 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
1266 if (!lookahead.sanitize (c, this))
1267 return_trace (false);
1268 const ArrayOf<HBGlyphID> &substitute = StructAfter<ArrayOf<HBGlyphID>> (lookahead);
1269 return_trace (substitute.sanitize (c));
1270 }
1271
1272 protected:
1273 HBUINT16 format; /* Format identifier--format = 1 */
1274 OffsetTo<Coverage>
1275 coverage; /* Offset to Coverage table--from
1276 * beginning of table */
1277 OffsetArrayOf<Coverage>
1278 backtrack; /* Array of coverage tables
1279 * in backtracking sequence, in glyph
1280 * sequence order */
1281 OffsetArrayOf<Coverage>
1282 lookaheadX; /* Array of coverage tables
1283 * in lookahead sequence, in glyph
1284 * sequence order */
1285 ArrayOf<HBGlyphID>
1286 substituteX; /* Array of substitute
1287 * GlyphIDs--ordered by Coverage Index */
1288 public:
1289 DEFINE_SIZE_MIN (10);
1290};
1291
1292struct ReverseChainSingleSubst
1293{
1294 template <typename context_t, typename ...Ts>
1295 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1296 {
1297 TRACE_DISPATCH (this, u.format);
1298 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
1299 switch (u.format) {
1300 case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
1301 default:return_trace (c->default_return_value ());
1302 }
1303 }
1304
1305 protected:
1306 union {
1307 HBUINT16 format; /* Format identifier */
1308 ReverseChainSingleSubstFormat1 format1;
1309 } u;
1310};
1311
1312
1313
1314/*
1315 * SubstLookup
1316 */
1317
1318struct SubstLookupSubTable
1319{
1320 friend struct Lookup;
1321 friend struct SubstLookup;
1322
1323 enum Type {
1324 Single = 1,
1325 Multiple = 2,
1326 Alternate = 3,
1327 Ligature = 4,
1328 Context = 5,
1329 ChainContext = 6,
1330 Extension = 7,
1331 ReverseChainSingle = 8
1332 };
1333
1334 template <typename context_t, typename ...Ts>
1335 typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const
1336 {
1337 TRACE_DISPATCH (this, lookup_type);
1338 switch (lookup_type) {
1339 case Single: return_trace (u.single.dispatch (c, hb_forward<Ts> (ds)...));
1340 case Multiple: return_trace (u.multiple.dispatch (c, hb_forward<Ts> (ds)...));
1341 case Alternate: return_trace (u.alternate.dispatch (c, hb_forward<Ts> (ds)...));
1342 case Ligature: return_trace (u.ligature.dispatch (c, hb_forward<Ts> (ds)...));
1343 case Context: return_trace (u.context.dispatch (c, hb_forward<Ts> (ds)...));
1344 case ChainContext: return_trace (u.chainContext.dispatch (c, hb_forward<Ts> (ds)...));
1345 case Extension: return_trace (u.extension.dispatch (c, hb_forward<Ts> (ds)...));
1346 case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c, hb_forward<Ts> (ds)...));
1347 default: return_trace (c->default_return_value ());
1348 }
1349 }
1350
1351 bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const
1352 {
1353 hb_intersects_context_t c (glyphs);
1354 return dispatch (&c, lookup_type);
1355 }
1356
1357 protected:
1358 union {
1359 SingleSubst single;
1360 MultipleSubst multiple;
1361 AlternateSubst alternate;
1362 LigatureSubst ligature;
1363 ContextSubst context;
1364 ChainContextSubst chainContext;
1365 ExtensionSubst extension;
1366 ReverseChainSingleSubst reverseChainContextSingle;
1367 } u;
1368 public:
1369 DEFINE_SIZE_MIN (0);
1370};
1371
1372
1373struct SubstLookup : Lookup
1374{
1375 typedef SubstLookupSubTable SubTable;
1376
1377 const SubTable& get_subtable (unsigned int i) const
1378 { return Lookup::get_subtable<SubTable> (i); }
1379
1380 static inline bool lookup_type_is_reverse (unsigned int lookup_type)
1381 { return lookup_type == SubTable::ReverseChainSingle; }
1382
1383 bool is_reverse () const
1384 {
1385 unsigned int type = get_type ();
1386 if (unlikely (type == SubTable::Extension))
1387 return reinterpret_cast<const ExtensionSubst &> (get_subtable (0)).is_reverse ();
1388 return lookup_type_is_reverse (type);
1389 }
1390
1391 bool apply (hb_ot_apply_context_t *c) const
1392 {
1393 TRACE_APPLY (this);
1394 return_trace (dispatch (c));
1395 }
1396
1397 bool intersects (const hb_set_t *glyphs) const
1398 {
1399 hb_intersects_context_t c (glyphs);
1400 return dispatch (&c);
1401 }
1402
1403 hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
1404 {
1405 if (!c->should_visit_lookup (this_index))
1406 return hb_closure_context_t::default_return_value ();
1407
1408 c->set_recurse_func (dispatch_closure_recurse_func);
1409
1410 hb_closure_context_t::return_t ret = dispatch (c);
1411
1412 c->flush ();
1413
1414 return ret;
1415 }
1416
1417 hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const
1418 {
1419 if (c->is_lookup_visited (this_index))
1420 return hb_closure_lookups_context_t::default_return_value ();
1421
1422 c->set_lookup_visited (this_index);
1423 if (!intersects (c->glyphs))
1424 {
1425 c->set_lookup_inactive (this_index);
1426 return hb_closure_lookups_context_t::default_return_value ();
1427 }
1428
1429 c->set_recurse_func (dispatch_closure_lookups_recurse_func);
1430
1431 hb_closure_lookups_context_t::return_t ret = dispatch (c);
1432 return ret;
1433 }
1434
1435 hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
1436 {
1437 c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
1438 return dispatch (c);
1439 }
1440
1441 template <typename set_t>
1442 void collect_coverage (set_t *glyphs) const
1443 {
1444 hb_collect_coverage_context_t<set_t> c (glyphs);
1445 dispatch (&c);
1446 }
1447
1448 bool would_apply (hb_would_apply_context_t *c,
1449 const hb_ot_layout_lookup_accelerator_t *accel) const
1450 {
1451 if (unlikely (!c->len)) return false;
1452 if (!accel->may_have (c->glyphs[0])) return false;
1453 return dispatch (c);
1454 }
1455
1456 static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
1457
1458 SubTable& serialize_subtable (hb_serialize_context_t *c,
1459 unsigned int i)
1460 { return get_subtables<SubTable> ()[i].serialize (c, this); }
1461
1462 bool serialize_single (hb_serialize_context_t *c,
1463 uint32_t lookup_props,
1464 hb_sorted_array_t<const HBGlyphID> glyphs,
1465 hb_array_t<const HBGlyphID> substitutes)
1466 {
1467 TRACE_SERIALIZE (this);
1468 if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
1469 return_trace (serialize_subtable (c, 0).u.single.
1470 serialize (c, hb_zip (glyphs, substitutes)));
1471 }
1472
1473 bool serialize_multiple (hb_serialize_context_t *c,
1474 uint32_t lookup_props,
1475 hb_sorted_array_t<const HBGlyphID> glyphs,
1476 hb_array_t<const unsigned int> substitute_len_list,
1477 hb_array_t<const HBGlyphID> substitute_glyphs_list)
1478 {
1479 TRACE_SERIALIZE (this);
1480 if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false);
1481 return_trace (serialize_subtable (c, 0).u.multiple.
1482 serialize (c,
1483 glyphs,
1484 substitute_len_list,
1485 substitute_glyphs_list));
1486 }
1487
1488 bool serialize_alternate (hb_serialize_context_t *c,
1489 uint32_t lookup_props,
1490 hb_sorted_array_t<const HBGlyphID> glyphs,
1491 hb_array_t<const unsigned int> alternate_len_list,
1492 hb_array_t<const HBGlyphID> alternate_glyphs_list)
1493 {
1494 TRACE_SERIALIZE (this);
1495 if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false);
1496 return_trace (serialize_subtable (c, 0).u.alternate.
1497 serialize (c,
1498 glyphs,
1499 alternate_len_list,
1500 alternate_glyphs_list));
1501 }
1502
1503 bool serialize_ligature (hb_serialize_context_t *c,
1504 uint32_t lookup_props,
1505 hb_sorted_array_t<const HBGlyphID> first_glyphs,
1506 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
1507 hb_array_t<const HBGlyphID> ligatures_list,
1508 hb_array_t<const unsigned int> component_count_list,
1509 hb_array_t<const HBGlyphID> component_list /* Starting from second for each ligature */)
1510 {
1511 TRACE_SERIALIZE (this);
1512 if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false);
1513 return_trace (serialize_subtable (c, 0).u.ligature.
1514 serialize (c,
1515 first_glyphs,
1516 ligature_per_first_glyph_count_list,
1517 ligatures_list,
1518 component_count_list,
1519 component_list));
1520 }
1521
1522 template <typename context_t>
1523 static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
1524
1525 static inline hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index)
1526 {
1527 if (!c->should_visit_lookup (lookup_index))
1528 return hb_empty_t ();
1529
1530 hb_closure_context_t::return_t ret = dispatch_recurse_func (c, lookup_index);
1531
1532 /* While in theory we should flush here, it will cause timeouts because a recursive
1533 * lookup can keep growing the glyph set. Skip, and outer loop will retry up to
1534 * HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */
1535 //c->flush ();
1536
1537 return ret;
1538 }
1539
1540 HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned lookup_index);
1541
1542 template <typename context_t, typename ...Ts>
1543 typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1544 { return Lookup::dispatch<SubTable> (c, hb_forward<Ts> (ds)...); }
1545
1546 bool subset (hb_subset_context_t *c) const
1547 { return Lookup::subset<SubTable> (c); }
1548
1549 bool sanitize (hb_sanitize_context_t *c) const
1550 { return Lookup::sanitize<SubTable> (c); }
1551};
1552
1553/*
1554 * GSUB -- Glyph Substitution
1555 * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub
1556 */
1557
1558struct GSUB : GSUBGPOS
1559{
1560 static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB;
1561
1562 const SubstLookup& get_lookup (unsigned int i) const
1563 { return static_cast<const SubstLookup &> (GSUBGPOS::get_lookup (i)); }
1564
1565 bool subset (hb_subset_context_t *c) const
1566 {
1567 hb_subset_layout_context_t l (c, tableTag, c->plan->gsub_lookups, c->plan->gsub_features);
1568 return GSUBGPOS::subset<SubstLookup> (&l);
1569 }
1570
1571 bool sanitize (hb_sanitize_context_t *c) const
1572 { return GSUBGPOS::sanitize<SubstLookup> (c); }
1573
1574 HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
1575 hb_face_t *face) const;
1576
1577 void closure_lookups (hb_face_t *face,
1578 const hb_set_t *glyphs,
1579 hb_set_t *lookup_indexes /* IN/OUT */) const
1580 { GSUBGPOS::closure_lookups<SubstLookup> (face, glyphs, lookup_indexes); }
1581
1582 typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t;
1583};
1584
1585
1586struct GSUB_accelerator_t : GSUB::accelerator_t {};
1587
1588
1589/* Out-of-class implementation for methods recursing */
1590
1591#ifndef HB_NO_OT_LAYOUT
1592/*static*/ inline bool ExtensionSubst::is_reverse () const
1593{
1594 return SubstLookup::lookup_type_is_reverse (get_type ());
1595}
1596template <typename context_t>
1597/*static*/ typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
1598{
1599 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1600 return l.dispatch (c);
1601}
1602
1603/*static*/ inline hb_closure_lookups_context_t::return_t SubstLookup::dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index)
1604{
1605 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (this_index);
1606 return l.closure_lookups (c, this_index);
1607}
1608
1609/*static*/ bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
1610{
1611 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1612 unsigned int saved_lookup_props = c->lookup_props;
1613 unsigned int saved_lookup_index = c->lookup_index;
1614 c->set_lookup_index (lookup_index);
1615 c->set_lookup_props (l.get_props ());
1616 bool ret = l.dispatch (c);
1617 c->set_lookup_index (saved_lookup_index);
1618 c->set_lookup_props (saved_lookup_props);
1619 return ret;
1620}
1621#endif
1622
1623
1624} /* namespace OT */
1625
1626
1627#endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */
1628