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"
34 #define BUFFER context->buffer
37 struct SingleSubstFormat1
39 friend struct SingleSubst;
43 inline bool apply (hb_apply_context_t *context) const
46 hb_codepoint_t glyph_id = IN_CURGLYPH ();
47 unsigned int index = (this+coverage) (glyph_id);
48 if (likely (index == NOT_COVERED))
51 glyph_id += deltaGlyphID;
52 _hb_buffer_replace_glyph (context->buffer, glyph_id);
54 /* We inherit the old glyph class to the substituted glyph */
55 if (_hb_ot_layout_has_new_glyph_classes (context->layout->face))
56 _hb_ot_layout_set_glyph_property (context->layout->face, glyph_id, context->property);
61 inline bool sanitize (hb_sanitize_context_t *context) {
63 return coverage.sanitize (context, this)
64 && deltaGlyphID.sanitize (context);
68 USHORT format; /* Format identifier--format = 1 */
70 coverage; /* Offset to Coverage table--from
71 * beginning of Substitution table */
72 SHORT deltaGlyphID; /* Add to original GlyphID to get
73 * substitute GlyphID */
75 DEFINE_SIZE_STATIC (6);
78 struct SingleSubstFormat2
80 friend struct SingleSubst;
84 inline bool apply (hb_apply_context_t *context) const
87 hb_codepoint_t glyph_id = IN_CURGLYPH ();
88 unsigned int index = (this+coverage) (glyph_id);
89 if (likely (index == NOT_COVERED))
92 if (unlikely (index >= substitute.len))
95 glyph_id = substitute[index];
96 _hb_buffer_replace_glyph (context->buffer, glyph_id);
98 /* We inherit the old glyph class to the substituted glyph */
99 if (_hb_ot_layout_has_new_glyph_classes (context->layout->face))
100 _hb_ot_layout_set_glyph_property (context->layout->face, glyph_id, context->property);
105 inline bool sanitize (hb_sanitize_context_t *context) {
107 return coverage.sanitize (context, this)
108 && substitute.sanitize (context);
112 USHORT format; /* Format identifier--format = 2 */
114 coverage; /* Offset to Coverage table--from
115 * beginning of Substitution table */
117 substitute; /* Array of substitute
118 * GlyphIDs--ordered by Coverage Index */
120 DEFINE_SIZE_VAR (6, GlyphID);
125 friend struct SubstLookupSubTable;
129 inline bool apply (hb_apply_context_t *context) const
133 case 1: return u.format1->apply (context);
134 case 2: return u.format2->apply (context);
135 default:return false;
139 inline bool sanitize (hb_sanitize_context_t *context) {
141 if (!u.format.sanitize (context)) return false;
143 case 1: return u.format1->sanitize (context);
144 case 2: return u.format2->sanitize (context);
151 USHORT format; /* Format identifier */
152 SingleSubstFormat1 format1[VAR];
153 SingleSubstFormat2 format2[VAR];
160 friend struct MultipleSubstFormat1;
163 inline bool apply (hb_apply_context_t *context) const
166 if (unlikely (!substitute.len))
169 _hb_buffer_add_output_glyphs_be16 (context->buffer, 1,
170 substitute.len, (const uint16_t *) substitute.array(),
173 /* This is a guess only ... */
174 if (_hb_ot_layout_has_new_glyph_classes (context->layout->face))
176 unsigned int property = context->property;
177 if (property == HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)
178 property = HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
180 unsigned int count = substitute.len;
181 for (unsigned int n = 0; n < count; n++)
182 _hb_ot_layout_set_glyph_property (context->layout->face, substitute[n], property);
189 inline bool sanitize (hb_sanitize_context_t *context) {
191 return substitute.sanitize (context);
196 substitute; /* String of GlyphIDs to substitute */
198 DEFINE_SIZE_VAR (2, GlyphID);
201 struct MultipleSubstFormat1
203 friend struct MultipleSubst;
207 inline bool apply (hb_apply_context_t *context) const
211 unsigned int index = (this+coverage) (IN_CURGLYPH ());
212 if (likely (index == NOT_COVERED))
215 return (this+sequence[index]).apply (context);
218 inline bool sanitize (hb_sanitize_context_t *context) {
220 return coverage.sanitize (context, this)
221 && sequence.sanitize (context, this);
225 USHORT format; /* Format identifier--format = 1 */
227 coverage; /* Offset to Coverage table--from
228 * beginning of Substitution table */
229 OffsetArrayOf<Sequence>
230 sequence; /* Array of Sequence tables
231 * ordered by Coverage Index */
233 DEFINE_SIZE_VAR (6, OffsetTo<Sequence>);
238 friend struct SubstLookupSubTable;
242 inline bool apply (hb_apply_context_t *context) const
246 case 1: return u.format1->apply (context);
247 default:return false;
251 inline bool sanitize (hb_sanitize_context_t *context) {
253 if (!u.format.sanitize (context)) return false;
255 case 1: return u.format1->sanitize (context);
262 USHORT format; /* Format identifier */
263 MultipleSubstFormat1 format1[VAR];
268 typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in
271 struct AlternateSubstFormat1
273 friend struct AlternateSubst;
277 inline bool apply (hb_apply_context_t *context) const
280 hb_codepoint_t glyph_id = IN_CURGLYPH ();
282 unsigned int index = (this+coverage) (glyph_id);
283 if (likely (index == NOT_COVERED))
286 const AlternateSet &alt_set = this+alternateSet[index];
288 if (unlikely (!alt_set.len))
291 unsigned int alt_index = 0;
293 /* XXX callback to user to choose alternate
294 if (context->layout->face->altfunc)
295 alt_index = (context->layout->face->altfunc)(context->layout->layout, context->buffer,
296 context->buffer->out_pos, glyph_id,
297 alt_set.len, alt_set.array);
300 if (unlikely (alt_index >= alt_set.len))
303 glyph_id = alt_set[alt_index];
305 _hb_buffer_replace_glyph (context->buffer, glyph_id);
307 /* We inherit the old glyph class to the substituted glyph */
308 if (_hb_ot_layout_has_new_glyph_classes (context->layout->face))
309 _hb_ot_layout_set_glyph_property (context->layout->face, glyph_id, context->property);
314 inline bool sanitize (hb_sanitize_context_t *context) {
316 return coverage.sanitize (context, this)
317 && alternateSet.sanitize (context, this);
321 USHORT format; /* Format identifier--format = 1 */
323 coverage; /* Offset to Coverage table--from
324 * beginning of Substitution table */
325 OffsetArrayOf<AlternateSet>
326 alternateSet; /* Array of AlternateSet tables
327 * ordered by Coverage Index */
329 DEFINE_SIZE_VAR (6, OffsetTo<AlternateSet>);
332 struct AlternateSubst
334 friend struct SubstLookupSubTable;
338 inline bool apply (hb_apply_context_t *context) const
342 case 1: return u.format1->apply (context);
343 default:return false;
347 inline bool sanitize (hb_sanitize_context_t *context) {
349 if (!u.format.sanitize (context)) return false;
351 case 1: return u.format1->sanitize (context);
358 USHORT format; /* Format identifier */
359 AlternateSubstFormat1 format1[VAR];
366 friend struct LigatureSet;
369 inline bool apply (hb_apply_context_t *context, bool is_mark) const
373 unsigned int count = component.len;
374 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
375 if (unlikely (context->buffer->in_pos + count > end))
378 for (i = 1, j = context->buffer->in_pos + 1; i < count; i++, j++)
380 unsigned int property;
381 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, &property))
383 if (unlikely (j + count - i == end))
388 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
391 if (likely (IN_GLYPH (j) != component[i]))
394 /* This is just a guess ... */
395 if (_hb_ot_layout_has_new_glyph_classes (context->layout->face))
396 _hb_ot_layout_set_glyph_class (context->layout->face, ligGlyph,
397 is_mark ? HB_OT_LAYOUT_GLYPH_CLASS_MARK
398 : HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);
400 if (j == context->buffer->in_pos + i) /* No input glyphs skipped */
401 /* We don't use a new ligature ID if there are no skipped
402 glyphs and the ligature already has an ID. */
403 _hb_buffer_add_output_glyphs_be16 (context->buffer, i,
404 1, (const uint16_t *) &ligGlyph,
406 IN_LIGID (context->buffer->in_pos) && !IN_COMPONENT (context->buffer->in_pos) ?
407 0xFFFF : _hb_buffer_allocate_lig_id (context->buffer));
410 unsigned int lig_id = _hb_buffer_allocate_lig_id (context->buffer);
411 _hb_buffer_add_output_glyph (context->buffer, ligGlyph, 0xFFFF, lig_id);
413 /* Now we must do a second loop to copy the skipped glyphs to
414 `out' and assign component values to it. We start with the
415 glyph after the first component. Glyphs between component
416 i and i+1 belong to component i. Together with the lig_id
417 value it is later possible to check whether a specific
418 component value really belongs to a given ligature. */
420 for ( i = 1; i < count; i++ )
422 while (_hb_ot_layout_skip_mark (context->layout->face, IN_CURINFO (), context->lookup_flag, NULL))
423 _hb_buffer_add_output_glyph (context->buffer, IN_CURGLYPH (), i, lig_id);
425 (context->buffer->in_pos)++;
433 inline bool sanitize (hb_sanitize_context_t *context) {
435 return ligGlyph.sanitize (context)
436 && component.sanitize (context);
440 GlyphID ligGlyph; /* GlyphID of ligature to substitute */
441 HeadlessArrayOf<GlyphID>
442 component; /* Array of component GlyphIDs--start
443 * with the second component--ordered
444 * in writing direction */
446 DEFINE_SIZE_VAR (4, GlyphID);
451 friend struct LigatureSubstFormat1;
454 inline bool apply (hb_apply_context_t *context, bool is_mark) const
457 unsigned int num_ligs = ligature.len;
458 for (unsigned int i = 0; i < num_ligs; i++)
460 const Ligature &lig = this+ligature[i];
461 if (lig.apply (context, is_mark))
469 inline bool sanitize (hb_sanitize_context_t *context) {
471 return ligature.sanitize (context, this);
475 OffsetArrayOf<Ligature>
476 ligature; /* Array LigatureSet tables
477 * ordered by preference */
479 DEFINE_SIZE_VAR (2, OffsetTo<Ligature>);
482 struct LigatureSubstFormat1
484 friend struct LigatureSubst;
487 inline bool apply (hb_apply_context_t *context) const
490 hb_codepoint_t glyph_id = IN_CURGLYPH ();
492 bool first_is_mark = !!(context->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
494 unsigned int index = (this+coverage) (glyph_id);
495 if (likely (index == NOT_COVERED))
498 const LigatureSet &lig_set = this+ligatureSet[index];
499 return lig_set.apply (context, first_is_mark);
502 inline bool sanitize (hb_sanitize_context_t *context) {
504 return coverage.sanitize (context, this)
505 && ligatureSet.sanitize (context, this);
509 USHORT format; /* Format identifier--format = 1 */
511 coverage; /* Offset to Coverage table--from
512 * beginning of Substitution table */
513 OffsetArrayOf<LigatureSet>
514 ligatureSet; /* Array LigatureSet tables
515 * ordered by Coverage Index */
517 DEFINE_SIZE_VAR (6, OffsetTo<LigatureSet>);
522 friend struct SubstLookupSubTable;
525 inline bool apply (hb_apply_context_t *context) const
529 case 1: return u.format1->apply (context);
530 default:return false;
534 inline bool sanitize (hb_sanitize_context_t *context) {
536 if (!u.format.sanitize (context)) return false;
538 case 1: return u.format1->sanitize (context);
545 USHORT format; /* Format identifier */
546 LigatureSubstFormat1 format1[VAR];
552 static inline bool substitute_lookup (hb_apply_context_t *context, unsigned int lookup_index);
554 struct ContextSubst : Context
556 friend struct SubstLookupSubTable;
559 inline bool apply (hb_apply_context_t *context) const
562 return Context::apply (context, substitute_lookup);
566 struct ChainContextSubst : ChainContext
568 friend struct SubstLookupSubTable;
571 inline bool apply (hb_apply_context_t *context) const
574 return ChainContext::apply (context, substitute_lookup);
579 struct ExtensionSubst : Extension
581 friend struct SubstLookupSubTable;
582 friend struct SubstLookup;
585 inline const struct SubstLookupSubTable& get_subtable (void) const
587 unsigned int offset = get_offset ();
588 if (unlikely (!offset)) return Null(SubstLookupSubTable);
589 return StructAtOffset<SubstLookupSubTable> (this, offset);
592 inline bool apply (hb_apply_context_t *context) const;
594 inline bool sanitize (hb_sanitize_context_t *context);
596 inline bool is_reverse (void) const;
600 struct ReverseChainSingleSubstFormat1
602 friend struct ReverseChainSingleSubst;
605 inline bool apply (hb_apply_context_t *context) const
608 if (unlikely (context->context_length != NO_CONTEXT))
609 return false; /* No chaining to this type */
611 unsigned int index = (this+coverage) (IN_CURGLYPH ());
612 if (likely (index == NOT_COVERED))
615 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
616 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
618 if (match_backtrack (context,
619 backtrack.len, (USHORT *) backtrack.array(),
620 match_coverage, this) &&
621 match_lookahead (context,
622 lookahead.len, (USHORT *) lookahead.array(),
623 match_coverage, this,
626 IN_CURGLYPH () = substitute[index];
627 context->buffer->in_pos--; /* Reverse! */
634 inline bool sanitize (hb_sanitize_context_t *context) {
636 if (!(coverage.sanitize (context, this)
637 && backtrack.sanitize (context, this)))
639 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
640 if (!lookahead.sanitize (context, this))
642 ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
643 return substitute.sanitize (context);
647 USHORT format; /* Format identifier--format = 1 */
649 coverage; /* Offset to Coverage table--from
650 * beginning of table */
651 OffsetArrayOf<Coverage>
652 backtrack; /* Array of coverage tables
653 * in backtracking sequence, in glyph
655 OffsetArrayOf<Coverage>
656 lookaheadX; /* Array of coverage tables
657 * in lookahead sequence, in glyph
660 substituteX; /* Array of substitute
661 * GlyphIDs--ordered by Coverage Index */
663 DEFINE_SIZE_MIN (10);
666 struct ReverseChainSingleSubst
668 friend struct SubstLookupSubTable;
671 inline bool apply (hb_apply_context_t *context) const
675 case 1: return u.format1->apply (context);
676 default:return false;
680 inline bool sanitize (hb_sanitize_context_t *context) {
682 if (!u.format.sanitize (context)) return false;
684 case 1: return u.format1->sanitize (context);
691 USHORT format; /* Format identifier */
692 ReverseChainSingleSubstFormat1 format1[VAR];
702 struct SubstLookupSubTable
704 friend struct SubstLookup;
714 ReverseChainSingle = 8
717 inline bool apply (hb_apply_context_t *context, unsigned int lookup_type) const
720 switch (lookup_type) {
721 case Single: return u.single->apply (context);
722 case Multiple: return u.multiple->apply (context);
723 case Alternate: return u.alternate->apply (context);
724 case Ligature: return u.ligature->apply (context);
725 case Context: return u.context->apply (context);
726 case ChainContext: return u.chainContext->apply (context);
727 case Extension: return u.extension->apply (context);
728 case ReverseChainSingle: return u.reverseChainContextSingle->apply (context);
729 default:return false;
733 inline bool sanitize (hb_sanitize_context_t *context) {
735 if (!u.format.sanitize (context)) return false;
737 case Single: return u.single->sanitize (context);
738 case Multiple: return u.multiple->sanitize (context);
739 case Alternate: return u.alternate->sanitize (context);
740 case Ligature: return u.ligature->sanitize (context);
741 case Context: return u.context->sanitize (context);
742 case ChainContext: return u.chainContext->sanitize (context);
743 case Extension: return u.extension->sanitize (context);
744 case ReverseChainSingle: return u.reverseChainContextSingle->sanitize (context);
752 SingleSubst single[VAR];
753 MultipleSubst multiple[VAR];
754 AlternateSubst alternate[VAR];
755 LigatureSubst ligature[VAR];
756 ContextSubst context[VAR];
757 ChainContextSubst chainContext[VAR];
758 ExtensionSubst extension[VAR];
759 ReverseChainSingleSubst reverseChainContextSingle[VAR];
766 struct SubstLookup : Lookup
768 inline const SubstLookupSubTable& get_subtable (unsigned int i) const
769 { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
771 inline static bool lookup_type_is_reverse (unsigned int lookup_type)
772 { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
774 inline bool is_reverse (void) const
776 unsigned int type = get_type ();
777 if (unlikely (type == SubstLookupSubTable::Extension))
778 return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
779 return lookup_type_is_reverse (type);
783 inline bool apply_once (hb_ot_layout_context_t *layout,
785 unsigned int context_length,
786 unsigned int nesting_level_left) const
788 unsigned int lookup_type = get_type ();
789 hb_apply_context_t context[1] = {{0}};
791 context->layout = layout;
792 context->buffer = buffer;
793 context->context_length = context_length;
794 context->nesting_level_left = nesting_level_left;
795 context->lookup_flag = get_flag ();
797 if (!_hb_ot_layout_check_glyph_property (context->layout->face, IN_CURINFO (), context->lookup_flag, &context->property))
800 if (unlikely (lookup_type == SubstLookupSubTable::Extension))
802 /* The spec says all subtables should have the same type.
803 * This is specially important if one has a reverse type!
805 * This is rather slow to do this here for every glyph,
806 * but it's easiest, and who uses extension lookups anyway?!*/
807 unsigned int count = get_subtable_count ();
808 unsigned int type = get_subtable(0).u.extension->get_type ();
809 for (unsigned int i = 1; i < count; i++)
810 if (get_subtable(i).u.extension->get_type () != type)
814 unsigned int count = get_subtable_count ();
815 for (unsigned int i = 0; i < count; i++)
816 if (get_subtable (i).apply (context, lookup_type))
822 inline bool apply_string (hb_ot_layout_context_t *layout,
824 hb_mask_t mask) const
827 #define BUFFER buffer
830 if (unlikely (!buffer->in_length))
833 if (likely (!is_reverse ()))
835 /* in/out forward substitution */
836 _hb_buffer_clear_output (buffer);
838 while (buffer->in_pos < buffer->in_length)
840 if ((~IN_MASK (buffer->in_pos) & mask) &&
841 apply_once (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL))
844 _hb_buffer_next_glyph (buffer);
848 _hb_buffer_swap (buffer);
852 /* in-place backward substitution */
853 buffer->in_pos = buffer->in_length - 1;
856 if ((~IN_MASK (buffer->in_pos) & mask) &&
857 apply_once (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL))
863 while ((int) buffer->in_pos >= 0);
869 inline bool sanitize (hb_sanitize_context_t *context) {
871 if (unlikely (!Lookup::sanitize (context))) return false;
872 OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
873 return list.sanitize (context, this);
877 typedef OffsetListOf<SubstLookup> SubstLookupList;
883 struct GSUB : GSUBGPOS
885 static const hb_tag_t Tag = HB_OT_TAG_GSUB;
887 inline const SubstLookup& get_lookup (unsigned int i) const
888 { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
890 inline bool substitute_lookup (hb_ot_layout_context_t *layout,
892 unsigned int lookup_index,
893 hb_mask_t mask) const
894 { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
896 inline bool sanitize (hb_sanitize_context_t *context) {
898 if (unlikely (!GSUBGPOS::sanitize (context))) return false;
899 OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
900 return list.sanitize (context, this);
903 DEFINE_SIZE_STATIC (10);
907 /* Out-of-class implementation for methods recursing */
909 inline bool ExtensionSubst::apply (hb_apply_context_t *context) const
912 return get_subtable ().apply (context, get_type ());
915 inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *context)
918 if (unlikely (!Extension::sanitize (context))) return false;
919 unsigned int offset = get_offset ();
920 if (unlikely (!offset)) return true;
921 return StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (context);
924 inline bool ExtensionSubst::is_reverse (void) const
926 unsigned int type = get_type ();
927 if (unlikely (type == SubstLookupSubTable::Extension))
928 return CastR<ExtensionSubst> (get_subtable()).is_reverse ();
929 return SubstLookup::lookup_type_is_reverse (type);
932 static inline bool substitute_lookup (hb_apply_context_t *context, unsigned int lookup_index)
934 const GSUB &gsub = *(context->layout->face->ot_layout.gsub);
935 const SubstLookup &l = gsub.get_lookup (lookup_index);
937 if (unlikely (context->nesting_level_left == 0))
940 if (unlikely (context->context_length < 1))
943 return l.apply_once (context->layout, context->buffer, context->context_length, context->nesting_level_left - 1);
947 #endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */