1 | #ifndef OT_LAYOUT_GSUB_SEQUENCE_HH |
2 | #define OT_LAYOUT_GSUB_SEQUENCE_HH |
3 | |
4 | #include "Common.hh" |
5 | |
6 | namespace OT { |
7 | namespace Layout { |
8 | namespace GSUB_impl { |
9 | |
10 | template <typename Types> |
11 | struct Sequence |
12 | { |
13 | protected: |
14 | Array16Of<typename Types::HBGlyphID> |
15 | substitute; /* String of GlyphIDs to substitute */ |
16 | public: |
17 | DEFINE_SIZE_ARRAY (2, substitute); |
18 | |
19 | bool sanitize (hb_sanitize_context_t *c) const |
20 | { |
21 | TRACE_SANITIZE (this); |
22 | return_trace (substitute.sanitize (c)); |
23 | } |
24 | |
25 | bool intersects (const hb_set_t *glyphs) const |
26 | { return hb_all (substitute, glyphs); } |
27 | |
28 | void closure (hb_closure_context_t *c) const |
29 | { c->output->add_array (substitute.arrayZ, substitute.len); } |
30 | |
31 | void collect_glyphs (hb_collect_glyphs_context_t *c) const |
32 | { c->output->add_array (substitute.arrayZ, substitute.len); } |
33 | |
34 | bool apply (hb_ot_apply_context_t *c) const |
35 | { |
36 | TRACE_APPLY (this); |
37 | unsigned int count = substitute.len; |
38 | |
39 | /* Special-case to make it in-place and not consider this |
40 | * as a "multiplied" substitution. */ |
41 | if (unlikely (count == 1)) |
42 | { |
43 | if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) |
44 | { |
45 | c->buffer->sync_so_far (); |
46 | c->buffer->message (c->font, |
47 | "replacing glyph at %u (multiple substitution)" , |
48 | c->buffer->idx); |
49 | } |
50 | |
51 | c->replace_glyph (substitute.arrayZ[0]); |
52 | |
53 | if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) |
54 | { |
55 | c->buffer->message (c->font, |
56 | "replaced glyph at %u (multiple substitution)" , |
57 | c->buffer->idx - 1u); |
58 | } |
59 | |
60 | return_trace (true); |
61 | } |
62 | /* Spec disallows this, but Uniscribe allows it. |
63 | * https://github.com/harfbuzz/harfbuzz/issues/253 */ |
64 | else if (unlikely (count == 0)) |
65 | { |
66 | if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) |
67 | { |
68 | c->buffer->sync_so_far (); |
69 | c->buffer->message (c->font, |
70 | "deleting glyph at %u (multiple substitution)" , |
71 | c->buffer->idx); |
72 | } |
73 | |
74 | c->buffer->delete_glyph (); |
75 | |
76 | if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) |
77 | { |
78 | c->buffer->sync_so_far (); |
79 | c->buffer->message (c->font, |
80 | "deleted glyph at %u (multiple substitution)" , |
81 | c->buffer->idx); |
82 | } |
83 | |
84 | return_trace (true); |
85 | } |
86 | |
87 | if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) |
88 | { |
89 | c->buffer->sync_so_far (); |
90 | c->buffer->message (c->font, |
91 | "multiplying glyph at %u" , |
92 | c->buffer->idx); |
93 | } |
94 | |
95 | unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ? |
96 | HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0; |
97 | unsigned lig_id = _hb_glyph_info_get_lig_id (&c->buffer->cur()); |
98 | |
99 | for (unsigned int i = 0; i < count; i++) |
100 | { |
101 | /* If is attached to a ligature, don't disturb that. |
102 | * https://github.com/harfbuzz/harfbuzz/issues/3069 */ |
103 | if (!lig_id) |
104 | _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i); |
105 | c->output_glyph_for_component (substitute.arrayZ[i], klass); |
106 | } |
107 | c->buffer->skip_glyph (); |
108 | |
109 | if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) |
110 | { |
111 | c->buffer->sync_so_far (); |
112 | |
113 | char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0}; |
114 | char *p = buf; |
115 | |
116 | for (unsigned i = c->buffer->idx - count; i < c->buffer->idx; i++) |
117 | { |
118 | if (buf < p) |
119 | *p++ = ','; |
120 | snprintf (p, sizeof(buf) - (p - buf), "%u" , i); |
121 | p += strlen(p); |
122 | } |
123 | |
124 | c->buffer->message (c->font, |
125 | "multiplied glyphs at %s" , |
126 | buf); |
127 | } |
128 | |
129 | return_trace (true); |
130 | } |
131 | |
132 | template <typename Iterator, |
133 | hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))> |
134 | bool serialize (hb_serialize_context_t *c, |
135 | Iterator subst) |
136 | { |
137 | TRACE_SERIALIZE (this); |
138 | return_trace (substitute.serialize (c, subst)); |
139 | } |
140 | |
141 | bool subset (hb_subset_context_t *c) const |
142 | { |
143 | TRACE_SUBSET (this); |
144 | const hb_set_t &glyphset = *c->plan->glyphset_gsub (); |
145 | const hb_map_t &glyph_map = *c->plan->glyph_map; |
146 | |
147 | if (!intersects (&glyphset)) return_trace (false); |
148 | |
149 | auto it = |
150 | + hb_iter (substitute) |
151 | | hb_map (glyph_map) |
152 | ; |
153 | |
154 | auto *out = c->serializer->start_embed (*this); |
155 | return_trace (out->serialize (c->serializer, it)); |
156 | } |
157 | }; |
158 | |
159 | |
160 | } |
161 | } |
162 | } |
163 | |
164 | |
165 | #endif /* OT_LAYOUT_GSUB_SEQUENCE_HH */ |
166 | |