Skip to content

feat(engine): resolve any google fonts family by name - #40

Open
heristop wants to merge 1 commit into
mainfrom
worktree-google-fonts-resolver
Open

feat(engine): resolve any google fonts family by name#40
heristop wants to merge 1 commit into
mainfrom
worktree-google-fonts-resolver

Conversation

@heristop

Copy link
Copy Markdown
Owner

A font that could not be staged was logged and skipped. A missing font does not stop drawtext — it
draws with a fallback face — so the only symptom was a finished video in the wrong typeface, with a
successful exit code. That silent path is now a hard failure.

Fixing it exposed why fonts kept failing to resolve in the first place: the Google Fonts lookup
rebuilt the family name from the filename, splitting on - and .. BebasNeue.ttf became
family=BebasNeue, which the API rejects with a 400. Multi-word families were unreachable, and the
weight in a name like Roboto-Bold.ttf was silently discarded.

Naming a font by family

font takes a third form anywhere a text sugar accepts one (caption, titleCard line styles,
global.overlays):

"font": "bebas"                                       // registry id — unchanged
"font": "Oswald.ttf"                                  // filename    — unchanged
"font": { "family": "Playfair Display", "weight": 700, "style": "italic" }

It is an object rather than another string on purpose: a typo in a registry id ("bebbas") stays a
local unknown_font validation error instead of becoming a network lookup that fails mid-render.
The trade-off is that family names are not checked at validation time — there is no offline copy of
the catalogue — so a bad family surfaces during the render, naming the family.

How it resolves

Requests go to the css2 endpoint with a legacy User-Agent, because Google keys the response format
off it and a modern UA yields woff2, which drawtext cannot read.

css2 also matters for a subtler reason: for a variable family it returns a static instance cut at
the requested weight
. Fetching the variable .ttf directly — for example from the google/fonts
repo — hands freetype a font it renders at its default instance, so a request for weight 700 would
quietly draw Regular. Verified against the real API: Roboto at 900 comes back as a 124KB static
cut, not the 876KB variable file.

The ref travels beside the staged filename rather than being encoded into it. The slug
(google-playfair-display-700-italic.ttf) is a cache key that is written and never parsed back —
decoding a slug into a family name is lossy for names like Press Start 2P, which is the same class
of guess that caused the original bug.

Staging order, cheapest first: already staged → bundled → persistent cache → catalog → download →
throw.

Caching and platform support

Downloads are copied to ~/.cache/leclap/fonts (override with FVC_FONT_CACHE_DIR), which outlives
the build directory, so a repeat render needs no network and does not re-hit the rate limit.

The browser backend declares no support and rejects the object form up front with an explanation —
it cannot override User-Agent, so it would only ever receive woff2. Web apps ship the fonts they
need instead. The curated 12 stay bundled and pinned everywhere.

Verification

1463/1463 engine tests pass. 31 of them are new, each written before the code and watched fail.
Engine, @leclap/web and @leclap/creative-kit typecheck clean; lint clean.

Resolution was also checked end to end against the live API: multi-word (Playfair Display),
digit-bearing (Press Start 2P), italic, and variable-at-weight-900 all return genuine TrueType
(magic 00010000); a nonexistent family 400s and raises an error naming it.

Known limitations

  • The web font picker only lists the curated registry, so a template using the object form that is
    opened and re-saved in the admin editor has its font replaced by the default. Documented at
    fontIdFromFile; templates using a named family should not be round-tripped through the editor
    until the picker can carry one.
  • The legacy filename-derived lookup is retained for back-compat, so Roboto-Bold.ttf still resolves
    to Roboto at weight 400 and ignores Bold. The object form is the replacement for it.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying leclap with  Cloudflare Pages  Cloudflare Pages

Latest commit: be83ebe
Status: ✅  Deploy successful!
Preview URL: https://4819af27.leclap.pages.dev
Branch Preview URL: https://worktree-google-fonts-resolv.leclap.pages.dev

View logs

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