Skip to content

Add automatic fontsource font detection and downloading#246

Merged
jonmmease merged 16 commits intomainfrom
jonmmease/auto-fonts
Mar 7, 2026
Merged

Add automatic fontsource font detection and downloading#246
jonmmease merged 16 commits intomainfrom
jonmmease/auto-fonts

Conversation

@jonmmease
Copy link
Collaborator

@jonmmease jonmmease commented Feb 25, 2026

Summary

Add automatic font detection and downloading from Fontsource during Vega/Vega-Lite conversions. When auto_fontsource is enabled, vl-convert parses the compiled Vega spec to find referenced fonts, checks them against the local fontdb, and downloads missing fonts from Fontsource before rendering.

Builds on PR #245 (fontsource crate + explicit register_fontsource_font() API). This PR adds automatic detection/download plus configurable missing-font handling.

Motivation

With explicit register_fontsource_font(), users must manually identify which fonts a spec references and pre-install them. This is error-prone, especially for theme-driven fonts or specs with many font references.

This PR separates two independent concerns:

  • auto_fontsource (bool) — whether to download missing fonts from Fontsource automatically
  • missing_fonts (policy) — what happens when referenced fonts are unavailable: fallback (silent, default), warn, or error

Behavior Matrix

auto_fontsource missing_fonts Behavior
false fallback Default. No font preprocessing; renderer falls back silently
false warn Validate against local fontdb; warn for each missing font. No network.
false error Validate against local fontdb; fail if any font is missing. No network.
true fallback Download from Fontsource; fall back silently on failures
true warn Download from Fontsource; warn on API/catalog/download failures
true error Download from Fontsource; fail on API/catalog/download failures

Generic CSS families (serif, sans-serif, etc.) are never flagged as missing. For multi-font CSS strings like "Roboto, Arial, sans-serif", only the first entry is checked/downloaded — this matches Vega's rendering behavior, which tries the first font and falls back to system generics.

Backwards compatible: Both options default to off (auto_fontsource: false, missing_fonts: "fallback"), so existing behavior is unchanged.

Changes

New module: vl-convert-rs/src/extract.rs

  • CSS font-family parsing via svgtypes::parse_font_families() (spec-compliant quoting/escaping) with naive-split fallback for non-standard idents
  • Vega spec traversal extracting font references from marks, axes, legends, titles, config, and wordcloud text
  • resolve_first_fonts() classifies each font string's first entry as Available, NeedsDownload, Generic, or Unavailable
  • Unit tests inline (~30 tests) plus integration tests in tests/test_extract.rs

Fontsource client: vl-convert-fontsource/src/client.rs

  • is_known_font() / is_known_font_blocking() — lightweight metadata-only check (no font file downloads)
  • Checks local cache first, falls back to Fontsource API; returns false on 404

Core library: vl-convert-rs/src/converter.rs

  • MissingFontsPolicy enum (Fallback, Warn, Error)
  • auto_fontsource and missing_fonts fields on VlConverterConfig
  • preprocess_fonts() pipeline: extract → check local fontdb → query Fontsource → report/download
  • maybe_compile_vl_with_preprocessed_fonts() for VL→* methods (compiles VL→Vega first to avoid double compilation, preprocesses fonts, renders via Vg→* path)
  • maybe_preprocess_vega_fonts() for Vg→* methods
  • All 12 rendering methods hooked up (6 Vega→*, 6 Vega-Lite→*)

CLI: vl-convert/src/main.rs

  • --auto-fontsource (bool flag)
  • --missing-fonts <fallback|warn|error> (value enum)
  • Both are global flags available on all subcommands

Python bindings: vl-convert-python/src/lib.rs + vl_convert.pyi

  • auto_fontsource and missing_fonts kwargs on configure()
  • Reported by get_converter_config()
  • Type stubs updated
  • pyo3-log integration: Rust log::warn!() / log::error!() messages (missing font warnings, Fontsource API errors) are forwarded to Python's logging module

@jonmmease jonmmease force-pushed the jonmmease/auto-fonts branch 2 times, most recently from ed148bb to 29c9209 Compare February 25, 2026 14:24
@jonmmease jonmmease force-pushed the jonmmease/auto-fonts branch from 29c9209 to d437728 Compare February 25, 2026 14:27
@jonmmease jonmmease changed the title Add auto font download support Add auto font install support Feb 26, 2026
@jonmmease jonmmease force-pushed the jonmmease/fontsource-install branch 2 times, most recently from 8714c5d to 5a77b20 Compare March 4, 2026 19:56
Base automatically changed from jonmmease/fontsource-install to main March 5, 2026 18:25
jonmmease added a commit that referenced this pull request Mar 5, 2026
CSS font-family parser, Vega spec font traversal, and first-font
resolution for the auto_fontsource pipeline. Ported from PR #246.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
jonmmease and others added 6 commits March 5, 2026 16:22
CSS font-family parser, Vega spec font traversal, and first-font
resolution for the auto_fontsource pipeline. Ported from PR #246.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Lightweight metadata-only check (no blob download) to determine if a
font ID exists on Fontsource. Returns true/false, propagating network
errors. Used by the auto_fontsource pipeline to classify fonts before
attempting downloads.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add MissingFontsPolicy enum (Fallback/Warn/Error) and auto_fontsource
flag to VlConverterConfig. Implement preprocess_fonts() pipeline that
extracts font references from Vega specs, checks local availability via
fontdb, queries Fontsource API for missing fonts, and downloads them
on-demand. Hook font preprocessing into all vega_to_* and vegalite_to_*
conversion methods.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add MissingFontsArg enum mapping to MissingFontsPolicy, and wire both
--auto-fontsource and --missing-fonts global flags through all CLI
subcommands into VlConverterConfig.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add auto_fontsource and missing_fonts kwargs to configure(), expose
them in get_converter_config() output, and update vl_convert.pyi type
stubs with both sync and async signatures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jonmmease jonmmease force-pushed the jonmmease/auto-fonts branch from 93a76b3 to 2adfb92 Compare March 5, 2026 21:22
@jonmmease jonmmease changed the title Add auto font install support Add automatic fontsource font detection and downloading Mar 5, 2026
jonmmease and others added 2 commits March 6, 2026 08:27
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jonmmease jonmmease marked this pull request as ready for review March 7, 2026 14:27
jonmmease and others added 8 commits March 7, 2026 09:52
Replace naive comma-splitting with svgtypes::parse_font_families,
which correctly handles quoted names, escaping, and multi-word
unquoted families. Falls back to naive parsing for non-standard
idents that svgtypes rejects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove config keys that don't exist in the Vega spec and can't
appear in compiled Vega output:
- 4 VL-only axis keys (axisDiscrete, axisPoint, axisQuantitative, axisTemporal)
- 4 header config keys (header, headerColumn, headerRow, headerFacet)
- config.font top-level (not in Vega's Config interface)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove parse_css_font_family_naive and strip_quotes helpers — svgtypes
handles all real-world font names; invalid CSS idents now return an
empty list instead of attempting naive parsing. Also remove decorative
section-header comments and add CSS Fonts Level 4 spec link.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ation

Delete tests/test_extract.rs (266 lines) — its 10 tests were a subset
of the 42 inline tests in extract.rs. Moved the 2 unique cases
(whitespace-only input, quoted font with comma) inline. Also removed
section-header comments from the test module.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update docstrings on MissingFontsPolicy enum, its variants,
preprocess_fonts, and the VlConverterConfig field to make clear
that only the first non-generic font in each CSS font-family
string is checked.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…eline

preprocess_fonts() now returns Vec<FontsourceFontRequest> instead of
globally registering fonts via register_fontsource_font(). The caller
adds auto-detected font requests to VgOpts.fontsource_fonts, letting
the existing resolve → with_font_overlay! pipeline handle download and
per-request registration/cleanup. This prevents unbounded memory growth
from auto-downloaded fonts persisting for the process lifetime.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jonmmease jonmmease merged commit 2bc1698 into main Mar 7, 2026
23 of 24 checks passed
@jonmmease jonmmease deleted the jonmmease/auto-fonts branch March 7, 2026 20:24
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.

1 participant