From d893d3234d76eaa57eea7e242a889d88eba8b8bc Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Tue, 5 Aug 2025 07:11:13 -0400 Subject: [PATCH] LibGfx: Support UTF-16 SVG text placement --- Libraries/LibGfx/Path.h | 13 +++++--- Libraries/LibGfx/PathSkia.cpp | 63 +++++++++++++++++++++++++++-------- Libraries/LibGfx/PathSkia.h | 6 ++-- 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/Libraries/LibGfx/Path.h b/Libraries/LibGfx/Path.h index aaa86d01a8b..b7039f509f8 100644 --- a/Libraries/LibGfx/Path.h +++ b/Libraries/LibGfx/Path.h @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -32,7 +31,8 @@ public: virtual void arc_to(FloatPoint point, float radius, bool large_arc, bool sweep) = 0; virtual void quadratic_bezier_curve_to(FloatPoint through, FloatPoint point) = 0; virtual void cubic_bezier_curve_to(FloatPoint c1, FloatPoint c2, FloatPoint p2) = 0; - virtual void text(Utf8View, Font const&) = 0; + virtual void text(Utf8View const&, Font const&) = 0; + virtual void text(Utf16View const&, Font const&) = 0; virtual void glyph_run(GlyphRun const&) = 0; virtual void offset(Gfx::FloatPoint const&) = 0; @@ -47,7 +47,8 @@ public: virtual NonnullOwnPtr clone() const = 0; virtual NonnullOwnPtr copy_transformed(Gfx::AffineTransform const&) const = 0; - virtual NonnullOwnPtr place_text_along(Utf8View text, Font const&) const = 0; + virtual NonnullOwnPtr place_text_along(Utf8View const& text, Font const&) const = 0; + virtual NonnullOwnPtr place_text_along(Utf16View const& text, Font const&) const = 0; }; class Path { @@ -91,7 +92,8 @@ public: void arc_to(FloatPoint point, float radius, bool large_arc, bool sweep) { impl().arc_to(point, radius, large_arc, sweep); } void quadratic_bezier_curve_to(FloatPoint through, FloatPoint point) { impl().quadratic_bezier_curve_to(through, point); } void cubic_bezier_curve_to(FloatPoint c1, FloatPoint c2, FloatPoint p2) { impl().cubic_bezier_curve_to(c1, c2, p2); } - void text(Utf8View text, Font const& font) { impl().text(text, font); } + void text(Utf8View const& text, Font const& font) { impl().text(text, font); } + void text(Utf16View const& text, Font const& font) { impl().text(text, font); } void glyph_run(GlyphRun const& glyph_run) { impl().glyph_run(glyph_run); } void offset(Gfx::FloatPoint const& offset) { impl().offset(offset); } @@ -109,7 +111,8 @@ public: Gfx::Path clone() const { return Gfx::Path { impl().clone() }; } Gfx::Path copy_transformed(Gfx::AffineTransform const& transform) const { return Gfx::Path { impl().copy_transformed(transform) }; } - Gfx::Path place_text_along(Utf8View text, Font const& font) const { return Gfx::Path { impl().place_text_along(text, font) }; } + Gfx::Path place_text_along(Utf8View const& text, Font const& font) const { return Gfx::Path { impl().place_text_along(text, font) }; } + Gfx::Path place_text_along(Utf16View const& text, Font const& font) const { return Gfx::Path { impl().place_text_along(text, font) }; } void transform(Gfx::AffineTransform const& transform) { m_impl = impl().copy_transformed(transform); } diff --git a/Libraries/LibGfx/PathSkia.cpp b/Libraries/LibGfx/PathSkia.cpp index 796ef3af420..c97a74e94c7 100644 --- a/Libraries/LibGfx/PathSkia.cpp +++ b/Libraries/LibGfx/PathSkia.cpp @@ -7,6 +7,8 @@ #define AK_DONT_REPLACE_STD #include +#include +#include #include #include #include @@ -136,11 +138,21 @@ void PathImplSkia::cubic_bezier_curve_to(FloatPoint c1, FloatPoint c2, FloatPoin m_path->cubicTo(c1.x(), c1.y(), c2.x(), c2.y(), p2.x(), p2.y()); } -void PathImplSkia::text(Utf8View string, Font const& font) +void PathImplSkia::text(Utf8View const& string, Font const& font) { SkTextUtils::GetPath(string.as_string().characters_without_null_termination(), string.as_string().length(), SkTextEncoding::kUTF8, last_point().x(), last_point().y(), font.skia_font(1), m_path.ptr()); } +void PathImplSkia::text(Utf16View const& string, Font const& font) +{ + if (string.has_ascii_storage()) { + text(Utf8View { string.bytes() }, font); + return; + } + + SkTextUtils::GetPath(string.utf16_span().data(), string.length_in_code_units() * sizeof(char16_t), SkTextEncoding::kUTF16, last_point().x(), last_point().y(), font.skia_font(1), m_path.ptr()); +} + void PathImplSkia::glyph_run(GlyphRun const& glyph_run) { auto sk_font = glyph_run.font().skia_font(1); @@ -160,37 +172,41 @@ void PathImplSkia::offset(Gfx::FloatPoint const& offset) m_path->offset(offset.x(), offset.y()); } -NonnullOwnPtr PathImplSkia::place_text_along(Utf8View text, Font const& font) const +template +static NonnullOwnPtr place_text_along_impl(SkPath const& path, Font const& font, size_t length_in_code_points, TextToGlyphs&& text_to_glyphs) { auto sk_font = font.skia_font(1); - size_t const text_length = text.length(); SkScalar x = 0; SkScalar y = 0; + SkTextBlobBuilder builder; - SkTextBlobBuilder::RunBuffer runBuffer = builder.allocRun(sk_font, text_length, x, y, nullptr); - sk_font.textToGlyphs(text.as_string().characters_without_null_termination(), text.as_string().length(), SkTextEncoding::kUTF8, runBuffer.glyphs, text_length); - SkPathMeasure pathMeasure(*m_path, false); + auto const& run_buffer = builder.allocRun(sk_font, static_cast(length_in_code_points), x, y, nullptr); + text_to_glyphs(sk_font, run_buffer); + + SkPathMeasure path_measure(path, false); SkScalar accumulated_distance = 0; + auto output_path = PathImplSkia::create(); - for (size_t i = 0; i < text_length; ++i) { - SkGlyphID glyph = runBuffer.glyphs[i]; - SkPath glyphPath; - sk_font.getPath(glyph, &glyphPath); + + for (size_t i = 0; i < length_in_code_points; ++i) { + SkGlyphID glyph = run_buffer.glyphs[i]; + SkPath glyph_path; + sk_font.getPath(glyph, &glyph_path); SkScalar advance; sk_font.getWidths(&glyph, 1, &advance); SkPoint position; SkVector tangent; - if (!pathMeasure.getPosTan(accumulated_distance, &position, &tangent)) + if (!path_measure.getPosTan(accumulated_distance, &position, &tangent)) continue; SkMatrix matrix; matrix.setTranslate(position.x(), position.y()); matrix.preRotate(SkRadiansToDegrees(std::atan2(tangent.y(), tangent.x()))); - glyphPath.transform(matrix); - output_path->sk_path().addPath(glyphPath); + glyph_path.transform(matrix); + output_path->sk_path().addPath(glyph_path); accumulated_distance += advance; } @@ -198,6 +214,27 @@ NonnullOwnPtr PathImplSkia::place_text_along(Utf8View text, Font const return output_path; } +NonnullOwnPtr PathImplSkia::place_text_along(Utf8View const& text, Font const& font) const +{ + auto length_in_code_points = text.length(); + + return place_text_along_impl(*m_path, font, length_in_code_points, [&](auto const& sk_font, auto const& run_buffer) { + sk_font.textToGlyphs(text.as_string().characters_without_null_termination(), text.as_string().length(), SkTextEncoding::kUTF8, run_buffer.glyphs, length_in_code_points); + }); +} + +NonnullOwnPtr PathImplSkia::place_text_along(Utf16View const& text, Font const& font) const +{ + if (text.has_ascii_storage()) + return place_text_along(Utf8View { text.bytes() }, font); + + auto length_in_code_points = text.length_in_code_points(); + + return place_text_along_impl(*m_path, font, length_in_code_points, [&](auto const& sk_font, auto const& run_buffer) { + sk_font.textToGlyphs(text.utf16_span().data(), text.length_in_code_units() * sizeof(char16_t), SkTextEncoding::kUTF16, run_buffer.glyphs, length_in_code_points); + }); +} + void PathImplSkia::append_path(Gfx::Path const& other) { m_path->addPath(static_cast(other.impl()).sk_path()); diff --git a/Libraries/LibGfx/PathSkia.h b/Libraries/LibGfx/PathSkia.h index 507b577e476..537eac803fc 100644 --- a/Libraries/LibGfx/PathSkia.h +++ b/Libraries/LibGfx/PathSkia.h @@ -27,7 +27,8 @@ public: virtual void arc_to(FloatPoint point, float radius, bool large_arc, bool sweep) override; virtual void quadratic_bezier_curve_to(FloatPoint through, FloatPoint point) override; virtual void cubic_bezier_curve_to(FloatPoint c1, FloatPoint c2, FloatPoint p2) override; - virtual void text(Utf8View, Font const&) override; + virtual void text(Utf8View const&, Font const&) override; + virtual void text(Utf16View const&, Font const&) override; virtual void glyph_run(GlyphRun const&) override; virtual void offset(Gfx::FloatPoint const&) override; @@ -42,7 +43,8 @@ public: virtual NonnullOwnPtr clone() const override; virtual NonnullOwnPtr copy_transformed(Gfx::AffineTransform const&) const override; - virtual NonnullOwnPtr place_text_along(Utf8View text, Font const&) const override; + virtual NonnullOwnPtr place_text_along(Utf8View const& text, Font const&) const override; + virtual NonnullOwnPtr place_text_along(Utf16View const& text, Font const&) const override; SkPath const& sk_path() const { return *m_path; } SkPath& sk_path() { return *m_path; }