Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

Commit 2565cfe

Browse files
committed
[[ Bug 20482 ]] Use replacement chars on a cluster rather than glyph basis
We need fields to visually represent grapheme boundaries so that navigation works as expected, so deal with glyph info on a cluster by cluster basis, replacing whole clusters with a replacement char if any part of it is unsupported by the current font or fallback font.
1 parent 6210bcf commit 2565cfe

File tree

2 files changed

+53
-17
lines changed

2 files changed

+53
-17
lines changed

docs/notes/bugfix-20482.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Fix crash on Android with unsupported emojis

libgraphics/src/harfbuzztext.cpp

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,26 @@ MCHarfbuzzSkiaFace *MCHarfbuzzGetFaceForSkiaTypeface(SkTypeface *p_typeface, uin
236236
return nil;
237237
}
238238

239+
// Check if the font supports all glyphs in a given cluster from shape info
240+
static bool cluster_is_supported(hb_glyph_info_t* p_info, uindex_t p_index, uindex_t p_count, uindex_t& r_cluster_end)
241+
{
242+
bool t_supported = true;
243+
uindex_t t_cluster = p_info[p_index] . cluster;
244+
while (++p_index < p_count &&
245+
p_info[p_index] . cluster == t_cluster)
246+
{
247+
if (p_info[p_index] . codepoint == 0)
248+
{
249+
// If any of these glyphs' codepoints are 0 then the
250+
// whole cluster is unsupported
251+
t_supported = false;
252+
}
253+
}
254+
255+
r_cluster_end = p_index;
256+
return t_supported;
257+
}
258+
239259
static uindex_t shape_text_and_add_to_glyph_array(const unichar_t* p_text, uindex_t p_char_count, bool p_rtl, const MCGFont &p_font, bool p_use_fallback, MCGPoint* x_location, MCAutoArray<MCGlyphRun>& x_runs)
240260
{
241261
if (p_font . fid == nil)
@@ -284,19 +304,21 @@ static uindex_t shape_text_and_add_to_glyph_array(const unichar_t* p_text, uinde
284304
hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buffer, 0);
285305

286306
uindex_t t_cur_glyph = 0, t_start = 0, t_run_count = 0;
287-
307+
uindex_t t_cluster_end = 0;
308+
288309
// deal with runs of supported and unsupported glyphs
289310
while (t_cur_glyph < glyph_count)
290311
{
291312
MCGlyphRun t_run;
292313
t_start = t_cur_glyph;
293314

294-
// Run of successfully shaped glyphs
295-
while (t_cur_glyph < glyph_count && glyph_info[t_cur_glyph] . codepoint != 0)
315+
// Run of glyphs in successfully shaped clusters
316+
while (t_cur_glyph < glyph_count &&
317+
cluster_is_supported(glyph_info, t_cur_glyph, glyph_count, &t_cluster_end))
296318
{
297-
t_cur_glyph++;
319+
t_cur_glyph = t_cluster_end;
298320
}
299-
321+
300322
if (t_start != t_cur_glyph)
301323
{
302324
MCGlyphRunMake(glyph_info, glyph_pos, x_location, t_start, t_cur_glyph, t_typeface, p_font . size, t_run);
@@ -305,37 +327,50 @@ static uindex_t shape_text_and_add_to_glyph_array(const unichar_t* p_text, uinde
305327
t_run_count++;
306328
}
307329

308-
// If the first char is unsupported and we're already using the fallback
309-
// font for that char, assume there is no glyph and use a replacement.
310-
if (t_cur_glyph == 0 && glyph_info[t_cur_glyph] . codepoint == 0 && p_use_fallback)
330+
// If the first cluster is unsupported and we're already using the fallback
331+
// font for that cluster, assume there is no support and use a replacement.
332+
if (t_cur_glyph == 0 &&
333+
!cluster_is_supported(glyph_info, t_cur_glyph, glyph_count, &t_cluster_end)
334+
&& p_use_fallback)
311335
{
312-
t_cur_glyph++;
313-
314-
MCGlyphRunMake(glyph_info, glyph_pos, x_location, t_start, t_cur_glyph, (SkTypeface *)p_font . fid, p_font . size, t_run);
336+
// Enforce a replacement glyph for the whole cluster by passing a 0
337+
// codepoint and only adding a single glyph to the run
338+
glyph_info[t_cur_glyph].codepoint = 0;
339+
MCGlyphRunMake(glyph_info, glyph_pos, x_location, t_start, t_start + 1, (SkTypeface *)p_font . fid, p_font . size, t_run);
315340
x_runs . Push(t_run);
341+
t_cur_glyph = t_cluster_end
316342
t_start = t_cur_glyph;
317343
t_run_count++;
318344
}
319345

320-
// Deal with run of unsupported characters for this font.
321-
while (t_cur_glyph < glyph_count && glyph_info[t_cur_glyph] . codepoint == 0)
346+
// Deal with run of unsupported clusters for this font.
347+
while (t_cur_glyph < glyph_count &&
348+
!cluster_is_supported(glyph_info, t_cur_glyph, glyph_count, &t_cluster_end))
322349
{
323-
t_cur_glyph++;
350+
t_cur_glyph = t_cluster_end;
324351
}
325352

326-
// For the run of unsupported chars, shape using a fallback font.
353+
// For the run of unsupported clusters, shape using a fallback font.
327354
if (t_start != t_cur_glyph)
328355
{
356+
// Reshape from the beginning of the unsupported cluster run
357+
// to the beginning of the cluster containing t_cur_glyph.
358+
// At this point, t_cur_glyph might be equal to glyph_count,
359+
// in which case compute the reshape char count using the old char
360+
// count.
361+
uindex_t t_end_index = p_char_count;
362+
if (t_cur_glyph != glyph_count)
363+
t_end_index = glyph_info[t_cur_glyph] . cluster;
364+
329365
t_run_count += shape_text_and_add_to_glyph_array(
330366
p_text + glyph_info[t_start] . cluster,
331-
glyph_info[t_cur_glyph - 1] . cluster - glyph_info[t_start] . cluster + 1,
367+
t_end_index - glyph_info[t_start] . cluster,
332368
p_rtl,
333369
p_font,
334370
true,
335371
x_location,
336372
x_runs
337373
);
338-
t_start = t_cur_glyph;
339374
}
340375
}
341376

0 commit comments

Comments
 (0)