1 : /*
2 : * Copyright © 2007,2008,2009,2010 Red Hat, Inc.
3 : * Copyright © 2010 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-private.hh"
33 :
34 :
35 :
36 : struct SingleSubstFormat1
37 : {
38 : friend struct SingleSubst;
39 :
40 : private:
41 :
42 0 : inline bool apply (hb_apply_context_t *c) const
43 : {
44 0 : TRACE_APPLY ();
45 0 : hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
46 0 : unsigned int index = (this+coverage) (glyph_id);
47 0 : if (likely (index == NOT_COVERED))
48 0 : return false;
49 :
50 : /* According to the Adobe Annotated OpenType Suite, result is always
51 : * limited to 16bit. */
52 0 : glyph_id = (glyph_id + deltaGlyphID) & 0xFFFF;
53 0 : c->replace_glyph (glyph_id);
54 :
55 0 : return true;
56 : }
57 :
58 0 : inline bool sanitize (hb_sanitize_context_t *c) {
59 0 : TRACE_SANITIZE ();
60 0 : return coverage.sanitize (c, this)
61 0 : && deltaGlyphID.sanitize (c);
62 : }
63 :
64 : private:
65 : USHORT format; /* Format identifier--format = 1 */
66 : OffsetTo<Coverage>
67 : coverage; /* Offset to Coverage table--from
68 : * beginning of Substitution table */
69 : SHORT deltaGlyphID; /* Add to original GlyphID to get
70 : * substitute GlyphID */
71 : public:
72 : DEFINE_SIZE_STATIC (6);
73 : };
74 :
75 : struct SingleSubstFormat2
76 : {
77 : friend struct SingleSubst;
78 :
79 : private:
80 :
81 0 : inline bool apply (hb_apply_context_t *c) const
82 : {
83 0 : TRACE_APPLY ();
84 0 : hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
85 0 : unsigned int index = (this+coverage) (glyph_id);
86 0 : if (likely (index == NOT_COVERED))
87 0 : return false;
88 :
89 0 : if (unlikely (index >= substitute.len))
90 0 : return false;
91 :
92 0 : glyph_id = substitute[index];
93 0 : c->replace_glyph (glyph_id);
94 :
95 0 : return true;
96 : }
97 :
98 0 : inline bool sanitize (hb_sanitize_context_t *c) {
99 0 : TRACE_SANITIZE ();
100 0 : return coverage.sanitize (c, this)
101 0 : && substitute.sanitize (c);
102 : }
103 :
104 : private:
105 : USHORT format; /* Format identifier--format = 2 */
106 : OffsetTo<Coverage>
107 : coverage; /* Offset to Coverage table--from
108 : * beginning of Substitution table */
109 : ArrayOf<GlyphID>
110 : substitute; /* Array of substitute
111 : * GlyphIDs--ordered by Coverage Index */
112 : public:
113 : DEFINE_SIZE_ARRAY (6, substitute);
114 : };
115 :
116 : struct SingleSubst
117 : {
118 : friend struct SubstLookupSubTable;
119 :
120 : private:
121 :
122 0 : inline bool apply (hb_apply_context_t *c) const
123 : {
124 0 : TRACE_APPLY ();
125 0 : switch (u.format) {
126 0 : case 1: return u.format1.apply (c);
127 0 : case 2: return u.format2.apply (c);
128 0 : default:return false;
129 : }
130 : }
131 :
132 0 : inline bool sanitize (hb_sanitize_context_t *c) {
133 0 : TRACE_SANITIZE ();
134 0 : if (!u.format.sanitize (c)) return false;
135 0 : switch (u.format) {
136 0 : case 1: return u.format1.sanitize (c);
137 0 : case 2: return u.format2.sanitize (c);
138 0 : default:return true;
139 : }
140 : }
141 :
142 : private:
143 : union {
144 : USHORT format; /* Format identifier */
145 : SingleSubstFormat1 format1;
146 : SingleSubstFormat2 format2;
147 : } u;
148 : };
149 :
150 :
151 : struct Sequence
152 : {
153 : friend struct MultipleSubstFormat1;
154 :
155 : private:
156 0 : inline bool apply (hb_apply_context_t *c) const
157 : {
158 0 : TRACE_APPLY ();
159 0 : if (unlikely (!substitute.len))
160 0 : return false;
161 :
162 0 : if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)
163 0 : c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH);
164 0 : c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array);
165 :
166 0 : return true;
167 : }
168 :
169 : public:
170 0 : inline bool sanitize (hb_sanitize_context_t *c) {
171 0 : TRACE_SANITIZE ();
172 0 : return substitute.sanitize (c);
173 : }
174 :
175 : private:
176 : ArrayOf<GlyphID>
177 : substitute; /* String of GlyphIDs to substitute */
178 : public:
179 : DEFINE_SIZE_ARRAY (2, substitute);
180 : };
181 :
182 : struct MultipleSubstFormat1
183 : {
184 : friend struct MultipleSubst;
185 :
186 : private:
187 :
188 0 : inline bool apply (hb_apply_context_t *c) const
189 : {
190 0 : TRACE_APPLY ();
191 :
192 0 : unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
193 0 : if (likely (index == NOT_COVERED))
194 0 : return false;
195 :
196 0 : return (this+sequence[index]).apply (c);
197 : }
198 :
199 0 : inline bool sanitize (hb_sanitize_context_t *c) {
200 0 : TRACE_SANITIZE ();
201 0 : return coverage.sanitize (c, this)
202 0 : && sequence.sanitize (c, this);
203 : }
204 :
205 : private:
206 : USHORT format; /* Format identifier--format = 1 */
207 : OffsetTo<Coverage>
208 : coverage; /* Offset to Coverage table--from
209 : * beginning of Substitution table */
210 : OffsetArrayOf<Sequence>
211 : sequence; /* Array of Sequence tables
212 : * ordered by Coverage Index */
213 : public:
214 : DEFINE_SIZE_ARRAY (6, sequence);
215 : };
216 :
217 : struct MultipleSubst
218 : {
219 : friend struct SubstLookupSubTable;
220 :
221 : private:
222 :
223 0 : inline bool apply (hb_apply_context_t *c) const
224 : {
225 0 : TRACE_APPLY ();
226 0 : switch (u.format) {
227 0 : case 1: return u.format1.apply (c);
228 0 : default:return false;
229 : }
230 : }
231 :
232 0 : inline bool sanitize (hb_sanitize_context_t *c) {
233 0 : TRACE_SANITIZE ();
234 0 : if (!u.format.sanitize (c)) return false;
235 0 : switch (u.format) {
236 0 : case 1: return u.format1.sanitize (c);
237 0 : default:return true;
238 : }
239 : }
240 :
241 : private:
242 : union {
243 : USHORT format; /* Format identifier */
244 : MultipleSubstFormat1 format1;
245 : } u;
246 : };
247 :
248 :
249 : typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in
250 : * arbitrary order */
251 :
252 : struct AlternateSubstFormat1
253 : {
254 : friend struct AlternateSubst;
255 :
256 : private:
257 :
258 0 : inline bool apply (hb_apply_context_t *c) const
259 : {
260 0 : TRACE_APPLY ();
261 0 : hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
262 0 : hb_mask_t glyph_mask = c->buffer->info[c->buffer->idx].mask;
263 0 : hb_mask_t lookup_mask = c->lookup_mask;
264 :
265 0 : unsigned int index = (this+coverage) (glyph_id);
266 0 : if (likely (index == NOT_COVERED))
267 0 : return false;
268 :
269 0 : const AlternateSet &alt_set = this+alternateSet[index];
270 :
271 0 : if (unlikely (!alt_set.len))
272 0 : return false;
273 :
274 : /* Note: This breaks badly if two features enabled this lookup together. */
275 0 : unsigned int shift = _hb_ctz (lookup_mask);
276 0 : unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
277 :
278 0 : if (unlikely (alt_index > alt_set.len || alt_index == 0))
279 0 : return false;
280 :
281 0 : glyph_id = alt_set[alt_index - 1];
282 :
283 0 : c->replace_glyph (glyph_id);
284 :
285 0 : return true;
286 : }
287 :
288 0 : inline bool sanitize (hb_sanitize_context_t *c) {
289 0 : TRACE_SANITIZE ();
290 0 : return coverage.sanitize (c, this)
291 0 : && alternateSet.sanitize (c, this);
292 : }
293 :
294 : private:
295 : USHORT format; /* Format identifier--format = 1 */
296 : OffsetTo<Coverage>
297 : coverage; /* Offset to Coverage table--from
298 : * beginning of Substitution table */
299 : OffsetArrayOf<AlternateSet>
300 : alternateSet; /* Array of AlternateSet tables
301 : * ordered by Coverage Index */
302 : public:
303 : DEFINE_SIZE_ARRAY (6, alternateSet);
304 : };
305 :
306 : struct AlternateSubst
307 : {
308 : friend struct SubstLookupSubTable;
309 :
310 : private:
311 :
312 0 : inline bool apply (hb_apply_context_t *c) const
313 : {
314 0 : TRACE_APPLY ();
315 0 : switch (u.format) {
316 0 : case 1: return u.format1.apply (c);
317 0 : default:return false;
318 : }
319 : }
320 :
321 0 : inline bool sanitize (hb_sanitize_context_t *c) {
322 0 : TRACE_SANITIZE ();
323 0 : if (!u.format.sanitize (c)) return false;
324 0 : switch (u.format) {
325 0 : case 1: return u.format1.sanitize (c);
326 0 : default:return true;
327 : }
328 : }
329 :
330 : private:
331 : union {
332 : USHORT format; /* Format identifier */
333 : AlternateSubstFormat1 format1;
334 : } u;
335 : };
336 :
337 :
338 : struct Ligature
339 : {
340 : friend struct LigatureSet;
341 :
342 : private:
343 0 : inline bool apply (hb_apply_context_t *c) const
344 : {
345 0 : TRACE_APPLY ();
346 0 : unsigned int count = component.len;
347 0 : if (unlikely (count < 2))
348 0 : return false;
349 :
350 0 : hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
351 0 : if (skippy_iter.has_no_chance ())
352 0 : return false;
353 :
354 0 : bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
355 0 : bool found_non_mark = false;
356 :
357 0 : for (unsigned int i = 1; i < count; i++)
358 : {
359 : unsigned int property;
360 :
361 0 : if (!skippy_iter.next (&property))
362 0 : return false;
363 :
364 0 : found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
365 :
366 0 : if (likely (c->buffer->info[skippy_iter.idx].codepoint != component[i]))
367 0 : return false;
368 : }
369 :
370 0 : if (first_was_mark && found_non_mark)
371 0 : c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);
372 :
373 : /* Allocate new ligature id */
374 0 : unsigned int lig_id = allocate_lig_id (c->buffer);
375 0 : c->buffer->info[c->buffer->idx].lig_comp() = 0;
376 0 : c->buffer->info[c->buffer->idx].lig_id() = lig_id;
377 :
378 0 : if (skippy_iter.idx < c->buffer->idx + count) /* No input glyphs skipped */
379 : {
380 0 : c->replace_glyphs_be16 (count, 1, (const uint16_t *) &ligGlyph);
381 : }
382 : else
383 : {
384 0 : c->replace_glyph (ligGlyph);
385 :
386 : /* Now we must do a second loop to copy the skipped glyphs to
387 : `out' and assign component values to it. We start with the
388 : glyph after the first component. Glyphs between component
389 : i and i+1 belong to component i. Together with the lig_id
390 : value it is later possible to check whether a specific
391 : component value really belongs to a given ligature. */
392 :
393 0 : for (unsigned int i = 1; i < count; i++)
394 : {
395 0 : while (c->should_mark_skip_current_glyph ())
396 : {
397 0 : c->buffer->info[c->buffer->idx].lig_comp() = i;
398 0 : c->buffer->info[c->buffer->idx].lig_id() = lig_id;
399 0 : c->replace_glyph (c->buffer->info[c->buffer->idx].codepoint);
400 : }
401 :
402 : /* Skip the base glyph */
403 0 : c->buffer->idx++;
404 : }
405 : }
406 :
407 0 : return true;
408 : }
409 :
410 : public:
411 0 : inline bool sanitize (hb_sanitize_context_t *c) {
412 0 : TRACE_SANITIZE ();
413 0 : return ligGlyph.sanitize (c)
414 0 : && component.sanitize (c);
415 : }
416 :
417 : private:
418 : GlyphID ligGlyph; /* GlyphID of ligature to substitute */
419 : HeadlessArrayOf<GlyphID>
420 : component; /* Array of component GlyphIDs--start
421 : * with the second component--ordered
422 : * in writing direction */
423 : public:
424 : DEFINE_SIZE_ARRAY (4, component);
425 : };
426 :
427 : struct LigatureSet
428 : {
429 : friend struct LigatureSubstFormat1;
430 :
431 : private:
432 0 : inline bool apply (hb_apply_context_t *c) const
433 : {
434 0 : TRACE_APPLY ();
435 0 : unsigned int num_ligs = ligature.len;
436 0 : for (unsigned int i = 0; i < num_ligs; i++)
437 : {
438 0 : const Ligature &lig = this+ligature[i];
439 0 : if (lig.apply (c))
440 0 : return true;
441 : }
442 :
443 0 : return false;
444 : }
445 :
446 : public:
447 0 : inline bool sanitize (hb_sanitize_context_t *c) {
448 0 : TRACE_SANITIZE ();
449 0 : return ligature.sanitize (c, this);
450 : }
451 :
452 : private:
453 : OffsetArrayOf<Ligature>
454 : ligature; /* Array LigatureSet tables
455 : * ordered by preference */
456 : public:
457 : DEFINE_SIZE_ARRAY (2, ligature);
458 : };
459 :
460 : struct LigatureSubstFormat1
461 : {
462 : friend struct LigatureSubst;
463 :
464 : private:
465 0 : inline bool apply (hb_apply_context_t *c) const
466 : {
467 0 : TRACE_APPLY ();
468 0 : hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
469 :
470 0 : unsigned int index = (this+coverage) (glyph_id);
471 0 : if (likely (index == NOT_COVERED))
472 0 : return false;
473 :
474 0 : const LigatureSet &lig_set = this+ligatureSet[index];
475 0 : return lig_set.apply (c);
476 : }
477 :
478 0 : inline bool sanitize (hb_sanitize_context_t *c) {
479 0 : TRACE_SANITIZE ();
480 0 : return coverage.sanitize (c, this)
481 0 : && ligatureSet.sanitize (c, this);
482 : }
483 :
484 : private:
485 : USHORT format; /* Format identifier--format = 1 */
486 : OffsetTo<Coverage>
487 : coverage; /* Offset to Coverage table--from
488 : * beginning of Substitution table */
489 : OffsetArrayOf<LigatureSet>
490 : ligatureSet; /* Array LigatureSet tables
491 : * ordered by Coverage Index */
492 : public:
493 : DEFINE_SIZE_ARRAY (6, ligatureSet);
494 : };
495 :
496 : struct LigatureSubst
497 : {
498 : friend struct SubstLookupSubTable;
499 :
500 : private:
501 0 : inline bool apply (hb_apply_context_t *c) const
502 : {
503 0 : TRACE_APPLY ();
504 0 : switch (u.format) {
505 0 : case 1: return u.format1.apply (c);
506 0 : default:return false;
507 : }
508 : }
509 :
510 0 : inline bool sanitize (hb_sanitize_context_t *c) {
511 0 : TRACE_SANITIZE ();
512 0 : if (!u.format.sanitize (c)) return false;
513 0 : switch (u.format) {
514 0 : case 1: return u.format1.sanitize (c);
515 0 : default:return true;
516 : }
517 : }
518 :
519 : private:
520 : union {
521 : USHORT format; /* Format identifier */
522 : LigatureSubstFormat1 format1;
523 : } u;
524 : };
525 :
526 :
527 : static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
528 :
529 : struct ContextSubst : Context
530 : {
531 : friend struct SubstLookupSubTable;
532 :
533 : private:
534 0 : inline bool apply (hb_apply_context_t *c) const
535 : {
536 0 : TRACE_APPLY ();
537 0 : return Context::apply (c, substitute_lookup);
538 : }
539 : };
540 :
541 : struct ChainContextSubst : ChainContext
542 : {
543 : friend struct SubstLookupSubTable;
544 :
545 : private:
546 0 : inline bool apply (hb_apply_context_t *c) const
547 : {
548 0 : TRACE_APPLY ();
549 0 : return ChainContext::apply (c, substitute_lookup);
550 : }
551 : };
552 :
553 :
554 : struct ExtensionSubst : Extension
555 : {
556 : friend struct SubstLookupSubTable;
557 : friend struct SubstLookup;
558 :
559 : private:
560 0 : inline const struct SubstLookupSubTable& get_subtable (void) const
561 : {
562 0 : unsigned int offset = get_offset ();
563 0 : if (unlikely (!offset)) return Null(SubstLookupSubTable);
564 0 : return StructAtOffset<SubstLookupSubTable> (this, offset);
565 : }
566 :
567 : inline bool apply (hb_apply_context_t *c) const;
568 :
569 : inline bool sanitize (hb_sanitize_context_t *c);
570 :
571 : inline bool is_reverse (void) const;
572 : };
573 :
574 :
575 : struct ReverseChainSingleSubstFormat1
576 : {
577 : friend struct ReverseChainSingleSubst;
578 :
579 : private:
580 0 : inline bool apply (hb_apply_context_t *c) const
581 : {
582 0 : TRACE_APPLY ();
583 0 : if (unlikely (c->context_length != NO_CONTEXT))
584 0 : return false; /* No chaining to this type */
585 :
586 0 : unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
587 0 : if (likely (index == NOT_COVERED))
588 0 : return false;
589 :
590 0 : const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
591 0 : const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
592 :
593 0 : if (match_backtrack (c,
594 : backtrack.len, (USHORT *) backtrack.array,
595 0 : match_coverage, this) &&
596 : match_lookahead (c,
597 : lookahead.len, (USHORT *) lookahead.array,
598 : match_coverage, this,
599 0 : 1))
600 : {
601 0 : c->buffer->info[c->buffer->idx].codepoint = substitute[index];
602 0 : c->buffer->idx--; /* Reverse! */
603 0 : return true;
604 : }
605 :
606 0 : return false;
607 : }
608 :
609 0 : inline bool sanitize (hb_sanitize_context_t *c) {
610 0 : TRACE_SANITIZE ();
611 0 : if (!(coverage.sanitize (c, this)
612 0 : && backtrack.sanitize (c, this)))
613 0 : return false;
614 0 : OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
615 0 : if (!lookahead.sanitize (c, this))
616 0 : return false;
617 0 : ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
618 0 : return substitute.sanitize (c);
619 : }
620 :
621 : private:
622 : USHORT format; /* Format identifier--format = 1 */
623 : OffsetTo<Coverage>
624 : coverage; /* Offset to Coverage table--from
625 : * beginning of table */
626 : OffsetArrayOf<Coverage>
627 : backtrack; /* Array of coverage tables
628 : * in backtracking sequence, in glyph
629 : * sequence order */
630 : OffsetArrayOf<Coverage>
631 : lookaheadX; /* Array of coverage tables
632 : * in lookahead sequence, in glyph
633 : * sequence order */
634 : ArrayOf<GlyphID>
635 : substituteX; /* Array of substitute
636 : * GlyphIDs--ordered by Coverage Index */
637 : public:
638 : DEFINE_SIZE_MIN (10);
639 : };
640 :
641 : struct ReverseChainSingleSubst
642 : {
643 : friend struct SubstLookupSubTable;
644 :
645 : private:
646 0 : inline bool apply (hb_apply_context_t *c) const
647 : {
648 0 : TRACE_APPLY ();
649 0 : switch (u.format) {
650 0 : case 1: return u.format1.apply (c);
651 0 : default:return false;
652 : }
653 : }
654 :
655 0 : inline bool sanitize (hb_sanitize_context_t *c) {
656 0 : TRACE_SANITIZE ();
657 0 : if (!u.format.sanitize (c)) return false;
658 0 : switch (u.format) {
659 0 : case 1: return u.format1.sanitize (c);
660 0 : default:return true;
661 : }
662 : }
663 :
664 : private:
665 : union {
666 : USHORT format; /* Format identifier */
667 : ReverseChainSingleSubstFormat1 format1;
668 : } u;
669 : };
670 :
671 :
672 :
673 : /*
674 : * SubstLookup
675 : */
676 :
677 : struct SubstLookupSubTable
678 : {
679 : friend struct SubstLookup;
680 :
681 : enum {
682 : Single = 1,
683 : Multiple = 2,
684 : Alternate = 3,
685 : Ligature = 4,
686 : Context = 5,
687 : ChainContext = 6,
688 : Extension = 7,
689 : ReverseChainSingle = 8
690 : };
691 :
692 0 : inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
693 : {
694 0 : TRACE_APPLY ();
695 0 : switch (lookup_type) {
696 0 : case Single: return u.single.apply (c);
697 0 : case Multiple: return u.multiple.apply (c);
698 0 : case Alternate: return u.alternate.apply (c);
699 0 : case Ligature: return u.ligature.apply (c);
700 0 : case Context: return u.c.apply (c);
701 0 : case ChainContext: return u.chainContext.apply (c);
702 0 : case Extension: return u.extension.apply (c);
703 0 : case ReverseChainSingle: return u.reverseChainContextSingle.apply (c);
704 0 : default:return false;
705 : }
706 : }
707 :
708 0 : inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
709 0 : TRACE_SANITIZE ();
710 0 : switch (lookup_type) {
711 0 : case Single: return u.single.sanitize (c);
712 0 : case Multiple: return u.multiple.sanitize (c);
713 0 : case Alternate: return u.alternate.sanitize (c);
714 0 : case Ligature: return u.ligature.sanitize (c);
715 0 : case Context: return u.c.sanitize (c);
716 0 : case ChainContext: return u.chainContext.sanitize (c);
717 0 : case Extension: return u.extension.sanitize (c);
718 0 : case ReverseChainSingle: return u.reverseChainContextSingle.sanitize (c);
719 0 : default:return true;
720 : }
721 : }
722 :
723 : private:
724 : union {
725 : USHORT sub_format;
726 : SingleSubst single;
727 : MultipleSubst multiple;
728 : AlternateSubst alternate;
729 : LigatureSubst ligature;
730 : ContextSubst c;
731 : ChainContextSubst chainContext;
732 : ExtensionSubst extension;
733 : ReverseChainSingleSubst reverseChainContextSingle;
734 : } u;
735 : public:
736 : DEFINE_SIZE_UNION (2, sub_format);
737 : };
738 :
739 :
740 : struct SubstLookup : Lookup
741 : {
742 0 : inline const SubstLookupSubTable& get_subtable (unsigned int i) const
743 0 : { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
744 :
745 0 : inline static bool lookup_type_is_reverse (unsigned int lookup_type)
746 0 : { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
747 :
748 0 : inline bool is_reverse (void) const
749 : {
750 0 : unsigned int type = get_type ();
751 0 : if (unlikely (type == SubstLookupSubTable::Extension))
752 0 : return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
753 0 : return lookup_type_is_reverse (type);
754 : }
755 :
756 :
757 0 : inline bool apply_once (hb_face_t *face,
758 : hb_buffer_t *buffer,
759 : hb_mask_t lookup_mask,
760 : unsigned int context_length,
761 : unsigned int nesting_level_left) const
762 : {
763 0 : unsigned int lookup_type = get_type ();
764 0 : hb_apply_context_t c[1] = {{0}};
765 :
766 0 : c->face = face;
767 0 : c->buffer = buffer;
768 0 : c->direction = buffer->props.direction;
769 0 : c->lookup_mask = lookup_mask;
770 0 : c->context_length = context_length;
771 0 : c->nesting_level_left = nesting_level_left;
772 0 : c->lookup_props = get_props ();
773 :
774 0 : if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, &c->property))
775 0 : return false;
776 :
777 0 : if (unlikely (lookup_type == SubstLookupSubTable::Extension))
778 : {
779 : /* The spec says all subtables should have the same type.
780 : * This is specially important if one has a reverse type!
781 : *
782 : * This is rather slow to do this here for every glyph,
783 : * but it's easiest, and who uses extension lookups anyway?!*/
784 0 : unsigned int count = get_subtable_count ();
785 0 : unsigned int type = get_subtable(0).u.extension.get_type ();
786 0 : for (unsigned int i = 1; i < count; i++)
787 0 : if (get_subtable(i).u.extension.get_type () != type)
788 0 : return false;
789 : }
790 :
791 0 : unsigned int count = get_subtable_count ();
792 0 : for (unsigned int i = 0; i < count; i++)
793 0 : if (get_subtable (i).apply (c, lookup_type))
794 0 : return true;
795 :
796 0 : return false;
797 : }
798 :
799 0 : inline bool apply_string (hb_face_t *face,
800 : hb_buffer_t *buffer,
801 : hb_mask_t mask) const
802 : {
803 0 : bool ret = false;
804 :
805 0 : if (unlikely (!buffer->len))
806 0 : return false;
807 :
808 0 : if (likely (!is_reverse ()))
809 : {
810 : /* in/out forward substitution */
811 0 : buffer->clear_output ();
812 0 : buffer->idx = 0;
813 0 : while (buffer->idx < buffer->len)
814 : {
815 0 : if ((buffer->info[buffer->idx].mask & mask) &&
816 0 : apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
817 0 : ret = true;
818 : else
819 0 : buffer->next_glyph ();
820 :
821 : }
822 0 : if (ret)
823 0 : buffer->swap_buffers ();
824 : }
825 : else
826 : {
827 : /* in-place backward substitution */
828 0 : buffer->idx = buffer->len - 1;
829 0 : do
830 : {
831 0 : if ((buffer->info[buffer->idx].mask & mask) &&
832 0 : apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
833 0 : ret = true;
834 : else
835 0 : buffer->idx--;
836 :
837 : }
838 : while ((int) buffer->idx >= 0);
839 : }
840 :
841 0 : return ret;
842 : }
843 :
844 0 : inline bool sanitize (hb_sanitize_context_t *c) {
845 0 : TRACE_SANITIZE ();
846 0 : if (unlikely (!Lookup::sanitize (c))) return false;
847 0 : OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
848 0 : return list.sanitize (c, this, get_type ());
849 : }
850 : };
851 :
852 : typedef OffsetListOf<SubstLookup> SubstLookupList;
853 :
854 : /*
855 : * GSUB -- The Glyph Substitution Table
856 : */
857 :
858 : struct GSUB : GSUBGPOS
859 : {
860 : static const hb_tag_t Tag = HB_OT_TAG_GSUB;
861 :
862 0 : inline const SubstLookup& get_lookup (unsigned int i) const
863 0 : { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
864 :
865 0 : inline bool substitute_lookup (hb_face_t *face,
866 : hb_buffer_t *buffer,
867 : unsigned int lookup_index,
868 : hb_mask_t mask) const
869 0 : { return get_lookup (lookup_index).apply_string (face, buffer, mask); }
870 :
871 : static inline void substitute_start (hb_buffer_t *buffer);
872 : static inline void substitute_finish (hb_buffer_t *buffer);
873 :
874 0 : inline bool sanitize (hb_sanitize_context_t *c) {
875 0 : TRACE_SANITIZE ();
876 0 : if (unlikely (!GSUBGPOS::sanitize (c))) return false;
877 0 : OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
878 0 : return list.sanitize (c, this);
879 : }
880 : public:
881 : DEFINE_SIZE_STATIC (10);
882 : };
883 :
884 :
885 : void
886 0 : GSUB::substitute_start (hb_buffer_t *buffer)
887 : {
888 0 : HB_BUFFER_ALLOCATE_VAR (buffer, props_cache);
889 0 : HB_BUFFER_ALLOCATE_VAR (buffer, lig_id);
890 0 : HB_BUFFER_ALLOCATE_VAR (buffer, lig_comp);
891 :
892 0 : unsigned int count = buffer->len;
893 0 : for (unsigned int i = 0; i < count; i++)
894 0 : buffer->info[i].props_cache() = buffer->info[i].lig_id() = buffer->info[i].lig_comp() = 0;
895 0 : }
896 :
897 : void
898 0 : GSUB::substitute_finish (hb_buffer_t *buffer)
899 : {
900 0 : }
901 :
902 :
903 : /* Out-of-class implementation for methods recursing */
904 :
905 0 : inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
906 : {
907 0 : TRACE_APPLY ();
908 0 : return get_subtable ().apply (c, get_type ());
909 : }
910 :
911 0 : inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
912 : {
913 0 : TRACE_SANITIZE ();
914 0 : if (unlikely (!Extension::sanitize (c))) return false;
915 0 : unsigned int offset = get_offset ();
916 0 : if (unlikely (!offset)) return true;
917 0 : return StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ());
918 : }
919 :
920 0 : inline bool ExtensionSubst::is_reverse (void) const
921 : {
922 0 : unsigned int type = get_type ();
923 0 : if (unlikely (type == SubstLookupSubTable::Extension))
924 0 : return CastR<ExtensionSubst> (get_subtable()).is_reverse ();
925 0 : return SubstLookup::lookup_type_is_reverse (type);
926 : }
927 :
928 0 : static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
929 : {
930 0 : const GSUB &gsub = *(c->face->ot_layout->gsub);
931 0 : const SubstLookup &l = gsub.get_lookup (lookup_index);
932 :
933 0 : if (unlikely (c->nesting_level_left == 0))
934 0 : return false;
935 :
936 0 : if (unlikely (c->context_length < 1))
937 0 : return false;
938 :
939 0 : return l.apply_once (c->face, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1);
940 : }
941 :
942 :
943 :
944 : #endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */
|