Embed custom fonts into vector exports#463
Open
nul0m wants to merge 1 commit into
Open
Conversation
Add an opt `fonts` (a list of font-file paths) that embeds the given fonts into Kaleido's output so figures render with the correct typeface without the font being installed system-wide. Plotly's SVG output only references fonts by name; it never embeds the font bytes. For PDF/EPS, Kaleido loads that SVG into an isolated <img> document, so an uninstalled font silently falls back (e.g. Times New Roman). This change fixes that end to end: - font_tools.py: parse a .ttf/.otf/.woff file, read its family name from the sfnt `name` table, and build a base64 `@font-face` descriptor. .woff2 (Brotli) and .ttc collections are rejected with actionable errors. - fig_tools.py: accept `fonts` in LayoutOpts and package each path into the spec sent to the render scope. - render.js: load the fonts into the page before rendering (correct text metrics) and inline the `@font-face` (with base64 data) into the SVG so vector output is self-contained and survives the isolated <img> used for PDF/EPS. - Rebuilt vendor/kaleido_scopes.js bundle. - Tests for font parsing, format hints, error paths, and coerce_for_js. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
|
Thanks for the PR! Before we review, could you please create an issue/feature request that this fixes? In that issue, could you please include reproduction steps? In this PR, could you please add testing steps? |
Author
|
Thanks @camdecoster! Done on both:
Happy to adjust anything before review. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #464
Summary
Adds a
fontsimage option (a list of font-file paths) that embeds the given fonts into Kaleido's output, so figures render with the correct typeface without the font being installed system-wide.Why
Plotly's SVG output references fonts by name only — it never embeds the font bytes. For PDF/EPS, Kaleido loads that SVG into an isolated
<img>document, so a font that isn't installed silently falls back (e.g. Times New Roman on Windows). Charts then look different depending on which machine renders them. See #464 for a full reproduction.What changed
_utils/font_tools.py(new): parses a.ttf/.otf/.wofffile, reads its family name straight from the sfntnametable (preferring the typographic family, nameID 16), and builds a base64@font-facedescriptor..woff2(needs Brotli) and.ttccollections are rejected with actionable errors._utils/fig_tools.py:fontsadded toLayoutOpts; each path is packaged into the spec sent to the render scope.src/js/src/plotly/render.js: loads the fonts into the page before rendering (so text is measured with correct metrics), and inlines the@font-face(with base64 data) into the SVG so vector output is self-contained and survives the isolated<img>used for PDF/EPS.vendor/kaleido_scopes.js.tests/test_font_tools.py(new): family extraction (ttf/otf/woff), typographic-vs-basic precedence, format hints,coerce_for_jspackaging, and error paths (missing file, woff2, ttc, unrecognized, missing name table).Testing steps
Automated
Manual end-to-end (before/after reproduction of #464)
Pick any font file (
.ttf/.otf/.woff) whose family is not installed system-wide on your machine and set the two variables below. A distinctive display font such as Pacifico is a safe default on most systems:If you're unsure of the family name, let Kaleido read it from the file:
Render the same figure twice — once without the opt (current behavior) and once with it (the fix):
Verify:
Expected result:
before.pdfembeds a system fallback face (e.g.Times-Roman) —FAMILYis absent: this is the [FEATURE]: Embed custom fonts into vector exports (no system install) #464 bug.after.pdfembeds aFAMILYsubset, andafter.svgcontains an inlined base64@font-face— the fix.before.pdfalready embedsFAMILY, that font is installed on your machine — pick a different font so the contrast is meaningful.Notes / open questions
.ttf/.otf) and.woffv1 are parsed;.woff2requires a Brotli decoder and is rejected — happy to add support if a dependency is acceptable.font_familyis matched automatically.🤖 Generated with Claude Code