Skip to content

fix(render): pass letter-spacing through to the glyph path builder - #25

Open
leshchenko1979 wants to merge 1 commit into
Macawls:mainfrom
leshchenko1979:fix/letter-spacing-render
Open

leshchenko1979 wants to merge 1 commit into
Macawls:mainfrom
leshchenko1979:fix/letter-spacing-render

Conversation

@leshchenko1979

Copy link
Copy Markdown

Problem

letter-spacing is parsed, stored, and even applied when measuring text — but the glyph paths are built with a hardcoded spacing of 0, so the drawn text keeps its unspaced width while layout reserves the wider box.

Reproduced on main (32f3fad), font-size: 20px, text ABCDEFGHIJ:

letter-spacing: 0px   -> text width 253.7
letter-spacing: 10px  -> text width 253.7   (byte-identical SVG)

Chromium gives 94px and 184px for the same declarations.

Cause

font.Measurer.StringWidth does apply letter-spacing, so measurement and rendering disagree by exactly spacing × (n-1). The render side never received the value:

  • font/glyph.go — TextToPathVariations and ShapedTextToPathVariations passed a literal 0 as letterSpacing down to textToPathVariable / textToPathCached.
  • render/png.go — rasterizeShapedPath had no spacing parameter at all, so the PNG output would have diverged from the SVG output even after fixing the SVG path.

Fix

  • font/glyph.go: new TextToPathVariationsSpaced, ShapedTextToPathVariationsSpaced, ShapedRunToPathSpaced and shapedRunToPathVariationsSpaced take a spacing and insert it between glyphs, using the same (n-1) convention the measurer already uses. The existing functions delegate with 0, so current callers keep their behaviour byte for byte.
  • render/text.go, render/svg.go: all four glyph-path call sites now pass cs.LetterSpacing (text shadows, renderTextSegment, appendGlyphClip, renderTextAt).
  • render/png.go: rasterizeShapedPath no longer short-circuits when spacing is non-zero and forwards the value.

Verification

Same DejaVu Sans file handed to both engines:

letter-spacing before after Chromium
0px 84px 84px 94px
10px 84px 96px 106px

The 10px constant offset exists with spacing off too (84 vs 94), so it is a font-metric difference and not part of this bug. The widening itself now matches: +12px on this sample, +90px on a 10-glyph sample — identical in both engines.

Tests added:

  • font/letterspacing_test.go — zero spacing reproduces the old paths byte for byte; spacing widens the path by (n-1) × spacing; a single glyph is unaffected.
  • ogre_letter_spacing_e2e_test.go — through the public Render: 10px widens by 90px, and 0.5em at font-size: 20px yields the same width as 10px.

go build ./..., go vet ./... and go test ./... pass.

Fixes #22

letter-spacing was measured but never drawn: the text layout reserved the
wider box while the glyph paths were built with a hardcoded spacing of 0,
so the rendered text kept its unspaced width and the reserved space stayed
empty.

Reproduced on main (32f3fad), font-size 20px, "ABCDEFGHIJ":

  letter-spacing 0px   text width 253.7
  letter-spacing 10px  text width 253.7   <- identical SVG

font.Measurer.StringWidth did apply the spacing, so measure and render
disagreed by exactly spacing x (n-1).

Changes:

- font/glyph.go: TextToPathVariationsSpaced, ShapedTextToPathVariationsSpaced,
  ShapedRunToPathSpaced and shapedRunToPathVariationsSpaced accept a spacing
  and insert it between glyphs, matching the (n-1) convention already used by
  the measurer. The original functions delegate with 0, so existing callers
  are unaffected.
- render/text.go, render/svg.go: all four glyph-path call sites now pass
  cs.LetterSpacing (shadows, renderTextSegment, appendGlyphClip, renderTextAt).
- render/png.go: rasterizeShapedPath no longer short-circuits when spacing is
  non-zero and forwards the value, so the PNG path matches the SVG path.

Verified against Chromium with the same DejaVu Sans file for both engines:

  spacing 0px   84px  (Chromium 94px)
  spacing 10px  96px  (Chromium 106px)

The remaining 10px offset is a font-metric difference that is present with
spacing off (84 vs 94) and is not caused by this bug; the widening itself now
matches exactly (+12px here, +90px on the 10-glyph case, both engines).

Tests added:

- font/letterspacing_test.go: zero spacing reproduces the old paths byte for
  byte, spacing widens by (n-1) x spacing, a single glyph is unaffected.
- ogre_letter_spacing_e2e_test.go: through the public Render, 10px widens by
  90px, and 0.5em resolves to the same width as 10px.

Fixes Macawls#22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

letter-spacing is measured but not rendered: glyph paths ignore it

1 participant