Skip to content

Commit e0ba739

Browse files
[[ Bug 19094 ]] Certain unicode chars render incorrectly on android 7
It appears that font fallback is no longer supported in the current version of Skia. So, if harbuzz returns no glyph for a range of text, shape that range again using a fallback font.
1 parent d3a0756 commit e0ba739

2 files changed

Lines changed: 80 additions & 45 deletions

File tree

docs/notes/bugfix-19094.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Certain unicode characters render incorrectly on Android 7

libgraphics/src/harfbuzztext.cpp

Lines changed: 79 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ along with LiveCode. If not see <http://www.gnu.org/licenses/>. */
2727
#include <SkTypeface.h>
2828
#include <SkPoint.h>
2929
#include <SkTypes.h>
30+
#include <SkFontMgr.h>
31+
#include <SkUtils.h>
3032

3133
#include "foundation-unicode.h"
3234
#include <unicode/uscript.h>
@@ -65,10 +67,10 @@ struct MCGlyphRun
6567
sk_sp<SkTypeface> typeface;
6668
};
6769

68-
void MCGlyphRunMake(hb_glyph_info_t *p_infos, hb_glyph_position_t *p_positions, MCGPoint* x_location, uindex_t p_start, uindex_t p_end, const MCGFont &p_font, MCGlyphRun& r_run)
70+
void MCGlyphRunMake(hb_glyph_info_t *p_infos, hb_glyph_position_t *p_positions, MCGPoint* x_location, uindex_t p_start, uindex_t p_end, SkTypeface* p_typeface, uint16_t p_size, MCGlyphRun& r_run)
6971
{
7072
// Skia APIs expect typefaces to be passed as shared pointers
71-
sk_sp<SkTypeface> t_typeface((SkTypeface*)p_font.fid);
73+
sk_sp<SkTypeface> t_typeface(p_typeface);
7274
t_typeface->ref();
7375

7476
uindex_t t_count = p_end - p_start;
@@ -99,7 +101,7 @@ void MCGlyphRunMake(hb_glyph_info_t *p_infos, hb_glyph_position_t *p_positions,
99101
else
100102
{
101103
uindex_t advance;
102-
skia_get_replacement_glyph(p_font . size, t_typeface, r_run . glyphs[run_index], advance);
104+
skia_get_replacement_glyph(p_size, t_typeface, r_run . glyphs[run_index], advance);
103105
advance_x += advance;
104106
}
105107

@@ -234,31 +236,37 @@ MCHarfbuzzSkiaFace *MCHarfbuzzGetFaceForSkiaTypeface(SkTypeface *p_typeface, uin
234236
return nil;
235237
}
236238

237-
void shape(const unichar_t* p_text, uindex_t p_char_count, MCGPoint p_location, bool p_rtl, const MCGFont &p_font, MCGlyphRun*& r_runs, uindex_t& r_run_count)
239+
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)
238240
{
239-
MCAutoArray<MCGlyphRun> t_runs;
240-
241-
if (p_font . fid == nil)
242-
{
243-
MCLog("%s: Found null font!", __FUNCTION__);
244-
r_run_count = 0;
245-
r_runs = nil;
246-
return;
247-
}
248-
249-
SkTypeface *t_typeface = (SkTypeface *)p_font . fid;
250-
251-
//MCLog("typeface name %d", t_typeface -> uniqueID());
241+
if (p_font . fid == nil)
242+
return 0;
243+
244+
SkTypeface *t_typeface = nil;
245+
if (p_use_fallback)
246+
{
247+
// If we're to use a fallback font, use Skia's font manager to find a
248+
// font that has glyphs for the first char in the string.
249+
const unichar_t* t_text = p_text;
250+
SkUnichar t_uni_char = SkUTF16_NextUnichar(&t_text);
251+
sk_sp<SkFontMgr> t_fnt_mgr(SkFontMgr::RefDefault());
252+
t_typeface = t_fnt_mgr -> matchFamilyStyleCharacter(nil, ((SkTypeface *)p_font . fid) -> fontStyle(), nil, 0, t_uni_char);
253+
254+
// If there is no fallback font for the char, use the replacement glyph
255+
// in the original font.
256+
if (t_typeface == nil)
257+
t_typeface = (SkTypeface *)p_font . fid;
258+
}
259+
else
260+
t_typeface = (SkTypeface *)p_font . fid;
261+
262+
if (t_typeface == nil)
263+
return 0;
252264

253265
MCHarfbuzzSkiaFace *t_hb_sk_face;
254266
t_hb_sk_face = MCHarfbuzzGetFaceForSkiaTypeface(t_typeface, p_font . size);
255267

256268
if (t_hb_sk_face == nil)
257-
{
258-
r_run_count = 0;
259-
r_runs = nil;
260-
return;
261-
}
269+
return 0;
262270

263271
// Set up the HarfBuzz buffer
264272
hb_buffer_t *buffer = hb_buffer_create();
@@ -271,59 +279,85 @@ void shape(const unichar_t* p_text, uindex_t p_char_count, MCGPoint p_location,
271279

272280
hb_shape(HBSkiaFaceToHBFont(t_hb_sk_face), buffer, NULL, 0);
273281

274-
int glyph_count = hb_buffer_get_length(buffer);
282+
uindex_t glyph_count = hb_buffer_get_length(buffer);
275283
hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buffer, 0);
276284
hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buffer, 0);
277285

278-
uindex_t t_cur_glyph = 0;
279-
uindex_t t_start, t_end;
280-
uindex_t t_char_index = 0;
281-
286+
uindex_t t_cur_glyph = 0, t_start = 0, t_run_count = 0;
287+
282288
// deal with runs of supported and unsupported glyphs
283289
while (t_cur_glyph < glyph_count)
284290
{
285291
MCGlyphRun t_run;
286-
t_start = t_end = t_cur_glyph;
292+
t_start = t_cur_glyph;
287293

288294
// Run of successfully shaped glyphs
289295
while (t_cur_glyph < glyph_count && glyph_info[t_cur_glyph] . codepoint != 0)
290296
{
291-
t_char_index++;
292-
t_end++;
293297
t_cur_glyph++;
294298
}
295299

296-
if (t_start != t_end)
300+
if (t_start != t_cur_glyph)
297301
{
298-
MCGlyphRunMake(glyph_info, glyph_pos, &p_location, t_start, t_end, p_font, t_run);
299-
t_runs . Push(t_run);
302+
MCGlyphRunMake(glyph_info, glyph_pos, x_location, t_start, t_cur_glyph, t_typeface, p_font . size, t_run);
303+
x_runs . Push(t_run);
300304
t_start = t_cur_glyph;
301-
t_end = t_cur_glyph;
305+
t_run_count++;
306+
}
307+
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)
311+
{
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);
315+
x_runs . Push(t_run);
316+
t_start = t_cur_glyph;
317+
t_run_count++;
302318
}
303319

304320
// Deal with run of unsupported characters for this font.
305321
while (t_cur_glyph < glyph_count && glyph_info[t_cur_glyph] . codepoint == 0)
306322
{
307-
t_end++;
308323
t_cur_glyph++;
309324
}
310325

311-
if (t_start != t_end)
326+
// For the run of unsupported chars, shape using a fallback font.
327+
if (t_start != t_cur_glyph)
312328
{
313-
// Need to get a fallback font here.
314-
// For now, just fail to display the glyphs. Should display 'missing character' glyph.
315-
MCGlyphRunMake(glyph_info, glyph_pos, &p_location, t_start, t_end, p_font, t_run);
316-
t_runs . Push(t_run);
317-
329+
t_run_count += shape_text_and_add_to_glyph_array(
330+
p_text + glyph_info[t_start] . cluster,
331+
glyph_info[t_cur_glyph - 1] . cluster - glyph_info[t_start] . cluster + 1,
332+
p_rtl,
333+
p_font,
334+
true,
335+
x_location,
336+
x_runs
337+
);
318338
t_start = t_cur_glyph;
319-
t_end = t_cur_glyph;
320339
}
321-
322-
t_char_index += (t_end - t_start);
323340
}
324341

325342
hb_buffer_destroy(buffer);
326-
t_runs . Take(r_runs, r_run_count);
343+
return t_run_count;
344+
}
345+
346+
void shape(const unichar_t* p_text, uindex_t p_char_count, MCGPoint p_location, bool p_rtl, const MCGFont &p_font, MCGlyphRun*& r_runs, uindex_t& r_run_count)
347+
{
348+
MCAutoArray<MCGlyphRun> t_runs;
349+
uindex_t t_run_count = shape_text_and_add_to_glyph_array(p_text, p_char_count, p_rtl, p_font, false, &p_location, t_runs);
350+
351+
if (t_run_count == 0)
352+
{
353+
r_run_count = 0;
354+
r_runs = nil;
355+
}
356+
else
357+
{
358+
r_run_count = t_run_count;
359+
t_runs . Take(r_runs, r_run_count);
360+
}
327361
}
328362

329363
////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)