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->idx].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->idx].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->idx].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->idx].codepoint;
261 hb_mask_t glyph_mask = c->buffer->info[c->buffer->idx].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->idx + c->context_length);
348 if (unlikely (count < 2 || c->buffer->idx + 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->idx + 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->idx].lig_comp() = 0;
376 c->buffer->info[c->buffer->idx].lig_id() = lig_id;
378 if (j == c->buffer->idx + 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->idx], c->lookup_props, NULL))
397 c->buffer->info[c->buffer->idx].lig_comp() = i;
398 c->buffer->info[c->buffer->idx].lig_id() = lig_id;
399 c->replace_glyph (c->buffer->info[c->buffer->idx].codepoint);
402 /* Skip the base glyph */
411 inline bool sanitize (hb_sanitize_context_t *c) {
413 return ligGlyph.sanitize (c)
414 && component.sanitize (c);
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 */
424 DEFINE_SIZE_ARRAY (4, component);
429 friend struct LigatureSubstFormat1;
432 inline bool apply (hb_apply_context_t *c) const
435 unsigned int num_ligs = ligature.len;
436 for (unsigned int i = 0; i < num_ligs; i++)
438 const Ligature &lig = this+ligature[i];
447 inline bool sanitize (hb_sanitize_context_t *c) {
449 return ligature.sanitize (c, this);
453 OffsetArrayOf<Ligature>
454 ligature; /* Array LigatureSet tables
455 * ordered by preference */
457 DEFINE_SIZE_ARRAY (2, ligature);
460 struct LigatureSubstFormat1
462 friend struct LigatureSubst;
465 inline bool apply (hb_apply_context_t *c) const
468 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
470 unsigned int index = (this+coverage) (glyph_id);
471 if (likely (index == NOT_COVERED))
474 const LigatureSet &lig_set = this+ligatureSet[index];
475 return lig_set.apply (c);
478 inline bool sanitize (hb_sanitize_context_t *c) {
480 return coverage.sanitize (c, this)
481 && ligatureSet.sanitize (c, this);
485 USHORT format; /* Format identifier--format = 1 */
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 */
493 DEFINE_SIZE_ARRAY (6, ligatureSet);
498 friend struct SubstLookupSubTable;
501 inline bool apply (hb_apply_context_t *c) const
505 case 1: return u.format1.apply (c);
506 default:return false;
510 inline bool sanitize (hb_sanitize_context_t *c) {
512 if (!u.format.sanitize (c)) return false;
514 case 1: return u.format1.sanitize (c);
521 USHORT format; /* Format identifier */
522 LigatureSubstFormat1 format1;
528 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
531 struct ContextSubst : Context
533 friend struct SubstLookupSubTable;
536 inline bool apply (hb_apply_context_t *c) const
539 return Context::apply (c, substitute_lookup);
543 struct ChainContextSubst : ChainContext
545 friend struct SubstLookupSubTable;
548 inline bool apply (hb_apply_context_t *c) const
551 return ChainContext::apply (c, substitute_lookup);
556 struct ExtensionSubst : Extension
558 friend struct SubstLookupSubTable;
559 friend struct SubstLookup;
562 inline const struct SubstLookupSubTable& get_subtable (void) const
564 unsigned int offset = get_offset ();
565 if (unlikely (!offset)) return Null(SubstLookupSubTable);
566 return StructAtOffset<SubstLookupSubTable> (this, offset);
569 inline bool apply (hb_apply_context_t *c) const;
571 inline bool sanitize (hb_sanitize_context_t *c);
573 inline bool is_reverse (void) const;
577 struct ReverseChainSingleSubstFormat1
579 friend struct ReverseChainSingleSubst;
582 inline bool apply (hb_apply_context_t *c) const
585 if (unlikely (c->context_length != NO_CONTEXT))
586 return false; /* No chaining to this type */
588 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
589 if (likely (index == NOT_COVERED))
592 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
593 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
595 if (match_backtrack (c,
596 backtrack.len, (USHORT *) backtrack.array,
597 match_coverage, this) &&
599 lookahead.len, (USHORT *) lookahead.array,
600 match_coverage, this,
603 c->buffer->info[c->buffer->idx].codepoint = substitute[index];
604 c->buffer->idx--; /* Reverse! */
611 inline bool sanitize (hb_sanitize_context_t *c) {
613 if (!(coverage.sanitize (c, this)
614 && backtrack.sanitize (c, this)))
616 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
617 if (!lookahead.sanitize (c, this))
619 ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
620 return substitute.sanitize (c);
624 USHORT format; /* Format identifier--format = 1 */
626 coverage; /* Offset to Coverage table--from
627 * beginning of table */
628 OffsetArrayOf<Coverage>
629 backtrack; /* Array of coverage tables
630 * in backtracking sequence, in glyph
632 OffsetArrayOf<Coverage>
633 lookaheadX; /* Array of coverage tables
634 * in lookahead sequence, in glyph
637 substituteX; /* Array of substitute
638 * GlyphIDs--ordered by Coverage Index */
640 DEFINE_SIZE_MIN (10);
643 struct ReverseChainSingleSubst
645 friend struct SubstLookupSubTable;
648 inline bool apply (hb_apply_context_t *c) const
652 case 1: return u.format1.apply (c);
653 default:return false;
657 inline bool sanitize (hb_sanitize_context_t *c) {
659 if (!u.format.sanitize (c)) return false;
661 case 1: return u.format1.sanitize (c);
668 USHORT format; /* Format identifier */
669 ReverseChainSingleSubstFormat1 format1;
679 struct SubstLookupSubTable
681 friend struct SubstLookup;
691 ReverseChainSingle = 8
694 inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
697 switch (lookup_type) {
698 case Single: return u.single.apply (c);
699 case Multiple: return u.multiple.apply (c);
700 case Alternate: return u.alternate.apply (c);
701 case Ligature: return u.ligature.apply (c);
702 case Context: return u.c.apply (c);
703 case ChainContext: return u.chainContext.apply (c);
704 case Extension: return u.extension.apply (c);
705 case ReverseChainSingle: return u.reverseChainContextSingle.apply (c);
706 default:return false;
710 inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
712 switch (lookup_type) {
713 case Single: return u.single.sanitize (c);
714 case Multiple: return u.multiple.sanitize (c);
715 case Alternate: return u.alternate.sanitize (c);
716 case Ligature: return u.ligature.sanitize (c);
717 case Context: return u.c.sanitize (c);
718 case ChainContext: return u.chainContext.sanitize (c);
719 case Extension: return u.extension.sanitize (c);
720 case ReverseChainSingle: return u.reverseChainContextSingle.sanitize (c);
729 MultipleSubst multiple;
730 AlternateSubst alternate;
731 LigatureSubst ligature;
733 ChainContextSubst chainContext;
734 ExtensionSubst extension;
735 ReverseChainSingleSubst reverseChainContextSingle;
738 DEFINE_SIZE_UNION (2, sub_format);
742 struct SubstLookup : Lookup
744 inline const SubstLookupSubTable& get_subtable (unsigned int i) const
745 { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
747 inline static bool lookup_type_is_reverse (unsigned int lookup_type)
748 { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
750 inline bool is_reverse (void) const
752 unsigned int type = get_type ();
753 if (unlikely (type == SubstLookupSubTable::Extension))
754 return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
755 return lookup_type_is_reverse (type);
759 inline bool apply_once (hb_face_t *face,
761 hb_mask_t lookup_mask,
762 unsigned int context_length,
763 unsigned int nesting_level_left) const
765 unsigned int lookup_type = get_type ();
766 hb_apply_context_t c[1] = {{0}};
770 c->direction = buffer->props.direction;
771 c->lookup_mask = lookup_mask;
772 c->context_length = context_length;
773 c->nesting_level_left = nesting_level_left;
774 c->lookup_props = get_props ();
776 if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, &c->property))
779 if (unlikely (lookup_type == SubstLookupSubTable::Extension))
781 /* The spec says all subtables should have the same type.
782 * This is specially important if one has a reverse type!
784 * This is rather slow to do this here for every glyph,
785 * but it's easiest, and who uses extension lookups anyway?!*/
786 unsigned int count = get_subtable_count ();
787 unsigned int type = get_subtable(0).u.extension.get_type ();
788 for (unsigned int i = 1; i < count; i++)
789 if (get_subtable(i).u.extension.get_type () != type)
793 unsigned int count = get_subtable_count ();
794 for (unsigned int i = 0; i < count; i++)
795 if (get_subtable (i).apply (c, lookup_type))
801 inline bool apply_string (hb_face_t *face,
803 hb_mask_t mask) const
807 if (unlikely (!buffer->len))
810 if (likely (!is_reverse ()))
812 /* in/out forward substitution */
813 buffer->clear_output ();
815 while (buffer->idx < buffer->len)
817 if ((buffer->info[buffer->idx].mask & mask) &&
818 apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
821 buffer->next_glyph ();
825 buffer->swap_buffers ();
829 /* in-place backward substitution */
830 buffer->idx = buffer->len - 1;
833 if ((buffer->info[buffer->idx].mask & mask) &&
834 apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
840 while ((int) buffer->idx >= 0);
846 inline bool sanitize (hb_sanitize_context_t *c) {
848 if (unlikely (!Lookup::sanitize (c))) return false;
849 OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
850 return list.sanitize (c, this, get_type ());
854 typedef OffsetListOf<SubstLookup> SubstLookupList;
860 struct GSUB : GSUBGPOS
862 static const hb_tag_t Tag = HB_OT_TAG_GSUB;
864 inline const SubstLookup& get_lookup (unsigned int i) const
865 { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
867 inline bool substitute_lookup (hb_face_t *face,
869 unsigned int lookup_index,
870 hb_mask_t mask) const
871 { return get_lookup (lookup_index).apply_string (face, buffer, mask); }
873 static inline void substitute_start (hb_buffer_t *buffer);
874 static inline void substitute_finish (hb_buffer_t *buffer);
876 inline bool sanitize (hb_sanitize_context_t *c) {
878 if (unlikely (!GSUBGPOS::sanitize (c))) return false;
879 OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
880 return list.sanitize (c, this);
883 DEFINE_SIZE_STATIC (10);
888 GSUB::substitute_start (hb_buffer_t *buffer)
890 HB_BUFFER_ALLOCATE_VAR (buffer, props_cache);
891 HB_BUFFER_ALLOCATE_VAR (buffer, lig_id);
892 HB_BUFFER_ALLOCATE_VAR (buffer, lig_comp);
894 unsigned int count = buffer->len;
895 for (unsigned int i = 0; i < count; i++)
896 buffer->info[i].props_cache() = buffer->info[i].lig_id() = buffer->info[i].lig_comp() = 0;
900 GSUB::substitute_finish (hb_buffer_t *buffer)
905 /* Out-of-class implementation for methods recursing */
907 inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
910 return get_subtable ().apply (c, get_type ());
913 inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
916 if (unlikely (!Extension::sanitize (c))) return false;
917 unsigned int offset = get_offset ();
918 if (unlikely (!offset)) return true;
919 return StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ());
922 inline bool ExtensionSubst::is_reverse (void) const
924 unsigned int type = get_type ();
925 if (unlikely (type == SubstLookupSubTable::Extension))
926 return CastR<ExtensionSubst> (get_subtable()).is_reverse ();
927 return SubstLookup::lookup_type_is_reverse (type);
930 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
932 const GSUB &gsub = *(c->face->ot_layout->gsub);
933 const SubstLookup &l = gsub.get_lookup (lookup_index);
935 if (unlikely (c->nesting_level_left == 0))
938 if (unlikely (c->context_length < 1))
941 return l.apply_once (c->face, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1);
947 #endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */