--- src/text.cc +++ src/text.cc 2026-04-26 20:58:17.210610855 +0000 @@ -1,8 +1,27 @@ #include "text.h" -#include -#include -#include +#include +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#else +#include +#endif + +static sk_sp get_font_mgr() { + static sk_sp mgr; + if (!mgr) { +#ifdef __APPLE__ + mgr = SkFontMgr_New_CoreText(nullptr); +#else + mgr = SkFontMgr_New_FontConfig(nullptr, nullptr); +#endif + } + return mgr; +} FontStyle AttrsToFontStyle(Attr attrs) { if (attrs.flags & Attr::kBold) { @@ -35,6 +54,7 @@ } void GlyphRenderer::SetFont(string name) { + sk_sp fontMgr = get_font_mgr(); SkFontStyle styles[] = { SkFontStyle::Normal(), SkFontStyle::Bold(), @@ -42,7 +62,28 @@ }; for (int i = 0; i < kStyleEnd; i++) { - m_styled_fonts[i].font.setTypeface(SkTypeface::MakeFromName(name.c_str(), styles[i])); + sk_sp typeface = fontMgr->matchFamilyStyle(name.c_str(), styles[i]); + // Fallback chain for when requested font isn't found + if (!typeface) { +#ifdef __APPLE__ + // macOS fallback fonts + typeface = fontMgr->matchFamilyStyle("Menlo", styles[i]); + if (!typeface) { + typeface = fontMgr->matchFamilyStyle("Monaco", styles[i]); + } +#else + // Linux fallback fonts + typeface = fontMgr->matchFamilyStyle("DejaVu Sans Mono", styles[i]); + if (!typeface) { + typeface = fontMgr->matchFamilyStyle("Liberation Mono", styles[i]); + } +#endif + // Last resort: use default typeface + if (!typeface) { + typeface = fontMgr->legacyMakeTypeface(nullptr, styles[i]); + } + } + m_styled_fonts[i].font.setTypeface(typeface); } UpdateForFontChange(); @@ -59,8 +100,10 @@ } } - styled_font.font.textToGlyphs(&c, sizeof(c), kUTF32_SkTextEncoding, &m_glyphs[index], 1); - return m_glyphs[index] != 0; + SkGlyphID glyph; + styled_font.font.textToGlyphs(&c, sizeof(c), SkTextEncoding::kUTF32, {&glyph, 1}); + m_glyphs[index] = glyph; + return glyph != 0; } void GlyphRenderer::ClearGlyph(int index) { @@ -75,21 +118,26 @@ SkScalar GlyphRenderer::FindWidth() { auto &styled_font = m_styled_fonts[kStyleNormal]; - if (styled_font.metrics.fAvgCharWidth) { - return styled_font.metrics.fAvgCharWidth; - } - + // Prefer actual glyph width over fAvgCharWidth which may be inaccurate if (SkGlyphID glyph = styled_font.glyph_cache['x']) { - SkScalar width; - styled_font.font.getWidthsBounds(&glyph, 1, &width, nullptr, nullptr); - fmt::print("{}\n", width); - return width; + return styled_font.font.getWidth(glyph); } + // Fallback to measuring 'x' directly SkRect bounds; const char *s = "x"; - styled_font.font.measureText(s, sizeof(s[0]), kUTF8_SkTextEncoding, &bounds); - return bounds.width(); + styled_font.font.measureText(s, sizeof(s[0]), SkTextEncoding::kUTF8, &bounds); + if (bounds.width() > 0) { + return bounds.width(); + } + + // Last resort: use fAvgCharWidth if available + if (styled_font.metrics.fAvgCharWidth > 0) { + return styled_font.metrics.fAvgCharWidth; + } + + // Emergency fallback: estimate based on font size + return styled_font.font.getSize() * 0.6f; } SkScalar GlyphRenderer::FindBaselineOffset() { @@ -141,12 +189,13 @@ } SkScalar end_x = positions[i - 1].x() + FindWidth(); - SkPath path; - path.moveTo(begin_x, last_y + y_offset); - path.lineTo(end_x, last_y + y_offset); + // Skia m144+: SkPath is immutable, use SkPathBuilder + SkPathBuilder pathBuilder; + pathBuilder.moveTo(begin_x, last_y + y_offset); + pathBuilder.lineTo(end_x, last_y + y_offset); paint.setStrokeWidth(stroke_width); - canvas->drawPath(path, paint); + canvas->drawPath(pathBuilder.detach(), paint); if (i < end) { last_y = positions[i].y(); @@ -168,7 +217,7 @@ for (char c = 0; c < kCharMax; c++) { if (isprint(c)) { SkGlyphID glyph; - font.textToGlyphs(&c, sizeof(c), kUTF8_SkTextEncoding, &glyph, 1); + font.textToGlyphs(&c, sizeof(c), SkTextEncoding::kUTF8, {&glyph, 1}); if (glyph) { glyph_cache[c] = glyph; }