9fc03e28f7
VT323, Alex Brush, Tilt Warp, Hahmlet, Pinyon Script, Allura, Dancing Script and Amatic SC join the two the bundle already carried, each subset to the same latin / latin-ext / vietnamese ranges and pinned to its upright 400 instance where it is variable.
77 lines
3.8 KiB
TypeScript
77 lines
3.8 KiB
TypeScript
import { initSkia, hasGpu } from './skiaShim';
|
|
import type { RenderAssets } from './exportEngine';
|
|
|
|
// One CanvasKit instance per page, one copy of the render assets. The wasm file
|
|
// is served from /wasm/ (scripts/copy-wasm.mjs puts it there at build time); the
|
|
// fonts and the wall-frame artwork are in public/assets.
|
|
let assets: RenderAssets | null = null;
|
|
|
|
// The faces the watermarks' FONT option offers beside DEFAULT. There is no OS
|
|
// font service in a browser, so the list is exactly what the bundle carries —
|
|
// and each family is ONE file, because drawText has no glyph fallback: a name
|
|
// mapped to only the latin subset would print a Vietnamese place name as tofu.
|
|
// The first two are the site's own self-hosted faces (styles/fonts.css, SIL OFL),
|
|
// with their latin / latin-ext / vietnamese woff2 subsets decompressed, pinned
|
|
// to weight 400 @ opsz 14 and merged into one TTF per family (fontTools).
|
|
// The rest come from google/fonts (SIL OFL), subset to the same latin /
|
|
// latin-ext / vietnamese ranges with pyftsubset (fontTools); the variable ones
|
|
// (Hahmlet, Dancing Script @ wght 400, Tilt Warp @ XROT 0 YROT 0) are pinned to
|
|
// that one named instance first. Every file keeps whatever glyph its source had
|
|
// inside those ranges — no invented ones: Tilt Warp has no U+0111, the rest do.
|
|
export const MARK_FACES: { family: string; file: string }[] = [
|
|
{ family: 'Inter', file: '/assets/Inter-Regular.ttf' },
|
|
{ family: 'Fraunces', file: '/assets/Fraunces-Regular.ttf' },
|
|
{ family: 'VT323', file: '/assets/VT323-Regular.ttf' },
|
|
{ family: 'Alex Brush', file: '/assets/AlexBrush-Regular.ttf' },
|
|
{ family: 'Tilt Warp', file: '/assets/TiltWarp-Regular.ttf' },
|
|
{ family: 'Hahmlet', file: '/assets/Hahmlet-Regular.ttf' },
|
|
{ family: 'Pinyon Script', file: '/assets/PinyonScript-Regular.ttf' },
|
|
{ family: 'Allura', file: '/assets/Allura-Regular.ttf' },
|
|
{ family: 'Dancing Script', file: '/assets/DancingScript-Regular.ttf' },
|
|
{ family: 'Amatic SC', file: '/assets/AmaticSC-Regular.ttf' },
|
|
];
|
|
|
|
// The two frame sheets are drawn by hand and dropped over the file of the same
|
|
// name, and that name is the one thing neither cache lets go of: nginx answers
|
|
// /assets/ immutable for 30 days (nginx.conf) and the service worker serves the
|
|
// path cache-first (sw.js) — both stored for the sheet that was there the day the
|
|
// visitor first loaded the app, so a sheet swapped in today arrives as yesterday's.
|
|
// The build stamp in the query is what makes the URL move with the build; the old
|
|
// entry stays in both caches, unasked for, and ages out on its own.
|
|
const ART = `?v=${import.meta.env.VITE_VERSION ?? ''}`;
|
|
|
|
// The fonts never change under a build, so they keep the plain URL.
|
|
async function buf(url: string): Promise<ArrayBuffer> {
|
|
const res = await fetch(url);
|
|
if (!res.ok) throw new Error(`fetch ${url}: ${res.status}`);
|
|
return res.arrayBuffer();
|
|
}
|
|
|
|
// The engine is only usable on a WebGL-backed surface (see skiaShim.ts), so the
|
|
// app has to know whether it got one: reporting "no WebGL" beats silently
|
|
// spending minutes per photo on the raster fallback.
|
|
export function probeGpu(): boolean {
|
|
return hasGpu();
|
|
}
|
|
|
|
export async function loadEngine(): Promise<{ assets: RenderAssets; gpu: boolean }> {
|
|
await initSkia({ locateFile: (file) => `/wasm/${file}` });
|
|
if (!assets) {
|
|
const [textFont, emojiFont, wallframePng, oldFilmPng, markFonts] = await Promise.all([
|
|
buf('/assets/Cousine-Regular.ttf'),
|
|
buf('/assets/NotoEmoji-GPS.ttf'),
|
|
buf(`/assets/wallframe.png${ART}`),
|
|
buf(`/assets/old_film.png${ART}`),
|
|
Promise.all(MARK_FACES.map(async (face) => ({ family: face.family, bytes: await buf(face.file) }))),
|
|
]);
|
|
assets = {
|
|
textFont,
|
|
emojiFont,
|
|
wallframe: new Uint8Array(wallframePng),
|
|
oldFilm: new Uint8Array(oldFilmPng),
|
|
markFonts,
|
|
};
|
|
}
|
|
return { assets, gpu: probeGpu() };
|
|
}
|