2 * Copyright (C) 2007,2008,2009,2010 Red Hat, Inc.
4 * This is part of HarfBuzz, a text shaping library.
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 * Red Hat Author(s): Behdad Esfahbod
27 #ifndef HB_OT_LAYOUT_GSUB_PRIVATE_HH
28 #define HB_OT_LAYOUT_GSUB_PRIVATE_HH
30 #include "hb-ot-layout-gsubgpos-private.hh"
35 struct SingleSubstFormat1
37 friend struct SingleSubst;
41 inline bool apply (hb_apply_context_t *c) const
44 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
45 unsigned int index = (this+coverage) (glyph_id);
46 if (likely (index == NOT_COVERED))
49 glyph_id += deltaGlyphID;
50 c->buffer->replace_glyph (glyph_id);
52 /* We inherit the old glyph class to the substituted glyph */
53 if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
54 _hb_ot_layout_set_glyph_property (c->layout->face, glyph_id, c->property);
59 inline bool sanitize (hb_sanitize_context_t *c) {
61 return coverage.sanitize (c, this)
62 && deltaGlyphID.sanitize (c);
66 USHORT format; /* Format identifier--format = 1 */
68 coverage; /* Offset to Coverage table--from
69 * beginning of Substitution table */
70 SHORT deltaGlyphID; /* Add to original GlyphID to get
71 * substitute GlyphID */
73 DEFINE_SIZE_STATIC (6);
76 struct SingleSubstFormat2
78 friend struct SingleSubst;
82 inline bool apply (hb_apply_context_t *c) const
85 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
86 unsigned int index = (this+coverage) (glyph_id);
87 if (likely (index == NOT_COVERED))
90 if (unlikely (index >= substitute.len))
93 glyph_id = substitute[index];
94 c->buffer->replace_glyph (glyph_id);
96 /* We inherit the old glyph class to the substituted glyph */
97 if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
98 _hb_ot_layout_set_glyph_property (c->layout->face, glyph_id, c->property);
103 inline bool sanitize (hb_sanitize_context_t *c) {
105 return coverage.sanitize (c, this)
106 && substitute.sanitize (c);
110 USHORT format; /* Format identifier--format = 2 */
112 coverage; /* Offset to Coverage table--from
113 * beginning of Substitution table */
115 substitute; /* Array of substitute
116 * GlyphIDs--ordered by Coverage Index */
118 DEFINE_SIZE_ARRAY (6, substitute);
123 friend struct SubstLookupSubTable;
127 inline bool apply (hb_apply_context_t *c) const
131 case 1: return u.format1.apply (c);
132 case 2: return u.format2.apply (c);
133 default:return false;
137 inline bool sanitize (hb_sanitize_context_t *c) {
139 if (!u.format.sanitize (c)) return false;
141 case 1: return u.format1.sanitize (c);
142 case 2: return u.format2.sanitize (c);
149 USHORT format; /* Format identifier */
150 SingleSubstFormat1 format1;
151 SingleSubstFormat2 format2;
158 friend struct MultipleSubstFormat1;
161 inline bool apply (hb_apply_context_t *c) const
164 if (unlikely (!substitute.len))
167 c->buffer->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array);
169 /* This is a guess only ... */
170 if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
172 unsigned int property = c->property;
173 if (property == HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)
174 property = HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
176 unsigned int count = substitute.len;
177 for (unsigned int n = 0; n < count; n++)
178 _hb_ot_layout_set_glyph_property (c->layout->face, substitute[n], property);
185 inline bool sanitize (hb_sanitize_context_t *c) {
187 return substitute.sanitize (c);
192 substitute; /* String of GlyphIDs to substitute */
194 DEFINE_SIZE_ARRAY (2, substitute);
197 struct MultipleSubstFormat1
199 friend struct MultipleSubst;
203 inline bool apply (hb_apply_context_t *c) const
207 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
208 if (likely (index == NOT_COVERED))
211 return (this+sequence[index]).apply (c);
214 inline bool sanitize (hb_sanitize_context_t *c) {
216 return coverage.sanitize (c, this)
217 && sequence.sanitize (c, this);
221 USHORT format; /* Format identifier--format = 1 */
223 coverage; /* Offset to Coverage table--from
224 * beginning of Substitution table */
225 OffsetArrayOf<Sequence>
226 sequence; /* Array of Sequence tables
227 * ordered by Coverage Index */
229 DEFINE_SIZE_ARRAY (6, sequence);
234 friend struct SubstLookupSubTable;
238 inline bool apply (hb_apply_context_t *c) const
242 case 1: return u.format1.apply (c);
243 default:return false;
247 inline bool sanitize (hb_sanitize_context_t *c) {
249 if (!u.format.sanitize (c)) return false;
251 case 1: return u.format1.sanitize (c);
258 USHORT format; /* Format identifier */
259 MultipleSubstFormat1 format1;
264 typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in
267 struct AlternateSubstFormat1
269 friend struct AlternateSubst;
273 inline bool apply (hb_apply_context_t *c) const
276 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
277 hb_mask_t glyph_mask = c->buffer->info[c->buffer->i].mask;
278 hb_mask_t lookup_mask = c->lookup_mask;
280 unsigned int index = (this+coverage) (glyph_id);
281 if (likely (index == NOT_COVERED))
284 const AlternateSet &alt_set = this+alternateSet[index];
286 if (unlikely (!alt_set.len))
289 /* Note: This breaks badly if two features enabled this lookup together. */
290 unsigned int shift = _hb_ctz (lookup_mask);
291 unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
293 if (unlikely (alt_index > alt_set.len || alt_index == 0))
296 glyph_id = alt_set[alt_index - 1];
298 c->buffer->replace_glyph (glyph_id);
300 /* We inherit the old glyph class to the substituted glyph */
301 if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
302 _hb_ot_layout_set_glyph_property (c->layout->face, glyph_id, c->property);
307 inline bool sanitize (hb_sanitize_context_t *c) {
309 return coverage.sanitize (c, this)
310 && alternateSet.sanitize (c, this);
314 USHORT format; /* Format identifier--format = 1 */
316 coverage; /* Offset to Coverage table--from
317 * beginning of Substitution table */
318 OffsetArrayOf<AlternateSet>
319 alternateSet; /* Array of AlternateSet tables
320 * ordered by Coverage Index */
322 DEFINE_SIZE_ARRAY (6, alternateSet);
325 struct AlternateSubst
327 friend struct SubstLookupSubTable;
331 inline bool apply (hb_apply_context_t *c) const
335 case 1: return u.format1.apply (c);
336 default:return false;
340 inline bool sanitize (hb_sanitize_context_t *c) {
342 if (!u.format.sanitize (c)) return false;
344 case 1: return u.format1.sanitize (c);
351 USHORT format; /* Format identifier */
352 AlternateSubstFormat1 format1;
359 friend struct LigatureSet;
362 inline bool apply (hb_apply_context_t *c, bool is_mark) const
366 unsigned int count = component.len;
367 unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
368 if (unlikely (c->buffer->i + count > end))
371 for (i = 1, j = c->buffer->i + 1; i < count; i++, j++)
373 unsigned int property;
374 while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, &property))
376 if (unlikely (j + count - i == end))
381 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
384 if (likely (c->buffer->info[j].codepoint != component[i]))
387 /* This is just a guess ... */
388 if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
389 _hb_ot_layout_set_glyph_class (c->layout->face, ligGlyph,
390 is_mark ? HB_OT_LAYOUT_GLYPH_CLASS_MARK
391 : HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);
393 /* Allocate new ligature id */
394 unsigned int lig_id = allocate_lig_id (c->buffer);
395 c->buffer->info[c->buffer->i].component() = 0;
396 c->buffer->info[c->buffer->i].lig_id() = lig_id;
398 if (j == c->buffer->i + i) /* No input glyphs skipped */
400 c->buffer->replace_glyphs_be16 (i, 1, (const uint16_t *) &ligGlyph);
404 c->buffer->replace_glyph (ligGlyph);
406 /* Now we must do a second loop to copy the skipped glyphs to
407 `out' and assign component values to it. We start with the
408 glyph after the first component. Glyphs between component
409 i and i+1 belong to component i. Together with the lig_id
410 value it is later possible to check whether a specific
411 component value really belongs to a given ligature. */
413 for (i = 1; i < count; i++)
415 while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_flag, NULL))
417 c->buffer->info[c->buffer->i].component() = i;
418 c->buffer->info[c->buffer->i].lig_id() = lig_id;
419 c->buffer->replace_glyph (c->buffer->info[c->buffer->i].codepoint);
422 /* Skip the base glyph */
430 inline uint16_t allocate_lig_id (hb_buffer_t *buffer) const {
431 uint16_t lig_id = buffer->next_serial ();
432 if (unlikely (!lig_id)) lig_id = buffer->next_serial (); /* in case of overflows */
437 inline bool sanitize (hb_sanitize_context_t *c) {
439 return ligGlyph.sanitize (c)
440 && component.sanitize (c);
444 GlyphID ligGlyph; /* GlyphID of ligature to substitute */
445 HeadlessArrayOf<GlyphID>
446 component; /* Array of component GlyphIDs--start
447 * with the second component--ordered
448 * in writing direction */
450 DEFINE_SIZE_ARRAY (4, component);
455 friend struct LigatureSubstFormat1;
458 inline bool apply (hb_apply_context_t *c, bool is_mark) const
461 unsigned int num_ligs = ligature.len;
462 for (unsigned int i = 0; i < num_ligs; i++)
464 const Ligature &lig = this+ligature[i];
465 if (lig.apply (c, is_mark))
473 inline bool sanitize (hb_sanitize_context_t *c) {
475 return ligature.sanitize (c, this);
479 OffsetArrayOf<Ligature>
480 ligature; /* Array LigatureSet tables
481 * ordered by preference */
483 DEFINE_SIZE_ARRAY (2, ligature);
486 struct LigatureSubstFormat1
488 friend struct LigatureSubst;
491 inline bool apply (hb_apply_context_t *c) const
494 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
496 bool first_is_mark = !!(c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
498 unsigned int index = (this+coverage) (glyph_id);
499 if (likely (index == NOT_COVERED))
502 const LigatureSet &lig_set = this+ligatureSet[index];
503 return lig_set.apply (c, first_is_mark);
506 inline bool sanitize (hb_sanitize_context_t *c) {
508 return coverage.sanitize (c, this)
509 && ligatureSet.sanitize (c, this);
513 USHORT format; /* Format identifier--format = 1 */
515 coverage; /* Offset to Coverage table--from
516 * beginning of Substitution table */
517 OffsetArrayOf<LigatureSet>
518 ligatureSet; /* Array LigatureSet tables
519 * ordered by Coverage Index */
521 DEFINE_SIZE_ARRAY (6, ligatureSet);
526 friend struct SubstLookupSubTable;
529 inline bool apply (hb_apply_context_t *c) const
533 case 1: return u.format1.apply (c);
534 default:return false;
538 inline bool sanitize (hb_sanitize_context_t *c) {
540 if (!u.format.sanitize (c)) return false;
542 case 1: return u.format1.sanitize (c);
549 USHORT format; /* Format identifier */
550 LigatureSubstFormat1 format1;
556 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
559 struct ContextSubst : Context
561 friend struct SubstLookupSubTable;
564 inline bool apply (hb_apply_context_t *c) const
567 return Context::apply (c, substitute_lookup);
571 struct ChainContextSubst : ChainContext
573 friend struct SubstLookupSubTable;
576 inline bool apply (hb_apply_context_t *c) const
579 return ChainContext::apply (c, substitute_lookup);
584 struct ExtensionSubst : Extension
586 friend struct SubstLookupSubTable;
587 friend struct SubstLookup;
590 inline const struct SubstLookupSubTable& get_subtable (void) const
592 unsigned int offset = get_offset ();
593 if (unlikely (!offset)) return Null(SubstLookupSubTable);
594 return StructAtOffset<SubstLookupSubTable> (this, offset);
597 inline bool apply (hb_apply_context_t *c) const;
599 inline bool sanitize (hb_sanitize_context_t *c);
601 inline bool is_reverse (void) const;
605 struct ReverseChainSingleSubstFormat1
607 friend struct ReverseChainSingleSubst;
610 inline bool apply (hb_apply_context_t *c) const
613 if (unlikely (c->context_length != NO_CONTEXT))
614 return false; /* No chaining to this type */
616 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
617 if (likely (index == NOT_COVERED))
620 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
621 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
623 if (match_backtrack (c,
624 backtrack.len, (USHORT *) backtrack.array,
625 match_coverage, this) &&
627 lookahead.len, (USHORT *) lookahead.array,
628 match_coverage, this,
631 c->buffer->info[c->buffer->i].codepoint = substitute[index];
632 c->buffer->i--; /* Reverse! */
639 inline bool sanitize (hb_sanitize_context_t *c) {
641 if (!(coverage.sanitize (c, this)
642 && backtrack.sanitize (c, this)))
644 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
645 if (!lookahead.sanitize (c, this))
647 ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
648 return substitute.sanitize (c);
652 USHORT format; /* Format identifier--format = 1 */
654 coverage; /* Offset to Coverage table--from
655 * beginning of table */
656 OffsetArrayOf<Coverage>
657 backtrack; /* Array of coverage tables
658 * in backtracking sequence, in glyph
660 OffsetArrayOf<Coverage>
661 lookaheadX; /* Array of coverage tables
662 * in lookahead sequence, in glyph
665 substituteX; /* Array of substitute
666 * GlyphIDs--ordered by Coverage Index */
668 DEFINE_SIZE_MIN (10);
671 struct ReverseChainSingleSubst
673 friend struct SubstLookupSubTable;
676 inline bool apply (hb_apply_context_t *c) const
680 case 1: return u.format1.apply (c);
681 default:return false;
685 inline bool sanitize (hb_sanitize_context_t *c) {
687 if (!u.format.sanitize (c)) return false;
689 case 1: return u.format1.sanitize (c);
696 USHORT format; /* Format identifier */
697 ReverseChainSingleSubstFormat1 format1;
707 struct SubstLookupSubTable
709 friend struct SubstLookup;
719 ReverseChainSingle = 8
722 inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
725 switch (lookup_type) {
726 case Single: return u.single.apply (c);
727 case Multiple: return u.multiple.apply (c);
728 case Alternate: return u.alternate.apply (c);
729 case Ligature: return u.ligature.apply (c);
730 case Context: return u.c.apply (c);
731 case ChainContext: return u.chainContext.apply (c);
732 case Extension: return u.extension.apply (c);
733 case ReverseChainSingle: return u.reverseChainContextSingle.apply (c);
734 default:return false;
738 inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
740 switch (lookup_type) {
741 case Single: return u.single.sanitize (c);
742 case Multiple: return u.multiple.sanitize (c);
743 case Alternate: return u.alternate.sanitize (c);
744 case Ligature: return u.ligature.sanitize (c);
745 case Context: return u.c.sanitize (c);
746 case ChainContext: return u.chainContext.sanitize (c);
747 case Extension: return u.extension.sanitize (c);
748 case ReverseChainSingle: return u.reverseChainContextSingle.sanitize (c);
757 MultipleSubst multiple;
758 AlternateSubst alternate;
759 LigatureSubst ligature;
761 ChainContextSubst chainContext;
762 ExtensionSubst extension;
763 ReverseChainSingleSubst reverseChainContextSingle;
766 DEFINE_SIZE_UNION (2, sub_format);
770 struct SubstLookup : Lookup
772 inline const SubstLookupSubTable& get_subtable (unsigned int i) const
773 { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
775 inline static bool lookup_type_is_reverse (unsigned int lookup_type)
776 { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
778 inline bool is_reverse (void) const
780 unsigned int type = get_type ();
781 if (unlikely (type == SubstLookupSubTable::Extension))
782 return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
783 return lookup_type_is_reverse (type);
787 inline bool apply_once (hb_ot_layout_context_t *layout,
789 hb_mask_t lookup_mask,
790 unsigned int context_length,
791 unsigned int nesting_level_left) const
793 unsigned int lookup_type = get_type ();
794 hb_apply_context_t c[1] = {{0}};
798 c->lookup_mask = lookup_mask;
799 c->context_length = context_length;
800 c->nesting_level_left = nesting_level_left;
801 c->lookup_flag = get_flag ();
803 if (!_hb_ot_layout_check_glyph_property (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_flag, &c->property))
806 if (unlikely (lookup_type == SubstLookupSubTable::Extension))
808 /* The spec says all subtables should have the same type.
809 * This is specially important if one has a reverse type!
811 * This is rather slow to do this here for every glyph,
812 * but it's easiest, and who uses extension lookups anyway?!*/
813 unsigned int count = get_subtable_count ();
814 unsigned int type = get_subtable(0).u.extension.get_type ();
815 for (unsigned int i = 1; i < count; i++)
816 if (get_subtable(i).u.extension.get_type () != type)
820 unsigned int count = get_subtable_count ();
821 for (unsigned int i = 0; i < count; i++)
822 if (get_subtable (i).apply (c, lookup_type))
828 inline bool apply_string (hb_ot_layout_context_t *layout,
830 hb_mask_t mask) const
834 if (unlikely (!buffer->len))
837 if (likely (!is_reverse ()))
839 /* in/out forward substitution */
840 buffer->clear_output ();
842 while (buffer->i < buffer->len)
844 if ((buffer->info[buffer->i].mask & mask) &&
845 apply_once (layout, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
848 buffer->next_glyph ();
856 /* in-place backward substitution */
857 buffer->i = buffer->len - 1;
860 if ((buffer->info[buffer->i].mask & mask) &&
861 apply_once (layout, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
867 while ((int) buffer->i >= 0);
873 inline bool sanitize (hb_sanitize_context_t *c) {
875 if (unlikely (!Lookup::sanitize (c))) return false;
876 OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
877 return list.sanitize (c, this, get_type ());
881 typedef OffsetListOf<SubstLookup> SubstLookupList;
887 struct GSUB : GSUBGPOS
889 static const hb_tag_t Tag = HB_OT_TAG_GSUB;
891 inline const SubstLookup& get_lookup (unsigned int i) const
892 { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
894 inline bool substitute_lookup (hb_ot_layout_context_t *layout,
896 unsigned int lookup_index,
897 hb_mask_t mask) const
898 { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
900 inline bool sanitize (hb_sanitize_context_t *c) {
902 if (unlikely (!GSUBGPOS::sanitize (c))) return false;
903 OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
904 return list.sanitize (c, this);
907 DEFINE_SIZE_STATIC (10);
911 /* Out-of-class implementation for methods recursing */
913 inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
916 return get_subtable ().apply (c, get_type ());
919 inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
922 if (unlikely (!Extension::sanitize (c))) return false;
923 unsigned int offset = get_offset ();
924 if (unlikely (!offset)) return true;
925 return StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ());
928 inline bool ExtensionSubst::is_reverse (void) const
930 unsigned int type = get_type ();
931 if (unlikely (type == SubstLookupSubTable::Extension))
932 return CastR<ExtensionSubst> (get_subtable()).is_reverse ();
933 return SubstLookup::lookup_type_is_reverse (type);
936 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
938 const GSUB &gsub = *(c->layout->face->ot_layout->gsub);
939 const SubstLookup &l = gsub.get_lookup (lookup_index);
941 if (unlikely (c->nesting_level_left == 0))
944 if (unlikely (c->context_length < 1))
947 return l.apply_once (c->layout, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1);
953 #endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */