diff --git a/.changeset/brand-render-no-system-fonts.md b/.changeset/brand-render-no-system-fonts.md new file mode 100644 index 00000000..97490350 --- /dev/null +++ b/.changeset/brand-render-no-system-fonts.md @@ -0,0 +1,5 @@ +--- +"@codeatlasdev/ui": patch +--- + +`generateBrand` and the brand Vite plugin no longer load every system font for each PNG they render. resvg did that by default on every construction (~450 ms per render on Windows, ~2.7 s per icon set, at every build and dev start); fonts now load only when the mark has `` (#120). diff --git a/packages/ui/src/lib/brand.ts b/packages/ui/src/lib/brand.ts index 326d16f0..095a9f82 100644 --- a/packages/ui/src/lib/brand.ts +++ b/packages/ui/src/lib/brand.ts @@ -109,6 +109,18 @@ export interface GenerateBrandOptions { const SIZES = [16, 32, 48] as const; +/** + * Render options for one PNG. resvg loads every system font on each construction by default + * (~450 ms per render on Windows); a mark drawn with paths needs none, so fonts load only when the + * SVG has text (#120). + */ +export function resvgOptions(svg: string, size: number) { + return { + fitTo: { mode: "width" as const, value: size }, + font: { loadSystemFonts: /.svg`, `favicon..ico` (16/32/48), `favicon-32..png`, `apple-touch-icon..png` * (180, opaque), `icon-192/512..png`, `maskable-512..png` and `manifest..webmanifest`. @@ -146,7 +158,7 @@ export async function generateBrand(o: GenerateBrandOptions): Promise - new Resvg(svg, { fitTo: { mode: "width", value: size } }).render().asPng(); + new Resvg(svg, resvgOptions(svg, size)).render().asPng(); /** Transparent PNG of the mark alone (favicon sizes). */ const bare = (size: number) => png(light.replace(/ { expect(html).toContain("1200px"); }); }); + +describe("resvgOptions", () => { + // resvg loads every system font per render by default: ~450 ms each on Windows, so one icon set + // took ~2.7 s and the generateBrand test timed out there (#120). A mark without text needs none. + it("loads system fonts only for an SVG with text (#120)", () => { + expect(resvgOptions(MARK, 32).font?.loadSystemFonts).toBe(false); + const withText = MARK.replace("", 'A'); + expect(resvgOptions(withText, 32).font?.loadSystemFonts).toBe(true); + expect(resvgOptions(MARK, 180).fitTo).toEqual({ mode: "width", value: 180 }); + }); +});