2 * Copyright © 2007,2008,2009,2010 Red Hat, Inc.
3 * Copyright © 2010 Google, Inc.
5 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
29 #ifndef HB_OT_LAYOUT_GSUB_PRIVATE_HH
30 #define HB_OT_LAYOUT_GSUB_PRIVATE_HH
32 #include "hb-ot-layout-gsubgpos-private.hh"
37 struct SingleSubstFormat1
39 friend struct SingleSubst;
43 inline bool apply (hb_apply_context_t *c) const
46 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
47 unsigned int index = (this+coverage) (glyph_id);
48 if (likely (index == NOT_COVERED))
51 glyph_id += deltaGlyphID;
52 c->replace_glyph (glyph_id);
57 inline bool sanitize (hb_sanitize_context_t *c) {
59 return coverage.sanitize (c, this)
60 && deltaGlyphID.sanitize (c);
64 USHORT format; /* Format identifier--format = 1 */
66 coverage; /* Offset to Coverage table--from
67 * beginning of Substitution table */
68 SHORT deltaGlyphID; /* Add to original GlyphID to get
69 * substitute GlyphID */
71 DEFINE_SIZE_STATIC (6);
74 struct SingleSubstFormat2
76 friend struct SingleSubst;
80 inline bool apply (hb_apply_context_t *c) const
83 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
84 unsigned int index = (this+coverage) (glyph_id);
85 if (likely (index == NOT_COVERED))
88 if (unlikely (index >= substitute.len))
91 glyph_id = substitute[index];
92 c->replace_glyph (glyph_id);
97 inline bool sanitize (hb_sanitize_context_t *c) {
99 return coverage.sanitize (c, this)
100 && substitute.sanitize (c);
104 USHORT format; /* Format identifier--format = 2 */
106 coverage; /* Offset to Coverage table--from
107 * beginning of Substitution table */
109 substitute; /* Array of substitute
110 * GlyphIDs--ordered by Coverage Index */
112 DEFINE_SIZE_ARRAY (6, substitute);
117 friend struct SubstLookupSubTable;
121 inline bool apply (hb_apply_context_t *c) const
125 case 1: return u.format1.apply (c);
126 case 2: return u.format2.apply (c);
127 default:return false;
131 inline bool sanitize (hb_sanitize_context_t *c) {
133 if (!u.format.sanitize (c)) return false;
135 case 1: return u.format1.sanitize (c);
136 case 2: return u.format2.sanitize (c);
143 USHORT format; /* Format identifier */
144 SingleSubstFormat1 format1;
145 SingleSubstFormat2 format2;
152 friend struct MultipleSubstFormat1;
155 inline bool apply (hb_apply_context_t *c) const
158 if (unlikely (!substitute.len))
161 if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)
162 c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH);
163 c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array);
169 inline bool sanitize (hb_sanitize_context_t *c) {
171 return substitute.sanitize (c);
176 substitute; /* String of GlyphIDs to substitute */
178 DEFINE_SIZE_ARRAY (2, substitute);
181 struct MultipleSubstFormat1
183 friend struct MultipleSubst;
187 inline bool apply (hb_apply_context_t *c) const
191 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
192 if (likely (index == NOT_COVERED))
195 return (this+sequence[index]).apply (c);
198 inline bool sanitize (hb_sanitize_context_t *c) {
200 return coverage.sanitize (c, this)
201 && sequence.sanitize (c, this);
205 USHORT format; /* Format identifier--format = 1 */
207 coverage; /* Offset to Coverage table--from
208 * beginning of Substitution table */
209 OffsetArrayOf<Sequence>
210 sequence; /* Array of Sequence tables
211 * ordered by Coverage Index */
213 DEFINE_SIZE_ARRAY (6, sequence);
218 friend struct SubstLookupSubTable;
222 inline bool apply (hb_apply_context_t *c) const
226 case 1: return u.format1.apply (c);
227 default:return false;
231 inline bool sanitize (hb_sanitize_context_t *c) {
233 if (!u.format.sanitize (c)) return false;
235 case 1: return u.format1.sanitize (c);
242 USHORT format; /* Format identifier */
243 MultipleSubstFormat1 format1;
248 typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in
251 struct AlternateSubstFormat1
253 friend struct AlternateSubst;
257 inline bool apply (hb_apply_context_t *c) const
260 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
261 hb_mask_t glyph_mask = c->buffer->info[c->buffer->i].mask;
262 hb_mask_t lookup_mask = c->lookup_mask;
264 unsigned int index = (this+coverage) (glyph_id);
265 if (likely (index == NOT_COVERED))
268 const AlternateSet &alt_set = this+alternateSet[index];
270 if (unlikely (!alt_set.len))
273 /* Note: This breaks badly if two features enabled this lookup together. */
274 unsigned int shift = _hb_ctz (lookup_mask);
275 unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
277 if (unlikely (alt_index > alt_set.len || alt_index == 0))
280 glyph_id = alt_set[alt_index - 1];
282 c->replace_glyph (glyph_id);
287 inline bool sanitize (hb_sanitize_context_t *c) {
289 return coverage.sanitize (c, this)
290 && alternateSet.sanitize (c, this);
294 USHORT format; /* Format identifier--format = 1 */
296 coverage; /* Offset to Coverage table--from
297 * beginning of Substitution table */
298 OffsetArrayOf<AlternateSet>
299 alternateSet; /* Array of AlternateSet tables
300 * ordered by Coverage Index */
302 DEFINE_SIZE_ARRAY (6, alternateSet);
305 struct AlternateSubst
307 friend struct SubstLookupSubTable;
311 inline bool apply (hb_apply_context_t *c) const
315 case 1: return u.format1.apply (c);
316 default:return false;
320 inline bool sanitize (hb_sanitize_context_t *c) {
322 if (!u.format.sanitize (c)) return false;
324 case 1: return u.format1.sanitize (c);
331 USHORT format; /* Format identifier */
332 AlternateSubstFormat1 format1;
339 friend struct LigatureSet;
342 inline bool apply (hb_apply_context_t *c) const
346 unsigned int count = component.len;
347 unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
348 if (unlikely (count < 2 || c->buffer->i + count > end))
351 bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
352 bool found_non_mark = false;
354 for (i = 1, j = c->buffer->i + 1; i < count; i++, j++)
356 unsigned int property;
357 while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, &property))
359 if (unlikely (j + count - i == end))
364 found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
366 if (likely (c->buffer->info[j].codepoint != component[i]))
370 if (first_was_mark && found_non_mark)
371 c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);
373 /* Allocate new ligature id */
374 unsigned int lig_id = allocate_lig_id (c->buffer);
375 c->buffer->info[c->buffer->i].lig_comp() = 0;
376 c->buffer->info[c->buffer->i].lig_id() = lig_id;
378 if (j == c->buffer->i + i) /* No input glyphs skipped */
380 c->replace_glyphs_be16 (i, 1, (const uint16_t *) &ligGlyph);
384 c->replace_glyph (ligGlyph);
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. */
393 for (i = 1; i < count; i++)
395 while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[c->buffer->i], c->lookup_props, NULL))
397 c->buffer->info[c->buffer->i].lig_comp() = i;
398 c->buffer->info[c->buffer->i].lig_id() = lig_id;
399 c->replace_glyph (c->buffer->info[c->buffer->i].codepoint);
402 /* Skip the base glyph */
410 inline uint16_t allocate_lig_id (hb_buffer_t *buffer) const {
411 uint16_t lig_id = buffer->next_serial ();
412 if (unlikely (!lig_id)) lig_id = buffer->next_serial (); /* in case of overflow */
417 inline bool sanitize (hb_sanitize_context_t *c) {
419 return ligGlyph.sanitize (c)
420 && component.sanitize (c);
424 GlyphID ligGlyph; /* GlyphID of ligature to substitute */
425 HeadlessArrayOf<GlyphID>
426 component; /* Array of component GlyphIDs--start
427 * with the second component--ordered
428 * in writing direction */
430 DEFINE_SIZE_ARRAY (4, component);
435 friend struct LigatureSubstFormat1;
438 inline bool apply (hb_apply_context_t *c) const
441 unsigned int num_ligs = ligature.len;
442 for (unsigned int i = 0; i < num_ligs; i++)
444 const Ligature &lig = this+ligature[i];
453 inline bool sanitize (hb_sanitize_context_t *c) {
455 return ligature.sanitize (c, this);
459 OffsetArrayOf<Ligature>
460 ligature; /* Array LigatureSet tables
461 * ordered by preference */
463 DEFINE_SIZE_ARRAY (2, ligature);
466 struct LigatureSubstFormat1
468 friend struct LigatureSubst;
471 inline bool apply (hb_apply_context_t *c) const
474 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
476 unsigned int index = (this+coverage) (glyph_id);
477 if (likely (index == NOT_COVERED))
480 const LigatureSet &lig_set = this+ligatureSet[index];
481 return lig_set.apply (c);
484 inline bool sanitize (hb_sanitize_context_t *c) {
486 return coverage.sanitize (c, this)
487 && ligatureSet.sanitize (c, this);
491 USHORT format; /* Format identifier--format = 1 */
493 coverage; /* Offset to Coverage table--from
494 * beginning of Substitution table */
495 OffsetArrayOf<LigatureSet>
496 ligatureSet; /* Array LigatureSet tables
497 * ordered by Coverage Index */
499 DEFINE_SIZE_ARRAY (6, ligatureSet);
504 friend struct SubstLookupSubTable;
507 inline bool apply (hb_apply_context_t *c) const
511 case 1: return u.format1.apply (c);
512 default:return false;
516 inline bool sanitize (hb_sanitize_context_t *c) {
518 if (!u.format.sanitize (c)) return false;
520 case 1: return u.format1.sanitize (c);
527 USHORT format; /* Format identifier */
528 LigatureSubstFormat1 format1;
534 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
537 struct ContextSubst : Context
539 friend struct SubstLookupSubTable;
542 inline bool apply (hb_apply_context_t *c) const
545 return Context::apply (c, substitute_lookup);
549 struct ChainContextSubst : ChainContext
551 friend struct SubstLookupSubTable;
554 inline bool apply (hb_apply_context_t *c) const
557 return ChainContext::apply (c, substitute_lookup);
562 struct ExtensionSubst : Extension
564 friend struct SubstLookupSubTable;
565 friend struct SubstLookup;
568 inline const struct SubstLookupSubTable& get_subtable (void) const
570 unsigned int offset = get_offset ();
571 if (unlikely (!offset)) return Null(SubstLookupSubTable);
572 return StructAtOffset<SubstLookupSubTable> (this, offset);
575 inline bool apply (hb_apply_context_t *c) const;
577 inline bool sanitize (hb_sanitize_context_t *c);
579 inline bool is_reverse (void) const;
583 struct ReverseChainSingleSubstFormat1
585 friend struct ReverseChainSingleSubst;
588 inline bool apply (hb_apply_context_t *c) const
591 if (unlikely (c->context_length != NO_CONTEXT))
592 return false; /* No chaining to this type */
594 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
595 if (likely (index == NOT_COVERED))
598 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
599 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
601 if (match_backtrack (c,
602 backtrack.len, (USHORT *) backtrack.array,
603 match_coverage, this) &&
605 lookahead.len, (USHORT *) lookahead.array,
606 match_coverage, this,
609 c->buffer->info[c->buffer->i].codepoint = substitute[index];
610 c->buffer->i--; /* Reverse! */
617 inline bool sanitize (hb_sanitize_context_t *c) {
619 if (!(coverage.sanitize (c, this)
620 && backtrack.sanitize (c, this)))
622 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
623 if (!lookahead.sanitize (c, this))
625 ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
626 return substitute.sanitize (c);
630 USHORT format; /* Format identifier--format = 1 */
632 coverage; /* Offset to Coverage table--from
633 * beginning of table */
634 OffsetArrayOf<Coverage>
635 backtrack; /* Array of coverage tables
636 * in backtracking sequence, in glyph
638 OffsetArrayOf<Coverage>
639 lookaheadX; /* Array of coverage tables
640 * in lookahead sequence, in glyph
643 substituteX; /* Array of substitute
644 * GlyphIDs--ordered by Coverage Index */
646 DEFINE_SIZE_MIN (10);
649 struct ReverseChainSingleSubst
651 friend struct SubstLookupSubTable;
654 inline bool apply (hb_apply_context_t *c) const
658 case 1: return u.format1.apply (c);
659 default:return false;
663 inline bool sanitize (hb_sanitize_context_t *c) {
665 if (!u.format.sanitize (c)) return false;
667 case 1: return u.format1.sanitize (c);
674 USHORT format; /* Format identifier */
675 ReverseChainSingleSubstFormat1 format1;
685 struct SubstLookupSubTable
687 friend struct SubstLookup;
697 ReverseChainSingle = 8
700 inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
703 switch (lookup_type) {
704 case Single: return u.single.apply (c);
705 case Multiple: return u.multiple.apply (c);
706 case Alternate: return u.alternate.apply (c);
707 case Ligature: return u.ligature.apply (c);
708 case Context: return u.c.apply (c);
709 case ChainContext: return u.chainContext.apply (c);
710 case Extension: return u.extension.apply (c);
711 case ReverseChainSingle: return u.reverseChainContextSingle.apply (c);
712 default:return false;
716 inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
718 switch (lookup_type) {
719 case Single: return u.single.sanitize (c);
720 case Multiple: return u.multiple.sanitize (c);
721 case Alternate: return u.alternate.sanitize (c);
722 case Ligature: return u.ligature.sanitize (c);
723 case Context: return u.c.sanitize (c);
724 case ChainContext: return u.chainContext.sanitize (c);
725 case Extension: return u.extension.sanitize (c);
726 case ReverseChainSingle: return u.reverseChainContextSingle.sanitize (c);
735 MultipleSubst multiple;
736 AlternateSubst alternate;
737 LigatureSubst ligature;
739 ChainContextSubst chainContext;
740 ExtensionSubst extension;
741 ReverseChainSingleSubst reverseChainContextSingle;
744 DEFINE_SIZE_UNION (2, sub_format);
748 struct SubstLookup : Lookup
750 inline const SubstLookupSubTable& get_subtable (unsigned int i) const
751 { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
753 inline static bool lookup_type_is_reverse (unsigned int lookup_type)
754 { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
756 inline bool is_reverse (void) const
758 unsigned int type = get_type ();
759 if (unlikely (type == SubstLookupSubTable::Extension))
760 return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
761 return lookup_type_is_reverse (type);
765 inline bool apply_once (hb_face_t *face,
767 hb_mask_t lookup_mask,
768 unsigned int context_length,
769 unsigned int nesting_level_left) const
771 unsigned int lookup_type = get_type ();
772 hb_apply_context_t c[1] = {{0}};
776 c->direction = buffer->props.direction;
777 c->lookup_mask = lookup_mask;
778 c->context_length = context_length;
779 c->nesting_level_left = nesting_level_left;
780 c->lookup_props = get_props ();
782 if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->info[c->buffer->i], c->lookup_props, &c->property))
785 if (unlikely (lookup_type == SubstLookupSubTable::Extension))
787 /* The spec says all subtables should have the same type.
788 * This is specially important if one has a reverse type!
790 * This is rather slow to do this here for every glyph,
791 * but it's easiest, and who uses extension lookups anyway?!*/
792 unsigned int count = get_subtable_count ();
793 unsigned int type = get_subtable(0).u.extension.get_type ();
794 for (unsigned int i = 1; i < count; i++)
795 if (get_subtable(i).u.extension.get_type () != type)
799 unsigned int count = get_subtable_count ();
800 for (unsigned int i = 0; i < count; i++)
801 if (get_subtable (i).apply (c, lookup_type))
807 inline bool apply_string (hb_face_t *face,
809 hb_mask_t mask) const
813 if (unlikely (!buffer->len))
816 if (likely (!is_reverse ()))
818 /* in/out forward substitution */
819 buffer->clear_output ();
821 while (buffer->i < buffer->len)
823 if ((buffer->info[buffer->i].mask & mask) &&
824 apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
827 buffer->next_glyph ();
835 /* in-place backward substitution */
836 buffer->i = buffer->len - 1;
839 if ((buffer->info[buffer->i].mask & mask) &&
840 apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
846 while ((int) buffer->i >= 0);
852 inline bool sanitize (hb_sanitize_context_t *c) {
854 if (unlikely (!Lookup::sanitize (c))) return false;
855 OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
856 return list.sanitize (c, this, get_type ());
860 typedef OffsetListOf<SubstLookup> SubstLookupList;
866 struct GSUB : GSUBGPOS
868 static const hb_tag_t Tag = HB_OT_TAG_GSUB;
870 inline const SubstLookup& get_lookup (unsigned int i) const
871 { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
873 inline bool substitute_lookup (hb_face_t *face,
875 unsigned int lookup_index,
876 hb_mask_t mask) const
877 { return get_lookup (lookup_index).apply_string (face, buffer, mask); }
879 inline bool sanitize (hb_sanitize_context_t *c) {
881 if (unlikely (!GSUBGPOS::sanitize (c))) return false;
882 OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
883 return list.sanitize (c, this);
886 DEFINE_SIZE_STATIC (10);
890 /* Out-of-class implementation for methods recursing */
892 inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
895 return get_subtable ().apply (c, get_type ());
898 inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
901 if (unlikely (!Extension::sanitize (c))) return false;
902 unsigned int offset = get_offset ();
903 if (unlikely (!offset)) return true;
904 return StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ());
907 inline bool ExtensionSubst::is_reverse (void) const
909 unsigned int type = get_type ();
910 if (unlikely (type == SubstLookupSubTable::Extension))
911 return CastR<ExtensionSubst> (get_subtable()).is_reverse ();
912 return SubstLookup::lookup_type_is_reverse (type);
915 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
917 const GSUB &gsub = *(c->face->ot_layout->gsub);
918 const SubstLookup &l = gsub.get_lookup (lookup_index);
920 if (unlikely (c->nesting_level_left == 0))
923 if (unlikely (c->context_length < 1))
926 return l.apply_once (c->face, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1);
932 #endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */