Skip to content

Draw text with an embedded font, so charts are not blank on Linux - #61

Merged
davidnmbond merged 1 commit into
mainfrom
fix/issue-60-embed-font
Aug 29, 2026
Merged

Draw text with an embedded font, so charts are not blank on Linux#61
davidnmbond merged 1 commit into
mainfrom
fix/issue-60-embed-font

Conversation

@davidnmbond

Copy link
Copy Markdown
Contributor

Fixes #60.

The problem

Charts rendered on Linux contained no text at all — no axis labels, no tick values, no legend text, no titles — while the bars, lines and colours were correct. Same build on Windows: 16,132 bytes against 10,565, the difference being the entire text layer.

The cause, and two fixes that don't work

Not missing fonts. Consumers commonly reference SkiaSharp.NativeAssets.Linux.NoDependencies, a libSkiaSharp built without fontconfig, and with that build SkiaSharp can enumerate no system fonts at all.

It hides well: fc-list and fc-match work perfectly in the same container, because they're fontconfig's own tools and SkiaSharp never consults them. A font can be installed, aliased and confirmed present while being entirely unavailable to the renderer.

Both obvious fixes were measured before this one was written:

  1. Installing more fonts (including the Microsoft core set) — changes nothing. The font manager is empty, not under-stocked.
  2. Naming a font-family — changes nothing either. Probing this Svg.Skia version directly:
no font-family at all   -> 665 px drawn
Arial                   -> 718 px drawn
NoSuchFontXYZ           -> 665 px drawn   (unresolvable still draws - it falls back)
NoSuchFontXYZ, Arial    -> 665 px drawn   (stacks are NOT honoured)

The fix

Carry the font. Liberation Sans embedded, supplied through an ITypefaceProvider registered ahead of the system providers via SKSvgSettings.TypefaceProviders.

Registering it first rather than as a fallback is deliberate: it makes a chart render identically on a developer's machine and on a server. That's worth having on its own, since this output is routinely compared against another renderer and a font difference would read as a rendering difference.

Liberation Sans is metrically compatible with Arial, so label widths and wrapping match the reference renderer.

Licensing: SIL OFL 1.1, which permits embedding and redistribution. The copyright file is included; its GPL-2+ section covers Debian packaging that is not shipped.

The SVG path now always names a font too. Our raster path ignores the attribute — the provider answers every request — but SVG is a public output format, and a browser opening one would otherwise pick its own default and disagree with the PNG of the same chart.

Tests

TextRenderingTests asserts the resource is present, loads with glyphs, and that adding an axis title changes the pixels — with a longer title changing more of them than a shorter one, which is only true if glyphs are really being drawn.

The existing raster tests could not have caught this: they look for many distinct colours and content across the canvas, and a chart with no text satisfies both. That's why this shipped.

Verification

  • Full suite 92/92.
  • Removing the embedded resource fails the two mechanism tests, confirming they bite.
  • The pixel assertions are the red check on Linux CI, which runs the NoDependencies build that fails today — they should fail there without this change and pass with it.

 #60)

Charts rendered on Linux contained no text at all - no axis labels, no tick
values, no legend text, no titles - while the bars, lines and colours were
correct. The same build on Windows rendered fully: 16,132 bytes against 10,565,
the difference being the entire text layer.

The cause is not missing fonts. Consumers commonly reference
SkiaSharp.NativeAssets.Linux.NoDependencies, a libSkiaSharp built without
fontconfig, and with that build SkiaSharp can enumerate no system fonts at all,
so every text node resolves to nothing and draws nothing.

That is unusually well hidden. fc-list and fc-match work perfectly in the same
container - they are fontconfig's own tools and SkiaSharp never consults them -
so a font can be installed, aliased and confirmed present while being entirely
unavailable to the renderer. It also fails silently: a chart missing every label
still looks like a chart.

Two obvious fixes were measured first and neither works. Installing more fonts
changes nothing, because the font manager is empty rather than under-stocked.
Naming a font-family changes nothing either - probing this Svg.Skia version
directly, an unresolvable family still draws via a fallback, and comma-separated
stacks are ignored: "NoSuchFont, Arial" renders identically to "NoSuchFont",
not to Arial.

So carry the font. Liberation Sans is embedded and supplied through an
ITypefaceProvider registered ahead of the system providers, which also makes a
chart render identically on a developer's machine and on a server - worth having
in its own right, since this output is routinely compared against another
renderer and a font difference would read as a rendering difference. It is SIL
OFL 1.1, which permits embedding; the copyright file is included, and its GPL-2+
section covers Debian packaging that is not shipped.

The SVG path now always names a font too. Our raster path ignores the attribute -
the provider answers every request - but SVG is a public output format, and a
browser opening one would otherwise pick its own default and disagree with the
PNG of the same chart.

TextRenderingTests asserts the resource is present, that it loads with glyphs,
and that adding an axis title changes the pixels - with a longer title changing
more of them than a shorter one, which is only true if glyphs are really drawn.
The existing raster tests could not have caught this: they look for many colours
and content across the canvas, and a chart with no text satisfies both.

Full suite 92/92. Removing the embedded resource fails the two mechanism tests,
confirming they bite; the pixel assertions are the red check on Linux CI, which
runs the NoDependencies build that fails today.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@davidnmbond
davidnmbond force-pushed the fix/issue-60-embed-font branch from 8a09168 to 2e92aaa Compare August 29, 2026 16:46
@davidnmbond
davidnmbond merged commit dd52f6d into main Aug 29, 2026
2 checks passed
@davidnmbond
davidnmbond deleted the fix/issue-60-embed-font branch August 29, 2026 16:48
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.

Charts render no text on Linux: SkiaSharp has no font manager, so every glyph is silently dropped

1 participant