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
38 static inline void SingleSubst_serialize (hb_serialize_context_t *c,
39 hb_array_t<const GlyphID> glyphs,
40 hb_array_t<const GlyphID> substitutes);
41
42 struct SingleSubstFormat1
43 {
intersectsOT::SingleSubstFormat144 bool intersects (const hb_set_t *glyphs) const
45 { return (this+coverage).intersects (glyphs); }
46
closureOT::SingleSubstFormat147 void closure (hb_closure_context_t *c) const
48 {
49 TRACE_CLOSURE (this);
50 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
51 {
52 /* TODO Switch to range-based API to work around malicious fonts.
53 * https://github.com/harfbuzz/harfbuzz/issues/363 */
54 hb_codepoint_t glyph_id = iter.get_glyph ();
55 if (c->glyphs->has (glyph_id))
56 c->out->add ((glyph_id + deltaGlyphID) & 0xFFFFu);
57 }
58 }
59
collect_glyphsOT::SingleSubstFormat160 void collect_glyphs (hb_collect_glyphs_context_t *c) const
61 {
62 TRACE_COLLECT_GLYPHS (this);
63 if (unlikely (!(this+coverage).add_coverage (c->input))) return;
64 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
65 {
66 /* TODO Switch to range-based API to work around malicious fonts.
67 * https://github.com/harfbuzz/harfbuzz/issues/363 */
68 hb_codepoint_t glyph_id = iter.get_glyph ();
69 c->output->add ((glyph_id + deltaGlyphID) & 0xFFFFu);
70 }
71 }
72
get_coverageOT::SingleSubstFormat173 const Coverage &get_coverage () const { return this+coverage; }
74
would_applyOT::SingleSubstFormat175 bool would_apply (hb_would_apply_context_t *c) const
76 {
77 TRACE_WOULD_APPLY (this);
78 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
79 }
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
serializeOT::SingleSubstFormat196 bool serialize (hb_serialize_context_t *c,
97 hb_array_t<const GlyphID> glyphs,
98 int delta)
99 {
100 TRACE_SERIALIZE (this);
101 if (unlikely (!c->extend_min (*this))) return_trace (false);
102 if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false);
103 deltaGlyphID.set (delta); /* TODO(serialize) overflow? */
104 return_trace (true);
105 }
106
subsetOT::SingleSubstFormat1107 bool subset (hb_subset_context_t *c) const
108 {
109 TRACE_SUBSET (this);
110 const hb_set_t &glyphset = *c->plan->glyphset;
111 const hb_map_t &glyph_map = *c->plan->glyph_map;
112 hb_vector_t<GlyphID> from;
113 hb_vector_t<GlyphID> to;
114 hb_codepoint_t delta = deltaGlyphID;
115 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
116 {
117 if (!glyphset.has (iter.get_glyph ())) continue;
118 from.push ()->set (glyph_map[iter.get_glyph ()]);
119 to.push ()->set (glyph_map[(iter.get_glyph () + delta) & 0xFFFF]);
120 }
121 c->serializer->propagate_error (from, to);
122 SingleSubst_serialize (c->serializer, from, to);
123 return_trace (from.len);
124 }
125
sanitizeOT::SingleSubstFormat1126 bool sanitize (hb_sanitize_context_t *c) const
127 {
128 TRACE_SANITIZE (this);
129 return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
130 }
131
132 protected:
133 HBUINT16 format; /* Format identifier--format = 1 */
134 OffsetTo<Coverage>
135 coverage; /* Offset to Coverage table--from
136 * beginning of Substitution table */
137 HBINT16 deltaGlyphID; /* Add to original GlyphID to get
138 * substitute GlyphID */
139 public:
140 DEFINE_SIZE_STATIC (6);
141 };
142
143 struct SingleSubstFormat2
144 {
intersectsOT::SingleSubstFormat2145 bool intersects (const hb_set_t *glyphs) const
146 { return (this+coverage).intersects (glyphs); }
147
closureOT::SingleSubstFormat2148 void closure (hb_closure_context_t *c) const
149 {
150 TRACE_CLOSURE (this);
151 unsigned int count = substitute.len;
152 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
153 {
154 if (unlikely (iter.get_coverage () >= count))
155 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
156 if (c->glyphs->has (iter.get_glyph ()))
157 c->out->add (substitute[iter.get_coverage ()]);
158 }
159 }
160
collect_glyphsOT::SingleSubstFormat2161 void collect_glyphs (hb_collect_glyphs_context_t *c) const
162 {
163 TRACE_COLLECT_GLYPHS (this);
164 if (unlikely (!(this+coverage).add_coverage (c->input))) return;
165 unsigned int count = substitute.len;
166 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
167 {
168 if (unlikely (iter.get_coverage () >= count))
169 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
170 c->output->add (substitute[iter.get_coverage ()]);
171 }
172 }
173
get_coverageOT::SingleSubstFormat2174 const Coverage &get_coverage () const { return this+coverage; }
175
would_applyOT::SingleSubstFormat2176 bool would_apply (hb_would_apply_context_t *c) const
177 {
178 TRACE_WOULD_APPLY (this);
179 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
180 }
181
applyOT::SingleSubstFormat2182 bool apply (hb_ot_apply_context_t *c) const
183 {
184 TRACE_APPLY (this);
185 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
186 if (likely (index == NOT_COVERED)) return_trace (false);
187
188 if (unlikely (index >= substitute.len)) return_trace (false);
189
190 c->replace_glyph (substitute[index]);
191
192 return_trace (true);
193 }
194
serializeOT::SingleSubstFormat2195 bool serialize (hb_serialize_context_t *c,
196 hb_array_t<const GlyphID> glyphs,
197 hb_array_t<const GlyphID> substitutes)
198 {
199 TRACE_SERIALIZE (this);
200 if (unlikely (!c->extend_min (*this))) return_trace (false);
201 if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false);
202 if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false);
203 return_trace (true);
204 }
205
subsetOT::SingleSubstFormat2206 bool subset (hb_subset_context_t *c) const
207 {
208 TRACE_SUBSET (this);
209 const hb_set_t &glyphset = *c->plan->glyphset;
210 const hb_map_t &glyph_map = *c->plan->glyph_map;
211 hb_vector_t<GlyphID> from;
212 hb_vector_t<GlyphID> to;
213 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
214 {
215 if (!glyphset.has (iter.get_glyph ())) continue;
216 from.push ()->set (glyph_map[iter.get_glyph ()]);
217 to.push ()->set (glyph_map[substitute[iter.get_coverage ()]]);
218 }
219 c->serializer->propagate_error (from, to);
220 SingleSubst_serialize (c->serializer, from, to);
221 return_trace (from.len);
222 }
223
sanitizeOT::SingleSubstFormat2224 bool sanitize (hb_sanitize_context_t *c) const
225 {
226 TRACE_SANITIZE (this);
227 return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
228 }
229
230 protected:
231 HBUINT16 format; /* Format identifier--format = 2 */
232 OffsetTo<Coverage>
233 coverage; /* Offset to Coverage table--from
234 * beginning of Substitution table */
235 ArrayOf<GlyphID>
236 substitute; /* Array of substitute
237 * GlyphIDs--ordered by Coverage Index */
238 public:
239 DEFINE_SIZE_ARRAY (6, substitute);
240 };
241
242 struct SingleSubst
243 {
serializeOT::SingleSubst244 bool serialize (hb_serialize_context_t *c,
245 hb_array_t<const GlyphID> glyphs,
246 hb_array_t<const GlyphID> substitutes)
247 {
248 TRACE_SERIALIZE (this);
249 if (unlikely (!c->extend_min (u.format))) return_trace (false);
250 unsigned int format = 2;
251 int delta = 0;
252 if (glyphs.len)
253 {
254 format = 1;
255 /* TODO(serialize) check for wrap-around */
256 delta = substitutes[0] - glyphs[0];
257 for (unsigned int i = 1; i < glyphs.len; i++)
258 if (delta != (int) (substitutes[i] - glyphs[i])) {
259 format = 2;
260 break;
261 }
262 }
263 u.format.set (format);
264 switch (u.format) {
265 case 1: return_trace (u.format1.serialize (c, glyphs, delta));
266 case 2: return_trace (u.format2.serialize (c, glyphs, substitutes));
267 default:return_trace (false);
268 }
269 }
270
271 template <typename context_t>
dispatchOT::SingleSubst272 typename context_t::return_t dispatch (context_t *c) const
273 {
274 TRACE_DISPATCH (this, u.format);
275 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
276 switch (u.format) {
277 case 1: return_trace (c->dispatch (u.format1));
278 case 2: return_trace (c->dispatch (u.format2));
279 default:return_trace (c->default_return_value ());
280 }
281 }
282
283 protected:
284 union {
285 HBUINT16 format; /* Format identifier */
286 SingleSubstFormat1 format1;
287 SingleSubstFormat2 format2;
288 } u;
289 };
290
291 static inline void
SingleSubst_serialize(hb_serialize_context_t * c,hb_array_t<const GlyphID> glyphs,hb_array_t<const GlyphID> substitutes)292 SingleSubst_serialize (hb_serialize_context_t *c,
293 hb_array_t<const GlyphID> glyphs,
294 hb_array_t<const GlyphID> substitutes)
295 { c->start_embed<SingleSubst> ()->serialize (c, glyphs, substitutes); }
296
297 struct Sequence
298 {
closureOT::Sequence299 void closure (hb_closure_context_t *c) const
300 {
301 TRACE_CLOSURE (this);
302 unsigned int count = substitute.len;
303 for (unsigned int i = 0; i < count; i++)
304 c->out->add (substitute[i]);
305 }
306
collect_glyphsOT::Sequence307 void collect_glyphs (hb_collect_glyphs_context_t *c) const
308 {
309 TRACE_COLLECT_GLYPHS (this);
310 c->output->add_array (substitute.arrayZ, substitute.len);
311 }
312
applyOT::Sequence313 bool apply (hb_ot_apply_context_t *c) const
314 {
315 TRACE_APPLY (this);
316 unsigned int count = substitute.len;
317
318 /* Special-case to make it in-place and not consider this
319 * as a "multiplied" substitution. */
320 if (unlikely (count == 1))
321 {
322 c->replace_glyph (substitute.arrayZ[0]);
323 return_trace (true);
324 }
325 /* Spec disallows this, but Uniscribe allows it.
326 * https://github.com/harfbuzz/harfbuzz/issues/253 */
327 else if (unlikely (count == 0))
328 {
329 c->buffer->delete_glyph ();
330 return_trace (true);
331 }
332
333 unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ?
334 HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
335
336 for (unsigned int i = 0; i < count; i++) {
337 _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i);
338 c->output_glyph_for_component (substitute.arrayZ[i], klass);
339 }
340 c->buffer->skip_glyph ();
341
342 return_trace (true);
343 }
344
serializeOT::Sequence345 bool serialize (hb_serialize_context_t *c,
346 hb_array_t<const GlyphID> glyphs)
347 {
348 TRACE_SERIALIZE (this);
349 return_trace (substitute.serialize (c, glyphs));
350 }
351
sanitizeOT::Sequence352 bool sanitize (hb_sanitize_context_t *c) const
353 {
354 TRACE_SANITIZE (this);
355 return_trace (substitute.sanitize (c));
356 }
357
358 protected:
359 ArrayOf<GlyphID>
360 substitute; /* String of GlyphIDs to substitute */
361 public:
362 DEFINE_SIZE_ARRAY (2, substitute);
363 };
364
365 struct MultipleSubstFormat1
366 {
intersectsOT::MultipleSubstFormat1367 bool intersects (const hb_set_t *glyphs) const
368 { return (this+coverage).intersects (glyphs); }
369
closureOT::MultipleSubstFormat1370 void closure (hb_closure_context_t *c) const
371 {
372 TRACE_CLOSURE (this);
373 unsigned int count = sequence.len;
374 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
375 {
376 if (unlikely (iter.get_coverage () >= count))
377 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
378 if (c->glyphs->has (iter.get_glyph ()))
379 (this+sequence[iter.get_coverage ()]).closure (c);
380 }
381 }
382
collect_glyphsOT::MultipleSubstFormat1383 void collect_glyphs (hb_collect_glyphs_context_t *c) const
384 {
385 TRACE_COLLECT_GLYPHS (this);
386 if (unlikely (!(this+coverage).add_coverage (c->input))) return;
387 unsigned int count = sequence.len;
388 for (unsigned int i = 0; i < count; i++)
389 (this+sequence[i]).collect_glyphs (c);
390 }
391
get_coverageOT::MultipleSubstFormat1392 const Coverage &get_coverage () const { return this+coverage; }
393
would_applyOT::MultipleSubstFormat1394 bool would_apply (hb_would_apply_context_t *c) const
395 {
396 TRACE_WOULD_APPLY (this);
397 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
398 }
399
applyOT::MultipleSubstFormat1400 bool apply (hb_ot_apply_context_t *c) const
401 {
402 TRACE_APPLY (this);
403
404 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
405 if (likely (index == NOT_COVERED)) return_trace (false);
406
407 return_trace ((this+sequence[index]).apply (c));
408 }
409
serializeOT::MultipleSubstFormat1410 bool serialize (hb_serialize_context_t *c,
411 hb_array_t<const GlyphID> glyphs,
412 hb_array_t<const unsigned int> substitute_len_list,
413 hb_array_t<const GlyphID> substitute_glyphs_list)
414 {
415 TRACE_SERIALIZE (this);
416 if (unlikely (!c->extend_min (*this))) return_trace (false);
417 if (unlikely (!sequence.serialize (c, glyphs.len))) return_trace (false);
418 for (unsigned int i = 0; i < glyphs.len; i++)
419 {
420 unsigned int substitute_len = substitute_len_list[i];
421 if (unlikely (!sequence[i].serialize (c, this)
422 .serialize (c, substitute_glyphs_list.sub_array (0, substitute_len))))
423 return_trace (false);
424 substitute_glyphs_list += substitute_len;
425 }
426 return_trace (coverage.serialize (c, this).serialize (c, glyphs));
427 }
428
subsetOT::MultipleSubstFormat1429 bool subset (hb_subset_context_t *c) const
430 {
431 TRACE_SUBSET (this);
432 // TODO(subset)
433 return_trace (false);
434 }
435
sanitizeOT::MultipleSubstFormat1436 bool sanitize (hb_sanitize_context_t *c) const
437 {
438 TRACE_SANITIZE (this);
439 return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this));
440 }
441
442 protected:
443 HBUINT16 format; /* Format identifier--format = 1 */
444 OffsetTo<Coverage>
445 coverage; /* Offset to Coverage table--from
446 * beginning of Substitution table */
447 OffsetArrayOf<Sequence>
448 sequence; /* Array of Sequence tables
449 * ordered by Coverage Index */
450 public:
451 DEFINE_SIZE_ARRAY (6, sequence);
452 };
453
454 struct MultipleSubst
455 {
serializeOT::MultipleSubst456 bool serialize (hb_serialize_context_t *c,
457 hb_array_t<const GlyphID> glyphs,
458 hb_array_t<const unsigned int> substitute_len_list,
459 hb_array_t<const GlyphID> substitute_glyphs_list)
460 {
461 TRACE_SERIALIZE (this);
462 if (unlikely (!c->extend_min (u.format))) return_trace (false);
463 unsigned int format = 1;
464 u.format.set (format);
465 switch (u.format) {
466 case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, substitute_glyphs_list));
467 default:return_trace (false);
468 }
469 }
470
471 template <typename context_t>
dispatchOT::MultipleSubst472 typename context_t::return_t dispatch (context_t *c) const
473 {
474 TRACE_DISPATCH (this, u.format);
475 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
476 switch (u.format) {
477 case 1: return_trace (c->dispatch (u.format1));
478 default:return_trace (c->default_return_value ());
479 }
480 }
481
482 protected:
483 union {
484 HBUINT16 format; /* Format identifier */
485 MultipleSubstFormat1 format1;
486 } u;
487 };
488
489 struct AlternateSet
490 {
closureOT::AlternateSet491 void closure (hb_closure_context_t *c) const
492 {
493 TRACE_CLOSURE (this);
494 unsigned int count = alternates.len;
495 for (unsigned int i = 0; i < count; i++)
496 c->out->add (alternates[i]);
497 }
498
collect_glyphsOT::AlternateSet499 void collect_glyphs (hb_collect_glyphs_context_t *c) const
500 {
501 TRACE_COLLECT_GLYPHS (this);
502 c->output->add_array (alternates.arrayZ, alternates.len);
503 }
504
applyOT::AlternateSet505 bool apply (hb_ot_apply_context_t *c) const
506 {
507 TRACE_APPLY (this);
508 unsigned int count = alternates.len;
509
510 if (unlikely (!count)) return_trace (false);
511
512 hb_mask_t glyph_mask = c->buffer->cur().mask;
513 hb_mask_t lookup_mask = c->lookup_mask;
514
515 /* Note: This breaks badly if two features enabled this lookup together. */
516 unsigned int shift = hb_ctz (lookup_mask);
517 unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
518
519 /* If alt_index is MAX, randomize feature if it is the rand feature. */
520 if (alt_index == HB_OT_MAP_MAX_VALUE && c->random)
521 alt_index = c->random_number () % count + 1;
522
523 if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);
524
525 c->replace_glyph (alternates[alt_index - 1]);
526
527 return_trace (true);
528 }
529
serializeOT::AlternateSet530 bool serialize (hb_serialize_context_t *c,
531 hb_array_t<const GlyphID> glyphs)
532 {
533 TRACE_SERIALIZE (this);
534 return_trace (alternates.serialize (c, glyphs));
535 }
536
sanitizeOT::AlternateSet537 bool sanitize (hb_sanitize_context_t *c) const
538 {
539 TRACE_SANITIZE (this);
540 return_trace (alternates.sanitize (c));
541 }
542
543 protected:
544 ArrayOf<GlyphID>
545 alternates; /* Array of alternate GlyphIDs--in
546 * arbitrary order */
547 public:
548 DEFINE_SIZE_ARRAY (2, alternates);
549 };
550
551 struct AlternateSubstFormat1
552 {
intersectsOT::AlternateSubstFormat1553 bool intersects (const hb_set_t *glyphs) const
554 { return (this+coverage).intersects (glyphs); }
555
closureOT::AlternateSubstFormat1556 void closure (hb_closure_context_t *c) const
557 {
558 TRACE_CLOSURE (this);
559 unsigned int count = alternateSet.len;
560 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
561 {
562 if (unlikely (iter.get_coverage () >= count))
563 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
564 if (c->glyphs->has (iter.get_glyph ()))
565 (this+alternateSet[iter.get_coverage ()]).closure (c);
566 }
567 }
568
collect_glyphsOT::AlternateSubstFormat1569 void collect_glyphs (hb_collect_glyphs_context_t *c) const
570 {
571 TRACE_COLLECT_GLYPHS (this);
572 if (unlikely (!(this+coverage).add_coverage (c->input))) return;
573 unsigned int count = alternateSet.len;
574 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
575 {
576 if (unlikely (iter.get_coverage () >= count))
577 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
578 (this+alternateSet[iter.get_coverage ()]).collect_glyphs (c);
579 }
580 }
581
get_coverageOT::AlternateSubstFormat1582 const Coverage &get_coverage () const { return this+coverage; }
583
would_applyOT::AlternateSubstFormat1584 bool would_apply (hb_would_apply_context_t *c) const
585 {
586 TRACE_WOULD_APPLY (this);
587 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
588 }
589
applyOT::AlternateSubstFormat1590 bool apply (hb_ot_apply_context_t *c) const
591 {
592 TRACE_APPLY (this);
593
594 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
595 if (likely (index == NOT_COVERED)) return_trace (false);
596
597 return_trace ((this+alternateSet[index]).apply (c));
598 }
599
serializeOT::AlternateSubstFormat1600 bool serialize (hb_serialize_context_t *c,
601 hb_array_t<const GlyphID> glyphs,
602 hb_array_t<const unsigned int> alternate_len_list,
603 hb_array_t<const GlyphID> alternate_glyphs_list)
604 {
605 TRACE_SERIALIZE (this);
606 if (unlikely (!c->extend_min (*this))) return_trace (false);
607 if (unlikely (!alternateSet.serialize (c, glyphs.len))) return_trace (false);
608 for (unsigned int i = 0; i < glyphs.len; i++)
609 {
610 unsigned int alternate_len = alternate_len_list[i];
611 if (unlikely (!alternateSet[i].serialize (c, this)
612 .serialize (c, alternate_glyphs_list.sub_array (0, alternate_len))))
613 return_trace (false);
614 alternate_glyphs_list += alternate_len;
615 }
616 return_trace (coverage.serialize (c, this).serialize (c, glyphs));
617 }
618
subsetOT::AlternateSubstFormat1619 bool subset (hb_subset_context_t *c) const
620 {
621 TRACE_SUBSET (this);
622 // TODO(subset)
623 return_trace (false);
624 }
625
sanitizeOT::AlternateSubstFormat1626 bool sanitize (hb_sanitize_context_t *c) const
627 {
628 TRACE_SANITIZE (this);
629 return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
630 }
631
632 protected:
633 HBUINT16 format; /* Format identifier--format = 1 */
634 OffsetTo<Coverage>
635 coverage; /* Offset to Coverage table--from
636 * beginning of Substitution table */
637 OffsetArrayOf<AlternateSet>
638 alternateSet; /* Array of AlternateSet tables
639 * ordered by Coverage Index */
640 public:
641 DEFINE_SIZE_ARRAY (6, alternateSet);
642 };
643
644 struct AlternateSubst
645 {
serializeOT::AlternateSubst646 bool serialize (hb_serialize_context_t *c,
647 hb_array_t<const GlyphID> glyphs,
648 hb_array_t<const unsigned int> alternate_len_list,
649 hb_array_t<const GlyphID> alternate_glyphs_list)
650 {
651 TRACE_SERIALIZE (this);
652 if (unlikely (!c->extend_min (u.format))) return_trace (false);
653 unsigned int format = 1;
654 u.format.set (format);
655 switch (u.format) {
656 case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list));
657 default:return_trace (false);
658 }
659 }
660
661 template <typename context_t>
dispatchOT::AlternateSubst662 typename context_t::return_t dispatch (context_t *c) const
663 {
664 TRACE_DISPATCH (this, u.format);
665 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
666 switch (u.format) {
667 case 1: return_trace (c->dispatch (u.format1));
668 default:return_trace (c->default_return_value ());
669 }
670 }
671
672 protected:
673 union {
674 HBUINT16 format; /* Format identifier */
675 AlternateSubstFormat1 format1;
676 } u;
677 };
678
679
680 struct Ligature
681 {
intersectsOT::Ligature682 bool intersects (const hb_set_t *glyphs) const
683 {
684 unsigned int count = component.lenP1;
685 for (unsigned int i = 1; i < count; i++)
686 if (!glyphs->has (component[i]))
687 return false;
688 return true;
689 }
690
closureOT::Ligature691 void closure (hb_closure_context_t *c) const
692 {
693 TRACE_CLOSURE (this);
694 unsigned int count = component.lenP1;
695 for (unsigned int i = 1; i < count; i++)
696 if (!c->glyphs->has (component[i]))
697 return;
698 c->out->add (ligGlyph);
699 }
700
collect_glyphsOT::Ligature701 void collect_glyphs (hb_collect_glyphs_context_t *c) const
702 {
703 TRACE_COLLECT_GLYPHS (this);
704 c->input->add_array (component.arrayZ, component.lenP1 ? component.lenP1 - 1 : 0);
705 c->output->add (ligGlyph);
706 }
707
would_applyOT::Ligature708 bool would_apply (hb_would_apply_context_t *c) const
709 {
710 TRACE_WOULD_APPLY (this);
711 if (c->len != component.lenP1)
712 return_trace (false);
713
714 for (unsigned int i = 1; i < c->len; i++)
715 if (likely (c->glyphs[i] != component[i]))
716 return_trace (false);
717
718 return_trace (true);
719 }
720
applyOT::Ligature721 bool apply (hb_ot_apply_context_t *c) const
722 {
723 TRACE_APPLY (this);
724 unsigned int count = component.lenP1;
725
726 if (unlikely (!count)) return_trace (false);
727
728 /* Special-case to make it in-place and not consider this
729 * as a "ligated" substitution. */
730 if (unlikely (count == 1))
731 {
732 c->replace_glyph (ligGlyph);
733 return_trace (true);
734 }
735
736 unsigned int total_component_count = 0;
737
738 unsigned int match_length = 0;
739 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
740
741 if (likely (!match_input (c, count,
742 &component[1],
743 match_glyph,
744 nullptr,
745 &match_length,
746 match_positions,
747 &total_component_count)))
748 return_trace (false);
749
750 ligate_input (c,
751 count,
752 match_positions,
753 match_length,
754 ligGlyph,
755 total_component_count);
756
757 return_trace (true);
758 }
759
serializeOT::Ligature760 bool serialize (hb_serialize_context_t *c,
761 GlyphID ligature,
762 hb_array_t<const GlyphID> components /* Starting from second */)
763 {
764 TRACE_SERIALIZE (this);
765 if (unlikely (!c->extend_min (*this))) return_trace (false);
766 ligGlyph = ligature;
767 if (unlikely (!component.serialize (c, components))) return_trace (false);
768 return_trace (true);
769 }
770
771 public:
sanitizeOT::Ligature772 bool sanitize (hb_sanitize_context_t *c) const
773 {
774 TRACE_SANITIZE (this);
775 return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
776 }
777
778 protected:
779 GlyphID ligGlyph; /* GlyphID of ligature to substitute */
780 HeadlessArrayOf<GlyphID>
781 component; /* Array of component GlyphIDs--start
782 * with the second component--ordered
783 * in writing direction */
784 public:
785 DEFINE_SIZE_ARRAY (4, component);
786 };
787
788 struct LigatureSet
789 {
intersectsOT::LigatureSet790 bool intersects (const hb_set_t *glyphs) const
791 {
792 unsigned int num_ligs = ligature.len;
793 for (unsigned int i = 0; i < num_ligs; i++)
794 if ((this+ligature[i]).intersects (glyphs))
795 return true;
796 return false;
797 }
798
closureOT::LigatureSet799 void closure (hb_closure_context_t *c) const
800 {
801 TRACE_CLOSURE (this);
802 unsigned int num_ligs = ligature.len;
803 for (unsigned int i = 0; i < num_ligs; i++)
804 (this+ligature[i]).closure (c);
805 }
806
collect_glyphsOT::LigatureSet807 void collect_glyphs (hb_collect_glyphs_context_t *c) const
808 {
809 TRACE_COLLECT_GLYPHS (this);
810 unsigned int num_ligs = ligature.len;
811 for (unsigned int i = 0; i < num_ligs; i++)
812 (this+ligature[i]).collect_glyphs (c);
813 }
814
would_applyOT::LigatureSet815 bool would_apply (hb_would_apply_context_t *c) const
816 {
817 TRACE_WOULD_APPLY (this);
818 unsigned int num_ligs = ligature.len;
819 for (unsigned int i = 0; i < num_ligs; i++)
820 {
821 const Ligature &lig = this+ligature[i];
822 if (lig.would_apply (c))
823 return_trace (true);
824 }
825 return_trace (false);
826 }
827
applyOT::LigatureSet828 bool apply (hb_ot_apply_context_t *c) const
829 {
830 TRACE_APPLY (this);
831 unsigned int num_ligs = ligature.len;
832 for (unsigned int i = 0; i < num_ligs; i++)
833 {
834 const Ligature &lig = this+ligature[i];
835 if (lig.apply (c)) return_trace (true);
836 }
837
838 return_trace (false);
839 }
840
serializeOT::LigatureSet841 bool serialize (hb_serialize_context_t *c,
842 hb_array_t<const GlyphID> ligatures,
843 hb_array_t<const unsigned int> component_count_list,
844 hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
845 {
846 TRACE_SERIALIZE (this);
847 if (unlikely (!c->extend_min (*this))) return_trace (false);
848 if (unlikely (!ligature.serialize (c, ligatures.len))) return_trace (false);
849 for (unsigned int i = 0; i < ligatures.len; i++)
850 {
851 unsigned int component_count = MAX<int> (component_count_list[i] - 1, 0);
852 if (unlikely (!ligature[i].serialize (c, this)
853 .serialize (c,
854 ligatures[i],
855 component_list.sub_array (0, component_count))))
856 return_trace (false);
857 component_list += component_count;
858 }
859 return_trace (true);
860 }
861
sanitizeOT::LigatureSet862 bool sanitize (hb_sanitize_context_t *c) const
863 {
864 TRACE_SANITIZE (this);
865 return_trace (ligature.sanitize (c, this));
866 }
867
868 protected:
869 OffsetArrayOf<Ligature>
870 ligature; /* Array LigatureSet tables
871 * ordered by preference */
872 public:
873 DEFINE_SIZE_ARRAY (2, ligature);
874 };
875
876 struct LigatureSubstFormat1
877 {
intersectsOT::LigatureSubstFormat1878 bool intersects (const hb_set_t *glyphs) const
879 {
880 unsigned int count = ligatureSet.len;
881 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
882 {
883 if (unlikely (iter.get_coverage () >= count))
884 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
885 if (glyphs->has (iter.get_glyph ()) &&
886 (this+ligatureSet[iter.get_coverage ()]).intersects (glyphs))
887 return true;
888 }
889 return false;
890 }
891
closureOT::LigatureSubstFormat1892 void closure (hb_closure_context_t *c) const
893 {
894 TRACE_CLOSURE (this);
895 unsigned int count = ligatureSet.len;
896 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
897 {
898 if (unlikely (iter.get_coverage () >= count))
899 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
900 if (c->glyphs->has (iter.get_glyph ()))
901 (this+ligatureSet[iter.get_coverage ()]).closure (c);
902 }
903 }
904
collect_glyphsOT::LigatureSubstFormat1905 void collect_glyphs (hb_collect_glyphs_context_t *c) const
906 {
907 TRACE_COLLECT_GLYPHS (this);
908 if (unlikely (!(this+coverage).add_coverage (c->input))) return;
909 unsigned int count = ligatureSet.len;
910 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
911 {
912 if (unlikely (iter.get_coverage () >= count))
913 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
914 (this+ligatureSet[iter.get_coverage ()]).collect_glyphs (c);
915 }
916 }
917
get_coverageOT::LigatureSubstFormat1918 const Coverage &get_coverage () const { return this+coverage; }
919
would_applyOT::LigatureSubstFormat1920 bool would_apply (hb_would_apply_context_t *c) const
921 {
922 TRACE_WOULD_APPLY (this);
923 unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
924 if (likely (index == NOT_COVERED)) return_trace (false);
925
926 const LigatureSet &lig_set = this+ligatureSet[index];
927 return_trace (lig_set.would_apply (c));
928 }
929
applyOT::LigatureSubstFormat1930 bool apply (hb_ot_apply_context_t *c) const
931 {
932 TRACE_APPLY (this);
933
934 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
935 if (likely (index == NOT_COVERED)) return_trace (false);
936
937 const LigatureSet &lig_set = this+ligatureSet[index];
938 return_trace (lig_set.apply (c));
939 }
940
serializeOT::LigatureSubstFormat1941 bool serialize (hb_serialize_context_t *c,
942 hb_array_t<const GlyphID> first_glyphs,
943 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
944 hb_array_t<const GlyphID> ligatures_list,
945 hb_array_t<const unsigned int> component_count_list,
946 hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
947 {
948 TRACE_SERIALIZE (this);
949 if (unlikely (!c->extend_min (*this))) return_trace (false);
950 if (unlikely (!ligatureSet.serialize (c, first_glyphs.len))) return_trace (false);
951 for (unsigned int i = 0; i < first_glyphs.len; i++)
952 {
953 unsigned int ligature_count = ligature_per_first_glyph_count_list[i];
954 if (unlikely (!ligatureSet[i].serialize (c, this)
955 .serialize (c,
956 ligatures_list.sub_array (0, ligature_count),
957 component_count_list.sub_array (0, ligature_count),
958 component_list))) return_trace (false);
959 ligatures_list += ligature_count;
960 component_count_list += ligature_count;
961 for (unsigned int i = 0; i < ligature_count; i++)
962 component_list += MAX<int> (component_count_list[i] - 1, 0);
963 }
964 return_trace (coverage.serialize (c, this).serialize (c, first_glyphs));
965 }
966
subsetOT::LigatureSubstFormat1967 bool subset (hb_subset_context_t *c) const
968 {
969 TRACE_SUBSET (this);
970 // TODO(subset)
971 return_trace (false);
972 }
973
sanitizeOT::LigatureSubstFormat1974 bool sanitize (hb_sanitize_context_t *c) const
975 {
976 TRACE_SANITIZE (this);
977 return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
978 }
979
980 protected:
981 HBUINT16 format; /* Format identifier--format = 1 */
982 OffsetTo<Coverage>
983 coverage; /* Offset to Coverage table--from
984 * beginning of Substitution table */
985 OffsetArrayOf<LigatureSet>
986 ligatureSet; /* Array LigatureSet tables
987 * ordered by Coverage Index */
988 public:
989 DEFINE_SIZE_ARRAY (6, ligatureSet);
990 };
991
992 struct LigatureSubst
993 {
serializeOT::LigatureSubst994 bool serialize (hb_serialize_context_t *c,
995 hb_array_t<const GlyphID> first_glyphs,
996 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
997 hb_array_t<const GlyphID> ligatures_list,
998 hb_array_t<const unsigned int> component_count_list,
999 hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
1000 {
1001 TRACE_SERIALIZE (this);
1002 if (unlikely (!c->extend_min (u.format))) return_trace (false);
1003 unsigned int format = 1;
1004 u.format.set (format);
1005 switch (u.format) {
1006 case 1: return_trace (u.format1.serialize (c,
1007 first_glyphs,
1008 ligature_per_first_glyph_count_list,
1009 ligatures_list,
1010 component_count_list,
1011 component_list));
1012 default:return_trace (false);
1013 }
1014 }
1015
1016 template <typename context_t>
dispatchOT::LigatureSubst1017 typename context_t::return_t dispatch (context_t *c) const
1018 {
1019 TRACE_DISPATCH (this, u.format);
1020 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
1021 switch (u.format) {
1022 case 1: return_trace (c->dispatch (u.format1));
1023 default:return_trace (c->default_return_value ());
1024 }
1025 }
1026
1027 protected:
1028 union {
1029 HBUINT16 format; /* Format identifier */
1030 LigatureSubstFormat1 format1;
1031 } u;
1032 };
1033
1034
1035 struct ContextSubst : Context {};
1036
1037 struct ChainContextSubst : ChainContext {};
1038
1039 struct ExtensionSubst : Extension<ExtensionSubst>
1040 {
1041 typedef struct SubstLookupSubTable SubTable;
1042
1043 bool is_reverse () const;
1044 };
1045
1046
1047 struct ReverseChainSingleSubstFormat1
1048 {
intersectsOT::ReverseChainSingleSubstFormat11049 bool intersects (const hb_set_t *glyphs) const
1050 {
1051 if (!(this+coverage).intersects (glyphs))
1052 return false;
1053
1054 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1055
1056 unsigned int count;
1057
1058 count = backtrack.len;
1059 for (unsigned int i = 0; i < count; i++)
1060 if (!(this+backtrack[i]).intersects (glyphs))
1061 return false;
1062
1063 count = lookahead.len;
1064 for (unsigned int i = 0; i < count; i++)
1065 if (!(this+lookahead[i]).intersects (glyphs))
1066 return false;
1067
1068 return true;
1069 }
1070
closureOT::ReverseChainSingleSubstFormat11071 void closure (hb_closure_context_t *c) const
1072 {
1073 TRACE_CLOSURE (this);
1074 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1075
1076 unsigned int count;
1077
1078 count = backtrack.len;
1079 for (unsigned int i = 0; i < count; i++)
1080 if (!(this+backtrack[i]).intersects (c->glyphs))
1081 return;
1082
1083 count = lookahead.len;
1084 for (unsigned int i = 0; i < count; i++)
1085 if (!(this+lookahead[i]).intersects (c->glyphs))
1086 return;
1087
1088 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
1089 count = substitute.len;
1090 for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
1091 {
1092 if (unlikely (iter.get_coverage () >= count))
1093 break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
1094 if (c->glyphs->has (iter.get_glyph ()))
1095 c->out->add (substitute[iter.get_coverage ()]);
1096 }
1097 }
1098
collect_glyphsOT::ReverseChainSingleSubstFormat11099 void collect_glyphs (hb_collect_glyphs_context_t *c) const
1100 {
1101 TRACE_COLLECT_GLYPHS (this);
1102 if (unlikely (!(this+coverage).add_coverage (c->input))) return;
1103
1104 unsigned int count;
1105
1106 count = backtrack.len;
1107 for (unsigned int i = 0; i < count; i++)
1108 if (unlikely (!(this+backtrack[i]).add_coverage (c->before))) return;
1109
1110 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1111 count = lookahead.len;
1112 for (unsigned int i = 0; i < count; i++)
1113 if (unlikely (!(this+lookahead[i]).add_coverage (c->after))) return;
1114
1115 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
1116 count = substitute.len;
1117 c->output->add_array (substitute.arrayZ, substitute.len);
1118 }
1119
get_coverageOT::ReverseChainSingleSubstFormat11120 const Coverage &get_coverage () const { return this+coverage; }
1121
would_applyOT::ReverseChainSingleSubstFormat11122 bool would_apply (hb_would_apply_context_t *c) const
1123 {
1124 TRACE_WOULD_APPLY (this);
1125 return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
1126 }
1127
applyOT::ReverseChainSingleSubstFormat11128 bool apply (hb_ot_apply_context_t *c) const
1129 {
1130 TRACE_APPLY (this);
1131 if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
1132 return_trace (false); /* No chaining to this type */
1133
1134 unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
1135 if (likely (index == NOT_COVERED)) return_trace (false);
1136
1137 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1138 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
1139
1140 unsigned int start_index = 0, end_index = 0;
1141 if (match_backtrack (c,
1142 backtrack.len, (HBUINT16 *) backtrack.arrayZ,
1143 match_coverage, this,
1144 &start_index) &&
1145 match_lookahead (c,
1146 lookahead.len, (HBUINT16 *) lookahead.arrayZ,
1147 match_coverage, this,
1148 1, &end_index))
1149 {
1150 c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
1151 c->replace_glyph_inplace (substitute[index]);
1152 /* Note: We DON'T decrease buffer->idx. The main loop does it
1153 * for us. This is useful for preventing surprises if someone
1154 * calls us through a Context lookup. */
1155 return_trace (true);
1156 }
1157
1158 return_trace (false);
1159 }
1160
subsetOT::ReverseChainSingleSubstFormat11161 bool subset (hb_subset_context_t *c) const
1162 {
1163 TRACE_SUBSET (this);
1164 // TODO(subset)
1165 return_trace (false);
1166 }
1167
sanitizeOT::ReverseChainSingleSubstFormat11168 bool sanitize (hb_sanitize_context_t *c) const
1169 {
1170 TRACE_SANITIZE (this);
1171 if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
1172 return_trace (false);
1173 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
1174 if (!lookahead.sanitize (c, this))
1175 return_trace (false);
1176 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
1177 return_trace (substitute.sanitize (c));
1178 }
1179
1180 protected:
1181 HBUINT16 format; /* Format identifier--format = 1 */
1182 OffsetTo<Coverage>
1183 coverage; /* Offset to Coverage table--from
1184 * beginning of table */
1185 OffsetArrayOf<Coverage>
1186 backtrack; /* Array of coverage tables
1187 * in backtracking sequence, in glyph
1188 * sequence order */
1189 OffsetArrayOf<Coverage>
1190 lookaheadX; /* Array of coverage tables
1191 * in lookahead sequence, in glyph
1192 * sequence order */
1193 ArrayOf<GlyphID>
1194 substituteX; /* Array of substitute
1195 * GlyphIDs--ordered by Coverage Index */
1196 public:
1197 DEFINE_SIZE_MIN (10);
1198 };
1199
1200 struct ReverseChainSingleSubst
1201 {
1202 template <typename context_t>
dispatchOT::ReverseChainSingleSubst1203 typename context_t::return_t dispatch (context_t *c) const
1204 {
1205 TRACE_DISPATCH (this, u.format);
1206 if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
1207 switch (u.format) {
1208 case 1: return_trace (c->dispatch (u.format1));
1209 default:return_trace (c->default_return_value ());
1210 }
1211 }
1212
1213 protected:
1214 union {
1215 HBUINT16 format; /* Format identifier */
1216 ReverseChainSingleSubstFormat1 format1;
1217 } u;
1218 };
1219
1220
1221
1222 /*
1223 * SubstLookup
1224 */
1225
1226 struct SubstLookupSubTable
1227 {
1228 friend struct Lookup;
1229 friend struct SubstLookup;
1230
1231 enum Type {
1232 Single = 1,
1233 Multiple = 2,
1234 Alternate = 3,
1235 Ligature = 4,
1236 Context = 5,
1237 ChainContext = 6,
1238 Extension = 7,
1239 ReverseChainSingle = 8
1240 };
1241
1242 template <typename context_t>
dispatchOT::SubstLookupSubTable1243 typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
1244 {
1245 TRACE_DISPATCH (this, lookup_type);
1246 switch (lookup_type) {
1247 case Single: return_trace (u.single.dispatch (c));
1248 case Multiple: return_trace (u.multiple.dispatch (c));
1249 case Alternate: return_trace (u.alternate.dispatch (c));
1250 case Ligature: return_trace (u.ligature.dispatch (c));
1251 case Context: return_trace (u.context.dispatch (c));
1252 case ChainContext: return_trace (u.chainContext.dispatch (c));
1253 case Extension: return_trace (u.extension.dispatch (c));
1254 case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c));
1255 default: return_trace (c->default_return_value ());
1256 }
1257 }
1258
1259 protected:
1260 union {
1261 SingleSubst single;
1262 MultipleSubst multiple;
1263 AlternateSubst alternate;
1264 LigatureSubst ligature;
1265 ContextSubst context;
1266 ChainContextSubst chainContext;
1267 ExtensionSubst extension;
1268 ReverseChainSingleSubst reverseChainContextSingle;
1269 } u;
1270 public:
1271 DEFINE_SIZE_MIN (0);
1272 };
1273
1274
1275 struct SubstLookup : Lookup
1276 {
1277 typedef SubstLookupSubTable SubTable;
1278
get_subtableOT::SubstLookup1279 const SubTable& get_subtable (unsigned int i) const
1280 { return Lookup::get_subtable<SubTable> (i); }
1281
lookup_type_is_reverseOT::SubstLookup1282 static bool lookup_type_is_reverse (unsigned int lookup_type)
1283 { return lookup_type == SubTable::ReverseChainSingle; }
1284
is_reverseOT::SubstLookup1285 bool is_reverse () const
1286 {
1287 unsigned int type = get_type ();
1288 if (unlikely (type == SubTable::Extension))
1289 return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
1290 return lookup_type_is_reverse (type);
1291 }
1292
applyOT::SubstLookup1293 bool apply (hb_ot_apply_context_t *c) const
1294 {
1295 TRACE_APPLY (this);
1296 return_trace (dispatch (c));
1297 }
1298
intersectsOT::SubstLookup1299 bool intersects (const hb_set_t *glyphs) const
1300 {
1301 hb_intersects_context_t c (glyphs);
1302 return dispatch (&c);
1303 }
1304
closureOT::SubstLookup1305 hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
1306 {
1307 TRACE_CLOSURE (this);
1308 if (!c->should_visit_lookup (this_index))
1309 return_trace (HB_VOID);
1310
1311 c->set_recurse_func (dispatch_closure_recurse_func);
1312
1313 hb_closure_context_t::return_t ret = dispatch (c);
1314
1315 c->flush ();
1316
1317 return_trace (ret);
1318 }
1319
collect_glyphsOT::SubstLookup1320 hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
1321 {
1322 TRACE_COLLECT_GLYPHS (this);
1323 c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
1324 return_trace (dispatch (c));
1325 }
1326
1327 template <typename set_t>
add_coverageOT::SubstLookup1328 void add_coverage (set_t *glyphs) const
1329 {
1330 hb_add_coverage_context_t<set_t> c (glyphs);
1331 dispatch (&c);
1332 }
1333
would_applyOT::SubstLookup1334 bool would_apply (hb_would_apply_context_t *c,
1335 const hb_ot_layout_lookup_accelerator_t *accel) const
1336 {
1337 TRACE_WOULD_APPLY (this);
1338 if (unlikely (!c->len)) return_trace (false);
1339 if (!accel->may_have (c->glyphs[0])) return_trace (false);
1340 return_trace (dispatch (c));
1341 }
1342
1343 static bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
1344
serialize_subtableOT::SubstLookup1345 SubTable& serialize_subtable (hb_serialize_context_t *c,
1346 unsigned int i)
1347 { return get_subtables<SubTable> ()[i].serialize (c, this); }
1348
serialize_singleOT::SubstLookup1349 bool serialize_single (hb_serialize_context_t *c,
1350 uint32_t lookup_props,
1351 hb_array_t<const GlyphID> glyphs,
1352 hb_array_t<const GlyphID> substitutes)
1353 {
1354 TRACE_SERIALIZE (this);
1355 if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
1356 return_trace (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes));
1357 }
1358
serialize_multipleOT::SubstLookup1359 bool serialize_multiple (hb_serialize_context_t *c,
1360 uint32_t lookup_props,
1361 hb_array_t<const GlyphID> glyphs,
1362 hb_array_t<const unsigned int> substitute_len_list,
1363 hb_array_t<const GlyphID> substitute_glyphs_list)
1364 {
1365 TRACE_SERIALIZE (this);
1366 if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false);
1367 return_trace (serialize_subtable (c, 0).u.multiple.serialize (c,
1368 glyphs,
1369 substitute_len_list,
1370 substitute_glyphs_list));
1371 }
1372
serialize_alternateOT::SubstLookup1373 bool serialize_alternate (hb_serialize_context_t *c,
1374 uint32_t lookup_props,
1375 hb_array_t<const GlyphID> glyphs,
1376 hb_array_t<const unsigned int> alternate_len_list,
1377 hb_array_t<const GlyphID> alternate_glyphs_list)
1378 {
1379 TRACE_SERIALIZE (this);
1380 if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false);
1381 return_trace (serialize_subtable (c, 0).u.alternate.serialize (c,
1382 glyphs,
1383 alternate_len_list,
1384 alternate_glyphs_list));
1385 }
1386
serialize_ligatureOT::SubstLookup1387 bool serialize_ligature (hb_serialize_context_t *c,
1388 uint32_t lookup_props,
1389 hb_array_t<const GlyphID> first_glyphs,
1390 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
1391 hb_array_t<const GlyphID> ligatures_list,
1392 hb_array_t<const unsigned int> component_count_list,
1393 hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
1394 {
1395 TRACE_SERIALIZE (this);
1396 if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false);
1397 return_trace (serialize_subtable (c, 0).u.ligature.serialize (c,
1398 first_glyphs,
1399 ligature_per_first_glyph_count_list,
1400 ligatures_list,
1401 component_count_list,
1402 component_list));
1403 }
1404
1405 template <typename context_t>
1406 static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
1407
dispatch_closure_recurse_funcOT::SubstLookup1408 static hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index)
1409 {
1410 if (!c->should_visit_lookup (lookup_index))
1411 return HB_VOID;
1412
1413 hb_closure_context_t::return_t ret = dispatch_recurse_func (c, lookup_index);
1414
1415 /* While in theory we should flush here, it will cause timeouts because a recursive
1416 * lookup can keep growing the glyph set. Skip, and outer loop will retry up to
1417 * HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */
1418 //c->flush ();
1419
1420 return ret;
1421 }
1422
1423 template <typename context_t>
dispatchOT::SubstLookup1424 typename context_t::return_t dispatch (context_t *c) const
1425 { return Lookup::dispatch<SubTable> (c); }
1426
subsetOT::SubstLookup1427 bool subset (hb_subset_context_t *c) const
1428 { return Lookup::subset<SubTable> (c); }
1429
sanitizeOT::SubstLookup1430 bool sanitize (hb_sanitize_context_t *c) const
1431 { return Lookup::sanitize<SubTable> (c); }
1432 };
1433
1434 /*
1435 * GSUB -- Glyph Substitution
1436 * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub
1437 */
1438
1439 struct GSUB : GSUBGPOS
1440 {
1441 enum { tableTag = HB_OT_TAG_GSUB };
1442
get_lookupOT::GSUB1443 const SubstLookup& get_lookup (unsigned int i) const
1444 { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
1445
subsetOT::GSUB1446 bool subset (hb_subset_context_t *c) const
1447 { return GSUBGPOS::subset<SubstLookup> (c); }
1448
sanitizeOT::GSUB1449 bool sanitize (hb_sanitize_context_t *c) const
1450 { return GSUBGPOS::sanitize<SubstLookup> (c); }
1451
1452 HB_INTERNAL bool is_blacklisted (hb_blob_t *blob,
1453 hb_face_t *face) const;
1454
1455 typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t;
1456 };
1457
1458
1459 struct GSUB_accelerator_t : GSUB::accelerator_t {};
1460
1461
1462 /* Out-of-class implementation for methods recursing */
1463
is_reverse() const1464 /*static*/ inline bool ExtensionSubst::is_reverse () const
1465 {
1466 unsigned int type = get_type ();
1467 if (unlikely (type == SubTable::Extension))
1468 return CastR<ExtensionSubst> (get_subtable<SubTable>()).is_reverse ();
1469 return SubstLookup::lookup_type_is_reverse (type);
1470 }
1471
1472 template <typename context_t>
dispatch_recurse_func(context_t * c,unsigned int lookup_index)1473 /*static*/ inline typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
1474 {
1475 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1476 return l.dispatch (c);
1477 }
1478
apply_recurse_func(hb_ot_apply_context_t * c,unsigned int lookup_index)1479 /*static*/ inline bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
1480 {
1481 const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1482 unsigned int saved_lookup_props = c->lookup_props;
1483 unsigned int saved_lookup_index = c->lookup_index;
1484 c->set_lookup_index (lookup_index);
1485 c->set_lookup_props (l.get_props ());
1486 bool ret = l.dispatch (c);
1487 c->set_lookup_index (saved_lookup_index);
1488 c->set_lookup_props (saved_lookup_props);
1489 return ret;
1490 }
1491
1492 } /* namespace OT */
1493
1494
1495 #endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */
1496