summaryrefslogtreecommitdiffstats
path: root/meta/recipes-graphics/harfbuzz/harfbuzz
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-graphics/harfbuzz/harfbuzz')
-rw-r--r--meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193-pre0.patch335
-rw-r--r--meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193-pre1.patch135
-rw-r--r--meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193.patch179
3 files changed, 649 insertions, 0 deletions
diff --git a/meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193-pre0.patch b/meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193-pre0.patch
new file mode 100644
index 0000000000..90d4cfefb4
--- /dev/null
+++ b/meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193-pre0.patch
@@ -0,0 +1,335 @@
1From 3122c2cdc45a964efedad8953a2df67205c3e3a8 Mon Sep 17 00:00:00 2001
2From: Behdad Esfahbod <behdad@behdad.org>
3Date: Sat, 4 Dec 2021 19:50:33 -0800
4Subject: [PATCH] [buffer] Add HB_GLYPH_FLAG_UNSAFE_TO_CONCAT
5
6Fixes https://github.com/harfbuzz/harfbuzz/issues/1463
7Upstream-Status: Backport from [https://github.com/harfbuzz/harfbuzz/commit/3122c2cdc45a964efedad8953a2df67205c3e3a8]
8Comment1: To backport the fix for CVE-2023-25193, add defination for HB_GLYPH_FLAG_UNSAFE_TO_CONCAT. This patch is needed along with CVE-2023-25193-pre1.patch for sucessfull porting.
9Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
10---
11 src/hb-buffer.cc | 10 ++---
12 src/hb-buffer.h | 76 ++++++++++++++++++++++++++++++------
13 src/hb-buffer.hh | 33 ++++++++++------
14 src/hb-ot-layout-gsubgpos.hh | 39 +++++++++++++++---
15 src/hb-ot-shape.cc | 8 +---
16 5 files changed, 124 insertions(+), 42 deletions(-)
17
18diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
19index 6131c86..bba5eae 100644
20--- a/src/hb-buffer.cc
21+++ b/src/hb-buffer.cc
22@@ -610,14 +610,14 @@ done:
23 }
24
25 void
26-hb_buffer_t::unsafe_to_break_impl (unsigned int start, unsigned int end)
27+hb_buffer_t::unsafe_to_break_impl (unsigned int start, unsigned int end, hb_mask_t mask)
28 {
29 unsigned int cluster = (unsigned int) -1;
30 cluster = _unsafe_to_break_find_min_cluster (info, start, end, cluster);
31- _unsafe_to_break_set_mask (info, start, end, cluster);
32+ _unsafe_to_break_set_mask (info, start, end, cluster, mask);
33 }
34 void
35-hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end)
36+hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end, hb_mask_t mask)
37 {
38 if (!have_output)
39 {
40@@ -631,8 +631,8 @@ hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int en
41 unsigned int cluster = (unsigned int) -1;
42 cluster = _unsafe_to_break_find_min_cluster (out_info, start, out_len, cluster);
43 cluster = _unsafe_to_break_find_min_cluster (info, idx, end, cluster);
44- _unsafe_to_break_set_mask (out_info, start, out_len, cluster);
45- _unsafe_to_break_set_mask (info, idx, end, cluster);
46+ _unsafe_to_break_set_mask (out_info, start, out_len, cluster, mask);
47+ _unsafe_to_break_set_mask (info, idx, end, cluster, mask);
48 }
49
50 void
51diff --git a/src/hb-buffer.h b/src/hb-buffer.h
52index d5cb746..42dc92a 100644
53--- a/src/hb-buffer.h
54+++ b/src/hb-buffer.h
55@@ -77,26 +77,76 @@ typedef struct hb_glyph_info_t
56 * @HB_GLYPH_FLAG_UNSAFE_TO_BREAK: Indicates that if input text is broken at the
57 * beginning of the cluster this glyph is part of,
58 * then both sides need to be re-shaped, as the
59- * result might be different. On the flip side,
60- * it means that when this flag is not present,
61- * then it's safe to break the glyph-run at the
62- * beginning of this cluster, and the two sides
63- * represent the exact same result one would get
64- * if breaking input text at the beginning of
65- * this cluster and shaping the two sides
66- * separately. This can be used to optimize
67- * paragraph layout, by avoiding re-shaping
68- * of each line after line-breaking, or limiting
69- * the reshaping to a small piece around the
70- * breaking point only.
71+ * result might be different.
72+ *
73+ * On the flip side, it means that when this
74+ * flag is not present, then it is safe to break
75+ * the glyph-run at the beginning of this
76+ * cluster, and the two sides will represent the
77+ * exact same result one would get if breaking
78+ * input text at the beginning of this cluster
79+ * and shaping the two sides separately.
80+ *
81+ * This can be used to optimize paragraph
82+ * layout, by avoiding re-shaping of each line
83+ * after line-breaking.
84+ *
85+ * @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT: Indicates that if input text is changed on one
86+ * side of the beginning of the cluster this glyph
87+ * is part of, then the shaping results for the
88+ * other side might change.
89+ *
90+ * Note that the absence of this flag will NOT by
91+ * itself mean that it IS safe to concat text.
92+ * Only two pieces of text both of which clear of
93+ * this flag can be concatenated safely.
94+ *
95+ * This can be used to optimize paragraph
96+ * layout, by avoiding re-shaping of each line
97+ * after line-breaking, by limiting the
98+ * reshaping to a small piece around the
99+ * breaking positin only, even if the breaking
100+ * position carries the
101+ * #HB_GLYPH_FLAG_UNSAFE_TO_BREAK or when
102+ * hyphenation or other text transformation
103+ * happens at line-break position, in the following
104+ * way:
105+ *
106+ * 1. Iterate back from the line-break position till
107+ * the the first cluster start position that is
108+ * NOT unsafe-to-concat, 2. shape the segment from
109+ * there till the end of line, 3. check whether the
110+ * resulting glyph-run also is clear of the
111+ * unsafe-to-concat at its start-of-text position;
112+ * if it is, just splice it into place and the line
113+ * is shaped; If not, move on to a position further
114+ * back that is clear of unsafe-to-concat and retry
115+ * from there, and repeat.
116+ *
117+ * At the start of next line a similar algorithm can
118+ * be implemented. A slight complication will arise,
119+ * because while our buffer API has a way to
120+ * return flags for position corresponding to
121+ * start-of-text, there is currently no position
122+ * corresponding to end-of-text. This limitation
123+ * can be alleviated by shaping more text than needed
124+ * and looking for unsafe-to-concat flag within text
125+ * clusters.
126+ *
127+ * The #HB_GLYPH_FLAG_UNSAFE_TO_BREAK flag will
128+ * always imply this flag.
129+ *
130+ * Since: REPLACEME
131+ *
132 * @HB_GLYPH_FLAG_DEFINED: All the currently defined flags.
133 *
134 * Since: 1.5.0
135 */
136 typedef enum { /*< flags >*/
137 HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001,
138+ HB_GLYPH_FLAG_UNSAFE_TO_CONCAT = 0x00000002,
139
140- HB_GLYPH_FLAG_DEFINED = 0x00000001 /* OR of all defined flags */
141+ HB_GLYPH_FLAG_DEFINED = 0x00000003 /* OR of all defined flags */
142 } hb_glyph_flags_t;
143
144 HB_EXTERN hb_glyph_flags_t
145diff --git a/src/hb-buffer.hh b/src/hb-buffer.hh
146index b5596d9..beac7b6 100644
147--- a/src/hb-buffer.hh
148+++ b/src/hb-buffer.hh
149@@ -67,8 +67,8 @@ enum hb_buffer_scratch_flags_t {
150 HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES = 0x00000002u,
151 HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK = 0x00000004u,
152 HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT = 0x00000008u,
153- HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK = 0x00000010u,
154- HB_BUFFER_SCRATCH_FLAG_HAS_CGJ = 0x00000020u,
155+ HB_BUFFER_SCRATCH_FLAG_HAS_CGJ = 0x00000010u,
156+ HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS = 0x00000020u,
157
158 /* Reserved for complex shapers' internal use. */
159 HB_BUFFER_SCRATCH_FLAG_COMPLEX0 = 0x01000000u,
160@@ -324,8 +324,19 @@ struct hb_buffer_t
161 return;
162 unsafe_to_break_impl (start, end);
163 }
164- HB_INTERNAL void unsafe_to_break_impl (unsigned int start, unsigned int end);
165- HB_INTERNAL void unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end);
166+ void unsafe_to_concat (unsigned int start,
167+ unsigned int end)
168+ {
169+ if (end - start < 2)
170+ return;
171+ unsafe_to_break_impl (start, end, HB_GLYPH_FLAG_UNSAFE_TO_CONCAT);
172+ }
173+ HB_INTERNAL void unsafe_to_break_impl (unsigned int start, unsigned int end,
174+ hb_mask_t mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT);
175+ HB_INTERNAL void unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end,
176+ hb_mask_t mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT);
177+ void unsafe_to_concat_from_outbuffer (unsigned int start, unsigned int end)
178+ { unsafe_to_break_from_outbuffer (start, end, HB_GLYPH_FLAG_UNSAFE_TO_CONCAT); }
179
180
181 /* Internal methods */
182@@ -377,12 +388,7 @@ struct hb_buffer_t
183 set_cluster (hb_glyph_info_t &inf, unsigned int cluster, unsigned int mask = 0)
184 {
185 if (inf.cluster != cluster)
186- {
187- if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
188- inf.mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
189- else
190- inf.mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
191- }
192+ inf.mask = (inf.mask & ~HB_GLYPH_FLAG_DEFINED) | (mask & HB_GLYPH_FLAG_DEFINED);
193 inf.cluster = cluster;
194 }
195
196@@ -398,13 +404,14 @@ struct hb_buffer_t
197 void
198 _unsafe_to_break_set_mask (hb_glyph_info_t *infos,
199 unsigned int start, unsigned int end,
200- unsigned int cluster)
201+ unsigned int cluster,
202+ hb_mask_t mask)
203 {
204 for (unsigned int i = start; i < end; i++)
205 if (cluster != infos[i].cluster)
206 {
207- scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK;
208- infos[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
209+ scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS;
210+ infos[i].mask |= mask;
211 }
212 }
213
214diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh
215index 579d178..a6ca456 100644
216--- a/src/hb-ot-layout-gsubgpos.hh
217+++ b/src/hb-ot-layout-gsubgpos.hh
218@@ -369,7 +369,7 @@ struct hb_ot_apply_context_t :
219 may_skip (const hb_glyph_info_t &info) const
220 { return matcher.may_skip (c, info); }
221
222- bool next ()
223+ bool next (unsigned *unsafe_to = nullptr)
224 {
225 assert (num_items > 0);
226 while (idx + num_items < end)
227@@ -392,11 +392,17 @@ struct hb_ot_apply_context_t :
228 }
229
230 if (skip == matcher_t::SKIP_NO)
231+ {
232+ if (unsafe_to)
233+ *unsafe_to = idx + 1;
234 return false;
235+ }
236 }
237+ if (unsafe_to)
238+ *unsafe_to = end;
239 return false;
240 }
241- bool prev ()
242+ bool prev (unsigned *unsafe_from = nullptr)
243 {
244 assert (num_items > 0);
245 while (idx > num_items - 1)
246@@ -419,8 +425,14 @@ struct hb_ot_apply_context_t :
247 }
248
249 if (skip == matcher_t::SKIP_NO)
250+ {
251+ if (unsafe_from)
252+ *unsafe_from = hb_max (1u, idx) - 1u;
253 return false;
254+ }
255 }
256+ if (unsafe_from)
257+ *unsafe_from = 0;
258 return false;
259 }
260
261@@ -834,7 +846,12 @@ static inline bool match_input (hb_ot_apply_context_t *c,
262 match_positions[0] = buffer->idx;
263 for (unsigned int i = 1; i < count; i++)
264 {
265- if (!skippy_iter.next ()) return_trace (false);
266+ unsigned unsafe_to;
267+ if (!skippy_iter.next (&unsafe_to))
268+ {
269+ c->buffer->unsafe_to_concat (c->buffer->idx, unsafe_to);
270+ return_trace (false);
271+ }
272
273 match_positions[i] = skippy_iter.idx;
274
275@@ -1022,8 +1039,14 @@ static inline bool match_backtrack (hb_ot_apply_context_t *c,
276 skippy_iter.set_match_func (match_func, match_data, backtrack);
277
278 for (unsigned int i = 0; i < count; i++)
279- if (!skippy_iter.prev ())
280+ {
281+ unsigned unsafe_from;
282+ if (!skippy_iter.prev (&unsafe_from))
283+ {
284+ c->buffer->unsafe_to_concat_from_outbuffer (unsafe_from, c->buffer->idx);
285 return_trace (false);
286+ }
287+ }
288
289 *match_start = skippy_iter.idx;
290
291@@ -1045,8 +1068,14 @@ static inline bool match_lookahead (hb_ot_apply_context_t *c,
292 skippy_iter.set_match_func (match_func, match_data, lookahead);
293
294 for (unsigned int i = 0; i < count; i++)
295- if (!skippy_iter.next ())
296+ {
297+ unsigned unsafe_to;
298+ if (!skippy_iter.next (&unsafe_to))
299+ {
300+ c->buffer->unsafe_to_concat (c->buffer->idx + offset, unsafe_to);
301 return_trace (false);
302+ }
303+ }
304
305 *end_index = skippy_iter.idx + 1;
306
307diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
308index 5d9a70c..5d10b30 100644
309--- a/src/hb-ot-shape.cc
310+++ b/src/hb-ot-shape.cc
311@@ -1008,7 +1008,7 @@ hb_propagate_flags (hb_buffer_t *buffer)
312 /* Propagate cluster-level glyph flags to be the same on all cluster glyphs.
313 * Simplifies using them. */
314
315- if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK))
316+ if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS))
317 return;
318
319 hb_glyph_info_t *info = buffer->info;
320@@ -1017,11 +1017,7 @@ hb_propagate_flags (hb_buffer_t *buffer)
321 {
322 unsigned int mask = 0;
323 for (unsigned int i = start; i < end; i++)
324- if (info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
325- {
326- mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
327- break;
328- }
329+ mask |= info[i].mask & HB_GLYPH_FLAG_DEFINED;
330 if (mask)
331 for (unsigned int i = start; i < end; i++)
332 info[i].mask |= mask;
333--
3342.25.1
335
diff --git a/meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193-pre1.patch b/meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193-pre1.patch
new file mode 100644
index 0000000000..4994e0ef68
--- /dev/null
+++ b/meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193-pre1.patch
@@ -0,0 +1,135 @@
1From b29fbd16fa82b82bdf0dcb2f13a63f7dc23cf324 Mon Sep 17 00:00:00 2001
2From: Behdad Esfahbod <behdad@behdad.org>
3Date: Mon, 6 Feb 2023 13:08:52 -0700
4Subject: [PATCH] [gsubgpos] Refactor skippy_iter.match()
5
6Upstream-Status: Backport from [https://github.com/harfbuzz/harfbuzz/commit/b29fbd16fa82b82bdf0dcb2f13a63f7dc23cf324]
7Comment1: To backport the fix for CVE-2023-25193, add defination for MATCH, NOT_MATCH and SKIP.
8Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
9---
10 src/hb-ot-layout-gsubgpos.hh | 94 +++++++++++++++++++++---------------
11 1 file changed, 54 insertions(+), 40 deletions(-)
12
13diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh
14index a6ca456..5a7e564 100644
15--- a/src/hb-ot-layout-gsubgpos.hh
16+++ b/src/hb-ot-layout-gsubgpos.hh
17@@ -369,33 +369,52 @@ struct hb_ot_apply_context_t :
18 may_skip (const hb_glyph_info_t &info) const
19 { return matcher.may_skip (c, info); }
20
21+ enum match_t {
22+ MATCH,
23+ NOT_MATCH,
24+ SKIP
25+ };
26+
27+ match_t match (hb_glyph_info_t &info)
28+ {
29+ matcher_t::may_skip_t skip = matcher.may_skip (c, info);
30+ if (unlikely (skip == matcher_t::SKIP_YES))
31+ return SKIP;
32+
33+ matcher_t::may_match_t match = matcher.may_match (info, match_glyph_data);
34+ if (match == matcher_t::MATCH_YES ||
35+ (match == matcher_t::MATCH_MAYBE &&
36+ skip == matcher_t::SKIP_NO))
37+ return MATCH;
38+
39+ if (skip == matcher_t::SKIP_NO)
40+ return NOT_MATCH;
41+
42+ return SKIP;
43+ }
44+
45 bool next (unsigned *unsafe_to = nullptr)
46 {
47 assert (num_items > 0);
48 while (idx + num_items < end)
49 {
50 idx++;
51- const hb_glyph_info_t &info = c->buffer->info[idx];
52-
53- matcher_t::may_skip_t skip = matcher.may_skip (c, info);
54- if (unlikely (skip == matcher_t::SKIP_YES))
55- continue;
56-
57- matcher_t::may_match_t match = matcher.may_match (info, match_glyph_data);
58- if (match == matcher_t::MATCH_YES ||
59- (match == matcher_t::MATCH_MAYBE &&
60- skip == matcher_t::SKIP_NO))
61- {
62- num_items--;
63- if (match_glyph_data) match_glyph_data++;
64- return true;
65- }
66-
67- if (skip == matcher_t::SKIP_NO)
68+ switch (match (c->buffer->info[idx]))
69 {
70- if (unsafe_to)
71- *unsafe_to = idx + 1;
72- return false;
73+ case MATCH:
74+ {
75+ num_items--;
76+ if (match_glyph_data) match_glyph_data++;
77+ return true;
78+ }
79+ case NOT_MATCH:
80+ {
81+ if (unsafe_to)
82+ *unsafe_to = idx + 1;
83+ return false;
84+ }
85+ case SKIP:
86+ continue;
87 }
88 }
89 if (unsafe_to)
90@@ -408,27 +427,22 @@ struct hb_ot_apply_context_t :
91 while (idx > num_items - 1)
92 {
93 idx--;
94- const hb_glyph_info_t &info = c->buffer->out_info[idx];
95-
96- matcher_t::may_skip_t skip = matcher.may_skip (c, info);
97- if (unlikely (skip == matcher_t::SKIP_YES))
98- continue;
99-
100- matcher_t::may_match_t match = matcher.may_match (info, match_glyph_data);
101- if (match == matcher_t::MATCH_YES ||
102- (match == matcher_t::MATCH_MAYBE &&
103- skip == matcher_t::SKIP_NO))
104+ switch (match (c->buffer->out_info[idx]))
105 {
106- num_items--;
107- if (match_glyph_data) match_glyph_data++;
108- return true;
109- }
110-
111- if (skip == matcher_t::SKIP_NO)
112- {
113- if (unsafe_from)
114- *unsafe_from = hb_max (1u, idx) - 1u;
115- return false;
116+ case MATCH:
117+ {
118+ num_items--;
119+ if (match_glyph_data) match_glyph_data++;
120+ return true;
121+ }
122+ case NOT_MATCH:
123+ {
124+ if (unsafe_from)
125+ *unsafe_from = hb_max (1u, idx) - 1u;
126+ return false;
127+ }
128+ case SKIP:
129+ continue;
130 }
131 }
132 if (unsafe_from)
133--
1342.25.1
135
diff --git a/meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193.patch b/meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193.patch
new file mode 100644
index 0000000000..e4ac13dbad
--- /dev/null
+++ b/meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193.patch
@@ -0,0 +1,179 @@
1From 9c8e972dbecda93546038d24444d8216397d75a3 Mon Sep 17 00:00:00 2001
2From: Behdad Esfahbod <behdad@behdad.org>
3Date: Mon, 6 Feb 2023 14:51:25 -0700
4Subject: [PATCH] [GPOS] Avoid O(n^2) behavior in mark-attachment
5
6Upstream-Status: Backport from [https://github.com/harfbuzz/harfbuzz/commit/8708b9e081192786c027bb7f5f23d76dbe5c19e8]
7Comment1: The Original Patch [https://github.com/harfbuzz/harfbuzz/commit/85be877925ddbf34f74a1229f3ca1716bb6170dc] causes regression and was reverted. This Patch completes the fix.
8Comment2: The Patch contained files MarkBasePosFormat1.hh and MarkLigPosFormat1.hh which were moved from hb-ot-layout-gpos-table.hh as per https://github.com/harfbuzz/harfbuzz/commit/197d9a5c994eb41c8c89b7b958b26b1eacfeeb00
9CVE: CVE-2023-25193
10Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
11Signed-off-by: Dhairya Nagodra <dnagodra@cisco.com>
12
13---
14 src/hb-ot-layout-gpos-table.hh | 103 +++++++++++++++++++++++----------
15 src/hb-ot-layout-gsubgpos.hh | 5 +-
16 2 files changed, 78 insertions(+), 30 deletions(-)
17
18diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
19index 024312d..db5f9ae 100644
20--- a/src/hb-ot-layout-gpos-table.hh
21+++ b/src/hb-ot-layout-gpos-table.hh
22@@ -1458,6 +1458,25 @@ struct MarkBasePosFormat1
23
24 const Coverage &get_coverage () const { return this+markCoverage; }
25
26+ static inline bool accept (hb_buffer_t *buffer, unsigned idx)
27+ {
28+ /* We only want to attach to the first of a MultipleSubst sequence.
29+ * https://github.com/harfbuzz/harfbuzz/issues/740
30+ * Reject others...
31+ * ...but stop if we find a mark in the MultipleSubst sequence:
32+ * https://github.com/harfbuzz/harfbuzz/issues/1020 */
33+ return !_hb_glyph_info_multiplied (&buffer->info[idx]) ||
34+ 0 == _hb_glyph_info_get_lig_comp (&buffer->info[idx]) ||
35+ (idx == 0 ||
36+ _hb_glyph_info_is_mark (&buffer->info[idx - 1]) ||
37+ !_hb_glyph_info_multiplied (&buffer->info[idx - 1]) ||
38+ _hb_glyph_info_get_lig_id (&buffer->info[idx]) !=
39+ _hb_glyph_info_get_lig_id (&buffer->info[idx - 1]) ||
40+ _hb_glyph_info_get_lig_comp (&buffer->info[idx]) !=
41+ _hb_glyph_info_get_lig_comp (&buffer->info[idx - 1]) + 1
42+ );
43+ }
44+
45 bool apply (hb_ot_apply_context_t *c) const
46 {
47 TRACE_APPLY (this);
48@@ -1465,37 +1484,46 @@ struct MarkBasePosFormat1
49 unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint);
50 if (likely (mark_index == NOT_COVERED)) return_trace (false);
51
52- /* Now we search backwards for a non-mark glyph */
53+ /* Now we search backwards for a non-mark glyph.
54+ * We don't use skippy_iter.prev() to avoid O(n^2) behavior. */
55+
56 hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
57- skippy_iter.reset (buffer->idx, 1);
58 skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
59- do {
60- if (!skippy_iter.prev ()) return_trace (false);
61- /* We only want to attach to the first of a MultipleSubst sequence.
62- * https://github.com/harfbuzz/harfbuzz/issues/740
63- * Reject others...
64- * ...but stop if we find a mark in the MultipleSubst sequence:
65- * https://github.com/harfbuzz/harfbuzz/issues/1020 */
66- if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) ||
67- 0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) ||
68- (skippy_iter.idx == 0 ||
69- _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) ||
70- _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) !=
71- _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) ||
72- _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) !=
73- _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1
74- ))
75- break;
76- skippy_iter.reject ();
77- } while (true);
78+ unsigned j;
79+ for (j = buffer->idx; j > c->last_base_until; j--)
80+ {
81+ auto match = skippy_iter.match (buffer->info[j - 1]);
82+ if (match == skippy_iter.MATCH)
83+ {
84+ if (!accept (buffer, j - 1))
85+ match = skippy_iter.SKIP;
86+ }
87+ if (match == skippy_iter.MATCH)
88+ {
89+ c->last_base = (signed) j - 1;
90+ break;
91+ }
92+ }
93+ c->last_base_until = buffer->idx;
94+ if (c->last_base == -1)
95+ {
96+ buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1);
97+ return_trace (false);
98+ }
99+
100+ unsigned idx = (unsigned) c->last_base;
101
102 /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */
103- //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); }
104+ //if (!_hb_glyph_info_is_base_glyph (&buffer->info[idx])) { return_trace (false); }
105
106- unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[skippy_iter.idx].codepoint);
107- if (base_index == NOT_COVERED) return_trace (false);
108+ unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[idx].codepoint);
109+ if (base_index == NOT_COVERED)
110+ {
111+ buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1);
112+ return_trace (false);
113+ }
114
115- return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
116+ return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, idx));
117 }
118
119 bool subset (hb_subset_context_t *c) const
120@@ -1587,15 +1615,32 @@ struct MarkLigPosFormat1
121 if (likely (mark_index == NOT_COVERED)) return_trace (false);
122
123 /* Now we search backwards for a non-mark glyph */
124+
125 hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
126- skippy_iter.reset (buffer->idx, 1);
127 skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
128- if (!skippy_iter.prev ()) return_trace (false);
129+
130+ unsigned j;
131+ for (j = buffer->idx; j > c->last_base_until; j--)
132+ {
133+ auto match = skippy_iter.match (buffer->info[j - 1]);
134+ if (match == skippy_iter.MATCH)
135+ {
136+ c->last_base = (signed) j - 1;
137+ break;
138+ }
139+ }
140+ c->last_base_until = buffer->idx;
141+ if (c->last_base == -1)
142+ {
143+ buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1);
144+ return_trace (false);
145+ }
146+
147+ j = (unsigned) c->last_base;
148
149 /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */
150- //if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); }
151+ //if (!_hb_glyph_info_is_ligature (&buffer->info[idx])) { return_trace (false); }
152
153- unsigned int j = skippy_iter.idx;
154 unsigned int lig_index = (this+ligatureCoverage).get_coverage (buffer->info[j].codepoint);
155 if (lig_index == NOT_COVERED) return_trace (false);
156
157diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh
158index 5a7e564..437123c 100644
159--- a/src/hb-ot-layout-gsubgpos.hh
160+++ b/src/hb-ot-layout-gsubgpos.hh
161@@ -503,6 +503,9 @@ struct hb_ot_apply_context_t :
162 uint32_t random_state;
163
164
165+ signed last_base = -1; // GPOS uses
166+ unsigned last_base_until = 0; // GPOS uses
167+
168 hb_ot_apply_context_t (unsigned int table_index_,
169 hb_font_t *font_,
170 hb_buffer_t *buffer_) :
171@@ -536,7 +539,7 @@ struct hb_ot_apply_context_t :
172 iter_context.init (this, true);
173 }
174
175- void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; init_iters (); }
176+ void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; last_base = -1; last_base_until = 0; init_iters (); }
177 void set_auto_zwj (bool auto_zwj_) { auto_zwj = auto_zwj_; init_iters (); }
178 void set_auto_zwnj (bool auto_zwnj_) { auto_zwnj = auto_zwnj_; init_iters (); }
179 void set_random (bool random_) { random = random_; }