Files
Voiced/src/pdf/templates/catalog.ts
T
xavierk 63422d9060 Add font import for Now, Gotham and Open Sauce One overrides; package fonts for Void
Font import:
- Migration M6 adds user_fonts; files are stored content-addressed under
  the local data dir. New Rust commands inspect, import, list, read and
  remove fonts, refusing WOFF2, collections, variable fonts and
  restricted fsType, and unpacking WOFF1 to plain sfnt.
- Imported fonts register in the render worker as user:<Face> families
  with an italic alias, and are pinned in the frozen render prefs so
  issued invoices can still re-render. Fonts referenced by issued
  invoices are hidden, never deleted.
- Size and tracking compensations tuned for the substitutes apply only
  while the face is not overridden; default output and fingerprint
  goldens are unchanged.
- Settings gets a Fonts section with a licence acknowledgement.

Void packaging:
- Install fonts to /usr/share/fonts/voiced with per-family OFL licences,
  set font_dirs, and add INSTALL/REMOVE scripts that run fc-cache.
  xbps-create carries both scripts (verified in a void-glibc container).
- Fix xbps-create dependency specs in build-xbps.sh.
2026-10-04 13:58:06 +05:30

52 lines
3.0 KiB
TypeScript

/**
* What the template picker shows about each template. Kept free of layout code so the UI can import it
* without pulling react-pdf into the main bundle; every TemplateDefinition takes its identity from here.
*/
export interface TemplateCatalogEntry {
id: string;
name: string;
family: "classic" | "neutral" | "ledger" | "band" | "northline" | "standalone";
/** Static PNG from /public, written by scripts/thumbnails.mjs (sample data, not the user's invoice). */
thumbnail: string;
badge?: "Matches your original format";
/** No Canva placeholder: the logo shows only when the user turns it on. */
logoOptIn?: boolean;
/**
* The licensed faces this template draws (a user-imported font may replace them). Mirrors the roles of the
* template; a test keeps the two in step.
*/
faces?: readonly ("Now" | "Gotham" | "Open Sauce One")[];
}
export const FAMILY_LABELS: Record<TemplateCatalogEntry["family"], string> = {
classic: "Classic",
neutral: "Neutral",
ledger: "Ledger",
band: "Band",
northline: "Northline",
standalone: "Standalone",
};
/** Picker order: Classic first, Linea (the vendor's original format) second. */
export const TEMPLATE_CATALOG: readonly TemplateCatalogEntry[] = [
{ id: "classic", name: "Voiced Standard", family: "classic", thumbnail: "/templates/classic.png" },
{ id: "linea", name: "Linea", family: "neutral", thumbnail: "/templates/linea.png", badge: "Matches your original format", logoOptIn: true },
{ id: "monogram", name: "Monogram", family: "neutral", thumbnail: "/templates/monogram.png" },
{ id: "serenity", name: "Serenity", family: "neutral", thumbnail: "/templates/serenity.png" },
{ id: "crimson-grid", name: "Crimson Grid", family: "ledger", thumbnail: "/templates/crimson-grid.png" },
{ id: "tangerine-ledger", name: "Tangerine Ledger", family: "ledger", thumbnail: "/templates/tangerine-ledger.png" },
{ id: "slate-band", name: "Slate Band", family: "band", thumbnail: "/templates/slate-band.png" },
{ id: "teal-swoosh", name: "Teal Swoosh", family: "band", thumbnail: "/templates/teal-swoosh.png" },
{ id: "purple-pop", name: "Purple Pop", family: "northline", thumbnail: "/templates/purple-pop.png", logoOptIn: true, faces: ["Gotham", "Open Sauce One"] },
{ id: "citrus-split", name: "Citrus Split", family: "northline", thumbnail: "/templates/citrus-split.png", faces: ["Now", "Gotham"] },
{ id: "monolith", name: "Monolith", family: "standalone", thumbnail: "/templates/monolith.png" },
{ id: "cobalt-stripe", name: "Cobalt Stripe", family: "standalone", thumbnail: "/templates/cobalt-stripe.png" },
{ id: "marble", name: "Marble", family: "standalone", thumbnail: "/templates/marble.png", logoOptIn: true, faces: ["Now"] },
{ id: "highlighter", name: "Highlighter", family: "standalone", thumbnail: "/templates/highlighter.png", logoOptIn: true },
];
/** The entry for an id; an unknown id falls back to Classic, like getTemplate. */
export function catalogEntry(id: string | undefined): TemplateCatalogEntry {
return TEMPLATE_CATALOG.find((e) => e.id === id) ?? TEMPLATE_CATALOG[0];
}