Repository navigation
perf(producer): renders embed local fonts as-is instead of re-compressing them - #5865
Conversation
…sing them Local ttf/otf fonts in a render were woff2-compressed on every cold font cache, which costs seconds per font. The compiled HTML is a render-time artifact, so embed them raw. fontToDataUri gains a compress: false option; its default is unchanged.
Edit accuracy: accurate 271 (base branch 271), smooth 158 of thoseThe gate passes. Quarantined, measured but not gated (0) Unstable (2)
|
vanceingalls
left a comment
There was a problem hiding this comment.
Reviewed at d5e5abe8. All 8 required checks are green at this head, and the branch merges cleanly with main.
What's solid
- The opt-out is narrow.
fontToDataUrikeeps woff2 as its default, and only the producer's local@font-faceembed (htmlCompiler.ts:1846) passescompress: false. woff2 inputs, ttc, and the file-backed path for fonts overMAX_LOCAL_FONT_DATA_URI_BYTESare unchanged. That limit was already measured on the raw file, so no font crosses it differently now. - With
compress: falsethe raw path skips both the compressor and the cache. The tests pin both, and the PR body shows the frames byte-identical to main.
Important (non-required checks red at this head)
Commentsfails:fontCompression.ts5.0% → 5.8% andhtmlCompiler.ts+2 lines ("A file's comment share may only go down"). The check isn't in the required set, but the PR body ticks "Comments follow CONTRIBUTING.md". The new JSDoc oncompressand the two-line "Raw, not woff2" comment are what trip it.Studio: timeline viewport gatefails against base (interaction p95 66.7 vs 50.0 ms). I don't think it's this change: neither gate fixture (timeline-virtualization,studio-open) has an@font-faceor a font file, and Studio doesn't go through the producer'sembedLocalFontFaces. The gate ran only because the core change sets thestudiopath filter.
Nit
extcan bewoff(/\.(woff2?|ttf|otf|ttc)$/). Withcompress: falseit now goes straight torawMimeType("woff")→font/ttf. Before, it reached the same label only after a failed woff2 compress, so this predates the PR. Chrome sniffs font data, so it still loads, butwoff: "font/woff"inRAW_MIME_TYPESwould label it correctly.
Verdict: APPROVE
Reasoning: The change is correct and its scope is narrow. Every required check is green. The red non-required Comments check and the unrelated Studio perf gate don't block it.
— Via
…ing it in comments
vanceingalls
left a comment
There was a problem hiding this comment.
Re-review at b1f4f296. I approved the previous head, d5e5abe8, earlier.
Delta since d5e5abe8: one commit (b1f4f296) stacked on top of the head I reviewed. It changes 3 files, +4/−7, and only does what it says:
fontCompression.ts:36: the optioncompress?: booleanis renamedcompressToWoff2?: booleanand its doc comment is dropped. The guard at:98now readsoptions.compressToWoff2 === false. Behaviour is unchanged: woff2 passes through, ttc and the opt-out stay raw, and everything else still compresses through the cache.htmlCompiler.ts:1844: the call site moves to{ compressToWoff2: false }, and its two-line comment is removed.fontCompression.test.ts:144: the raw-embed test uses the new key.
No old compress: false/true caller is left anywhere under packages/, so the rename can't silently fall back to the default. It merges cleanly into current main.
CI at b1f4f296: all 8 required checks are green (Build, Render on windows-latest, Semantic PR title, Test, Test: runtime contract, Tests on windows-latest, Typecheck, regression). The Comments ratchet that was red at d5e5abe8 is green now, and so is Studio: timeline viewport gate.
Nit, non-blocking: the option key is now spelled the same as the exported compressToWoff2() function in the same module (:20). That's legal, since one is a property and the other a binding, but a grep for the function now also hits the option.
Verdict: APPROVE
Reasoning: The incremental diff is a mechanical rename plus comment removal with every call site updated, and CI is fully green at this exact head.
— Via
What
Renders now embed a project's local
.ttf/.otffonts into the compiled HTML as-is, instead of re-compressing them to woff2 first.Why
woff2 compression is slow (seconds for a large font) and its cache lives on the local disk, so every fresh render machine pays it again for each local font. The compiled HTML is only a render-time artifact, so the smaller woff2 payload buys nothing there.
Related work
None.
How
fontToDataUri(core) gains acompress: falseoption that returnsdata:font/ttf/data:font/otfwith the original bytes, skipping compression and the cache. Its default is unchanged, so every other caller still gets woff2.@font-faceembed passescompress: false. woff2, ttc and the file-backed path for fonts over 5 MiB are untouched.Test plan
data:font/ttf/data:font/otfwhose decoded bytes equal the files; the font cache dir stays empty). Both fail with the code change reverted..ttfand a CFF.otf(large text in each) to PNG frames with this branch and withmain. Every frame is byte-identical (same sha256); a control render without the fonts differs, so the fonts are in effect. No visible change.main, about 35 ms here (compiled HTML about 310 KB to 570 KB).htmlCompiler.test.ts+htmlCompiler.fontEmbed.test.ts(148 pass), coresrc/fonts/(261 pass), core and producer typecheck clean.