Add font embedding to HTML export and vegalite_fonts/vega_fonts API#247
Draft
jonmmease wants to merge 5 commits intojonmmease/auto-fontsfrom
Draft
Add font embedding to HTML export and vegalite_fonts/vega_fonts API#247jonmmease wants to merge 5 commits intojonmmease/auto-fontsfrom
jonmmease wants to merge 5 commits intojonmmease/auto-fontsfrom
Conversation
136fc70 to
f9467f4
Compare
617b385 to
ed148bb
Compare
f9467f4 to
b911ee6
Compare
ed148bb to
29c9209
Compare
b911ee6 to
c61a02d
Compare
29c9209 to
d437728
Compare
c61a02d to
0faf83e
Compare
Add a new `embed_local_fonts: bool` config flag (default false) that enables embedding locally-available fonts (system, --font-dir, vendored) as inline @font-face CSS with base64-encoded WOFF2 data in HTML output. Key changes: - Add FontSource enum (Fontsource/Local) to distinguish font origins - Extract subset_and_encode_bytes for in-memory font data from fontdb - Query fontdb for local font faces by family/weight/style - Handle CFF/TTC subset failures via missing_fonts policy - Local fonts get inline @font-face CSS even in CDN mode (no CDN URL) - Propagate to CLI (--embed-local-fonts) and Python bindings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add vegalite_fonts() and vega_fonts() as public methods on VlConverter with a FontFormat enum (name, url, link_tag, import_rule, font_face). Refactor vegalite_to_html/vega_to_html to use vega_fonts() internally. Replace generate_font_tags() with per-font functions (font_cdn_url, font_link_tag, font_import_rule). Change generate_font_face_css() to return Vec<String> for per-block access. Add Python bindings (sync + async) and type stubs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.
Summary
Connect the font pipeline from PRs 1 and 2 to the existing HTML export methods (
vegalite_to_html/vega_to_html). Previously,auto_install_fontshad no effect on HTML output — fonts were referenced by name but never included.This PR adds:
bundle=false): auto-installed fonts get<link>tags pointing to Google Fonts API or Fontsource jsDelivr CDNbundle=true): auto-installed fonts are subsetted to only the characters actually rendered, encoded as WOFF2, and inlined as base64@font-faceCSS — producing fully self-contained HTMLembed_local_fontsoption for embedding system-installed fonts discovered via fontdbvegalite_fonts()/vega_fonts()public API for retrieving font information in multiple formats, usable alongsidejavascript_bundle()for building custom HTMLMotivation
The existing HTML export produces
<script>tags for the Vega runtime but has no font handling at all. Even withauto_install_fonts=true, the HTML output references font families by name without including any font data or CDN links. This means bundled HTML depends on the viewer's system fonts and unbundled HTML has no way to load the correct fonts.Users building custom HTML with
javascript_bundle()also had no way to get font information for their charts. The newvegalite_fonts/vega_fontsAPI exposes font data as composable building blocks.Changes
New module:
font_embed.rsFont subsetting and WOFF2 encoding pipeline:
aggregate_chars_by_font_key()— collects characters used per (font, weight, style) from the scenegraph text extractionsubset_and_encode()/subset_and_encode_bytes()— subsets TTF files to required glyphs and encodes as base64 WOFF2generate_font_face_css()— orchestrates the pipeline, dispatching to Fontsource-cached or fontdb-local font paths; returnsVec<String>of individual@font-faceblocksAdditions to
html.rsPer-font formatting functions (replacing the earlier
generate_font_tags):font_cdn_url()— CDN stylesheet URL (Google Fonts API or Fontsource jsDelivr). ReturnsNonefor local fontsfont_link_tag()— wraps URL in<link rel="stylesheet">font_import_rule()— wraps URL in@import url(...)Public API:
vegalite_fonts/vega_fonts(converter.rs)New methods on
VlConverterthat returnVec<String>of font information in a requested format:FontFormat::Name— font family names (["Roboto", "Open Sans"])FontFormat::Url— CDN stylesheet URLsFontFormat::LinkTag—<link rel="stylesheet">tagsFontFormat::ImportRule—@import url(...)CSS rulesFontFormat::FontFace—@font-faceblocks with subsetted base64 WOFF2 dataEach function accepts
auto_install_fontsandembed_local_fontsparameters (defaulting to converter config).vegalite_fontscompiles VL→Vega, then delegates tovega_fonts. Local fonts returnNonefor url/link_tag/import_rule formats (filtered out), and only appear innameandfont_faceformats.HTML export refactored to use
vega_fontsvegalite_to_html/vega_to_htmlnow usevega_fonts()internally:FontFormat::FontFaceFontFormat::LinkTagfor auto-installed fonts, plus optional second call withFontFormat::FontFacefor local fonts whenembed_local_fontsis setOther converter changes
vega_to_text_by_font()— new method + embedded JS function that walks the rendered Vega scenegraph, collecting unique characters per (font, weight, style) tuplepreprocess_fontsextended to collect locally-available fonts whenembed_local_fontsis setData model (
extract.rs)FontForHtmlrefactored: flatfont_id/font_typefields replaced withFontSourceenum (Fontsource { font_id, font_type }|Local)CLI and Python bindings
--embed-local-fontsglobal CLI flagembed_local_fontsparameter in Pythonconfigure_converter()andConverterConfigvegalite_fonts()/vega_fonts()Python functions (sync + async) withformatparameterFontFormattype alias and full type stubsBuild
font-subsetcrate dependency for TTF subsetting + WOFF2 encodingDesign decisions
embed_local_fontsis opt-in (default false): System fonts may have licensing restrictions. Explicit opt-in avoids accidental embedding.embed_local_fontsis orthogonal toauto_install_fontsandbundle: Local fonts produce inline@font-faceCSS even in CDN mode (they have no CDN URL).font_cdn_url/font_link_tag/font_import_ruleoperate on single fonts for composability. One URL per Google font family (not combined). No preconnect hints.font-subsetcrate only handles TTF/glyf. Failures follow themissing_fontspolicy.fontdb::Databaseis cloned out of theUSVG_OPTIONSmutex so the lock is released before CPU-intensive subsetting.Commits
aebd8c8— Addfont_embed.rsmodule and per-font formatting functions inhtml.rs, with unit tests0faf83e— Integrate font embedding intovegalite_to_html/vega_to_html: addvega_to_text_by_fontJS bridge, extendpreprocess_fonts, wire font CSS intobuild_html2b52123— Addembed_local_fontsconfig option withFontSourceenum, fontdb integration, CLI flag, and Python bindings85885a3— Addvegalite_fonts/vega_fontspublic API withFontFormatenum; refactor HTML export to use it internally; Python bindings and type stubsTesting
pixi run check-rs— cleanpixi run fmt-rs/pixi run fmt-py— cleanpixi run clippy— no new warningspixi run test-rs— 102 passed, 0 failed (includes new unit tests in font_embed.rs, html.rs, converter.rs)pixi run test-cli— 106 passed, 0 failedpixi run test-py— 131 passed, 1 skipped