WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
266832
Curved SVG textpath doesn't render unicode text/font properly on Apple devices
https://bugs.webkit.org/show_bug.cgi?id=266832
Summary
Curved SVG textpath doesn't render unicode text/font properly on Apple devices
Sudhakar Anivella
Reported
2023-12-22 19:46:31 PST
Created
attachment 469183
[details]
SVG textpath unicode rendering issue screenshots and sample SVG I am trying to render Unicode text (Telugu) on a curved SVG textPath. Text on textPath is not rendered properly on apple devices (Safari as well as Chrome), characters do not cluster as expected, where as text using rendered properly. Where as same SVG code is workign as expected with Firefox and Chrome on Linux. Refer attached screenshots and SVG sample.
Attachments
SVG textpath unicode rendering issue screenshots and sample SVG
(176.29 KB, application/zip)
2023-12-22 19:46 PST
,
Sudhakar Anivella
no flags
Details
Testcase
(317.37 KB, image/svg+xml)
2023-12-23 08:26 PST
,
Ahmad Saleem
no flags
Details
rendering in safari, firefox, chrome
(117.80 KB, image/png)
2023-12-24 20:16 PST
,
Karl Dubost
no flags
Details
Test Case - as attachment
(317.36 KB, image/svg+xml)
2024-09-14 07:04 PDT
,
Ahmad Saleem
no flags
Details
rendering in safari, firefox, chrome
(558.81 KB, image/png)
2026-06-26 08:57 PDT
,
Karl Dubost
no flags
Details
testcase-probe
(1.94 KB, text/html)
2026-06-26 09:41 PDT
,
Karl Dubost
no flags
Details
Show Obsolete
(1)
View All
Add attachment
proposed patch, testcase, etc.
Ahmad Saleem
Comment 1
2023-12-23 08:26:48 PST
Created
attachment 469190
[details]
Testcase
Sudhakar Anivella
Comment 2
2023-12-23 19:16:23 PST
Comment on
attachment 469190
[details]
Testcase @ahmad.saleem792 I think testcase_266832.svg contains first line as blank line. You may want to remove it to prevent "XML Parsing Error: XML or text declaration not at start of entity"
Ahmad Saleem
Comment 3
2023-12-23 19:17:06 PST
(In reply to Sudhakar Anivella from
comment #2
)
> Comment on
attachment 469190
[details]
> Testcase > > @ahmad.saleem792 I think testcase_266832.svg contains first line as blank > line. You may want to remove it to prevent "XML Parsing Error: XML or text > declaration not at start of entity"
Yes - I marked it as 'obsolete' and added 'JSFiddle' in URL field. :-)
Sudhakar Anivella
Comment 4
2023-12-23 19:39:56 PST
Thank you!
Karl Dubost
Comment 5
2023-12-24 20:16:27 PST
Created
attachment 469195
[details]
rendering in safari, firefox, chrome Safari Technology Preview 184 19618.1.7.1.1 Firefox Nightly 123.0a1 12323.12.24 Google Chrome Canary 122.0.6205.0 6205.0
Radar WebKit Bug Importer
Comment 6
2023-12-29 19:47:12 PST
<
rdar://problem/120284006
>
Ahmad Saleem
Comment 7
2024-09-14 07:04:58 PDT
Created
attachment 472555
[details]
Test Case - as attachment
kyle.hensel
Comment 8
2026-06-26 06:28:21 PDT
another excellent test case is
https://mapmeld.com/text-path-problems
Karl Dubost
Comment 9
2026-06-26 08:57:31 PDT
Created
attachment 480219
[details]
rendering in safari, firefox, chrome Safari Technology Preview 26.0 (Release 246) 22625.1.20.19.1 Firefox Nightly 154.0a1 15426.6.25 Google Chrome Canary 151.0.7912.0 7912.0
Karl Dubost
Comment 10
2026-06-26 08:59:57 PDT
Vitor, Elika this last testcase has potentially interesting things.
Karl Dubost
Comment 11
2026-06-26 09:22:54 PDT
Ah this is about measurements relative to code point. in WebKit, on a path every character starts a new fragment WebKit needs to treat the text as a series of clusters and just not individual code point, with all their own width.
Karl Dubost
Comment 12
2026-06-26 09:41:00 PDT
Created
attachment 480220
[details]
testcase-probe And we can use diff --git a/Source/WebCore/rendering/svg/SVGTextBoxPainter.cpp b/Source/WebCore/rendering/svg/SVGTextBoxPainter.cpp index 94a33af7038a..b6645ceae273 100644 --- a/Source/WebCore/rendering/svg/SVGTextBoxPainter.cpp +++ b/Source/WebCore/rendering/svg/SVGTextBoxPainter.cpp @@ -672,6 +672,13 @@ void SVGTextBoxPainter<TextBoxPath>::paintText(const Style::ComputedStyle& style TextRun constructTextRun(StringView text, TextDirection direction, const Style::ComputedStyle& style, const SVGTextFragment& fragment) { + //
BUG266832
-DEBUG: confirm the painter re-shapes one code point at a time on a textPath. + // A lone combining mark here (length==1, U+09xx vowel sign etc.) is what makes the font emit a dotted circle. + auto debugSub = text.substring(fragment.characterOffset, fragment.length); + WTFLogAlways("[
BUG266832
-PAINT] charOffset=%u length=%u first=U+%04X last=U+%04X", + fragment.characterOffset, fragment.length, + debugSub.length() ? debugSub[0] : 0, debugSub.length() ? debugSub[debugSub.length() - 1] : 0); + TextRun run(text.substring(fragment.characterOffset, fragment.length), 0, /* xPos, only relevant with allowTabs=true */ 0, /* padding, only relevant for justified text, not relevant for SVG */ diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp index 74c2799243af..a8af418d909e 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp +++ b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp @@ -132,6 +132,11 @@ void SVGTextLayoutEngine::recordTextFragment(InlineIterator::SVGTextBoxIterator return Vector<SVGTextFragment> { }; }).iterator->value; + //
BUG266832
-DEBUG: confirm path layout emits one single-character fragment per code point. + WTFLogAlways("[
BUG266832
-LAYOUT] inPath=%d charOffset=%u length=%u metricsOffset=%u x=%.1f y=%.1f width=%.2f", + m_inPathLayout, m_currentTextFragment.characterOffset, m_currentTextFragment.length, + m_currentTextFragment.metricsListOffset, m_currentTextFragment.x, m_currentTextFragment.y, m_currentTextFragment.width); + fragments.append(m_currentTextFragment); m_currentTextFragment = SVGTextFragment(); }
Karl Dubost
Comment 13
2026-06-26 09:42:01 PDT
It confirms the issue. [
BUG266832
-LAYOUT] inPath=0 charOffset=0 length=6 metricsOffset=0 x=0.0 y=0.0 width=57.88 [
BUG266832
-LAYOUT] inPath=1 charOffset=0 length=1 metricsOffset=0 x=30.2 y=112.8 width=14.36 [
BUG266832
-LAYOUT] inPath=1 charOffset=1 length=1 metricsOffset=1 x=31.5 y=97.6 width=16.21 [
BUG266832
-LAYOUT] inPath=1 charOffset=2 length=1 metricsOffset=2 x=33.6 y=85.1 width=9.10 [
BUG266832
-LAYOUT] inPath=1 charOffset=3 length=1 metricsOffset=3 x=35.7 y=76.3 width=9.10 [
BUG266832
-LAYOUT] inPath=1 charOffset=4 length=1 metricsOffset=4 x=38.3 y=67.5 width=9.10 [
BUG266832
-LAYOUT] inPath=1 charOffset=5 length=1 metricsOffset=5 x=39.7 y=63.2 width=0.00 [
BUG266832
-LAYOUT] inPath=0 charOffset=0 length=6 metricsOffset=0 x=20.0 y=45.0 width=57.88 [
BUG266832
-LAYOUT] inPath=0 charOffset=0 length=6 metricsOffset=0 x=0.0 y=0.0 width=46.72 [
BUG266832
-LAYOUT] inPath=1 charOffset=0 length=1 metricsOffset=0 x=30.0 y=116.1 width=7.79 [
BUG266832
-LAYOUT] inPath=1 charOffset=1 length=1 metricsOffset=1 x=30.4 y=108.3 width=7.79 [
BUG266832
-LAYOUT] inPath=1 charOffset=2 length=1 metricsOffset=2 x=31.1 y=100.6 width=7.79 [
BUG266832
-LAYOUT] inPath=1 charOffset=3 length=1 metricsOffset=3 x=32.2 y=92.9 width=7.79 [
BUG266832
-LAYOUT] inPath=1 charOffset=4 length=1 metricsOffset=4 x=33.6 y=85.2 width=7.79 [
BUG266832
-LAYOUT] inPath=1 charOffset=5 length=1 metricsOffset=5 x=35.3 y=77.6 width=7.79 [
BUG266832
-PAINT] charOffset=0 length=1 first=U+0065 last=U+0065 [
BUG266832
-PAINT] charOffset=1 length=1 first=U+0301 last=U+0301 [
BUG266832
-PAINT] charOffset=2 length=1 first=U+0065 last=U+0065 [
BUG266832
-PAINT] charOffset=3 length=1 first=U+0301 last=U+0301 [
BUG266832
-PAINT] charOffset=4 length=1 first=U+0065 last=U+0065 [
BUG266832
-PAINT] charOffset=5 length=1 first=U+0301 last=U+0301 [
BUG266832
-PAINT] charOffset=0 length=1 first=U+0928 last=U+0928 [
BUG266832
-PAINT] charOffset=1 length=1 first=U+092E last=U+092E [
BUG266832
-PAINT] charOffset=2 length=1 first=U+0938 last=U+0938 [
BUG266832
-PAINT] charOffset=3 length=1 first=U+094D last=U+094D [
BUG266832
-PAINT] charOffset=4 length=1 first=U+0924 last=U+0924 [
BUG266832
-PAINT] charOffset=5 length=1 first=U+0947 last=U+0947 [
BUG266832
-PAINT] charOffset=0 length=6 first=U+0928 last=U+0947 [
BUG266832
-LAYOUT] inPath=0 charOffset=0 length=6 metricsOffset=0 x=0.0 y=0.0 width=57.88 [
BUG266832
-LAYOUT] inPath=1 charOffset=0 length=1 metricsOffset=0 x=30.2 y=112.8 width=14.36 [
BUG266832
-LAYOUT] inPath=1 charOffset=1 length=1 metricsOffset=1 x=31.5 y=97.6 width=16.21 [
BUG266832
-LAYOUT] inPath=1 charOffset=2 length=1 metricsOffset=2 x=33.6 y=85.1 width=9.10 [
BUG266832
-LAYOUT] inPath=1 charOffset=3 length=1 metricsOffset=3 x=35.7 y=76.3 width=9.10 [
BUG266832
-LAYOUT] inPath=1 charOffset=4 length=1 metricsOffset=4 x=38.3 y=67.5 width=9.10 [
BUG266832
-LAYOUT] inPath=1 charOffset=5 length=1 metricsOffset=5 x=39.7 y=63.2 width=0.00 [
BUG266832
-LAYOUT] inPath=0 charOffset=0 length=6 metricsOffset=0 x=20.0 y=45.0 width=57.88 [
BUG266832
-LAYOUT] inPath=0 charOffset=0 length=6 metricsOffset=0 x=0.0 y=0.0 width=46.72 [
BUG266832
-LAYOUT] inPath=1 charOffset=0 length=1 metricsOffset=0 x=30.0 y=116.1 width=7.79 [
BUG266832
-LAYOUT] inPath=1 charOffset=1 length=1 metricsOffset=1 x=30.4 y=108.3 width=7.79 [
BUG266832
-LAYOUT] inPath=1 charOffset=2 length=1 metricsOffset=2 x=31.1 y=100.6 width=7.79 [
BUG266832
-LAYOUT] inPath=1 charOffset=3 length=1 metricsOffset=3 x=32.2 y=92.9 width=7.79 [
BUG266832
-LAYOUT] inPath=1 charOffset=4 length=1 metricsOffset=4 x=33.6 y=85.2 width=7.79 [
BUG266832
-LAYOUT] inPath=1 charOffset=5 length=1 metricsOffset=5 x=35.3 y=77.6 width=7.79 [
BUG266832
-PAINT] charOffset=0 length=1 first=U+0065 last=U+0065 [
BUG266832
-PAINT] charOffset=1 length=1 first=U+0301 last=U+0301 [
BUG266832
-PAINT] charOffset=2 length=1 first=U+0065 last=U+0065 [
BUG266832
-PAINT] charOffset=3 length=1 first=U+0301 last=U+0301 [
BUG266832
-PAINT] charOffset=4 length=1 first=U+0065 last=U+0065 [
BUG266832
-PAINT] charOffset=5 length=1 first=U+0301 last=U+0301 [
BUG266832
-PAINT] charOffset=0 length=1 first=U+0928 last=U+0928 [
BUG266832
-PAINT] charOffset=1 length=1 first=U+092E last=U+092E [
BUG266832
-PAINT] charOffset=2 length=1 first=U+0938 last=U+0938 [
BUG266832
-PAINT] charOffset=3 length=1 first=U+094D last=U+094D [
BUG266832
-PAINT] charOffset=4 length=1 first=U+0924 last=U+0924 [
BUG266832
-PAINT] charOffset=5 length=1 first=U+0947 last=U+0947 [
BUG266832
-PAINT] charOffset=0 length=6 first=U+0928 last=U+0947 [
BUG266832
-PAINT] charOffset=0 length=1 first=U+0928 last=U+0928 [
BUG266832
-PAINT] charOffset=1 length=1 first=U+092E last=U+092E [
BUG266832
-PAINT] charOffset=2 length=1 first=U+0938 last=U+0938 [
BUG266832
-PAINT] charOffset=3 length=1 first=U+094D last=U+094D [
BUG266832
-PAINT] charOffset=4 length=1 first=U+0924 last=U+0924 [
BUG266832
-PAINT] charOffset=5 length=1 first=U+0947 last=U+0947 [
BUG266832
-PAINT] charOffset=0 length=6 first=U+0928 last=U+0947 [
BUG266832
-PAINT] charOffset=0 length=1 first=U+0065 last=U+0065 [
BUG266832
-PAINT] charOffset=1 length=1 first=U+0301 last=U+0301 [
BUG266832
-PAINT] charOffset=2 length=1 first=U+0065 last=U+0065 [
BUG266832
-PAINT] charOffset=3 length=1 first=U+0301 last=U+0301 [
BUG266832
-PAINT] charOffset=4 length=1 first=U+0065 last=U+0065 [
BUG266832
-PAINT] charOffset=5 length=1 first=U+0301 last=U+0301 [
BUG266832
-PAINT] charOffset=0 length=1 first=U+0065 last=U+0065 [
BUG266832
-PAINT] charOffset=1 length=1 first=U+0301 last=U+0301 [
BUG266832
-PAINT] charOffset=2 length=1 first=U+0065 last=U+0065 [
BUG266832
-PAINT] charOffset=3 length=1 first=U+0301 last=U+0301 [
BUG266832
-PAINT] charOffset=4 length=1 first=U+0065 last=U+0065 [
BUG266832
-PAINT] charOffset=5 length=1 first=U+0301 last=U+0301 [
BUG266832
-PAINT] charOffset=0 length=1 first=U+0928 last=U+0928 [
BUG266832
-PAINT] charOffset=1 length=1 first=U+092E last=U+092E [
BUG266832
-PAINT] charOffset=2 length=1 first=U+0938 last=U+0938 [
BUG266832
-PAINT] charOffset=3 length=1 first=U+094D last=U+094D [
BUG266832
-PAINT] charOffset=4 length=1 first=U+0924 last=U+0924 [
BUG266832
-PAINT] charOffset=5 length=1 first=U+0947 last=U+0947 [
BUG266832
-PAINT] charOffset=0 length=6 first=U+0928 last=U+0947
Karl Dubost
Comment 14
2026-06-26 09:57:01 PDT
1. One fragment per code point on a path six length=1 fragments at six distinct arc positions, vs a single length=6 fragment for plain text. (Predicted from SVGTextLayoutEngine.cpp:633.) 2. "Correct width" explained 14.36+16.21+9.10+9.10+9.10+0.00 = 57.87 ≈ 57.88 plain. Cluster advance preserved in total, smeared across code points, each planted at its own offset. 3. Dotted circles caught at the source U+094D (virama) and U+0947 (vowel sign E) handed to the shaper alone; plain text shapes all six together in one call. (Confirms SVGTextBoxPainter.cpp:675.) 4. Latin é shows the identical split same defect, subtler symptom.
Karl Dubost
Comment 15
2026-06-26 10:02:42 PDT
WebKit places code points today instead it must place typographic characters.
https://w3c.github.io/svgwg/svg2-draft/single-page.html#text-TSpanNotes
> The ‘x’, ‘y’, ‘dx’, ‘dy’, and ‘rotate’ on the ‘text’ and ‘tspan’ elements are useful in high-end typography scenarios where individual glyphs require exact placement.
Then the spec defines a set of rules for each cases.
Karl Dubost
Comment 16
2026-06-29 01:35:02 PDT
Pull request:
https://github.com/WebKit/WebKit/pull/68085
EWS
Comment 17
2026-06-30 07:21:11 PDT
Committed
316144@main
(ef2bac73ff93): <
https://commits.webkit.org/316144@main
> Reviewed commits have been landed. Closing PR #68085 and removing active labels.
Karl Dubost
Comment 18
2026-06-30 07:28:16 PDT
Submitted web-platform-tests pull request:
https://github.com/web-platform-tests/wpt/pull/60996
Karl Dubost
Comment 19
2026-08-17 02:06:58 PDT
Similar issue on
Bug 73354
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug