Files
Voiced/src/pdf/model/prefs.ts
T
xavierk aa7a407944 Add the Neutral template family, template picker and text-only logo allowances
Linea (the vendor's original format.pdf design), Monogram and Serenity are
rebuilt from the reference PDFs as one parametric family on the engine, with
the user's logo in the measured slot, a home for every GST field and
A4/Letter support. Adds static Montserrat, Open Sans and Poppins fonts and
a generated Ms Madi outline for Serenity's vertical word. Text-only logo
placeholders (Monolith, Tangerine) use a doc-derived allowance box. A
Carbon TemplatePicker with committed thumbnails opens from the page
setup controls.
2026-10-04 07:27:12 +05:30

56 lines
2.1 KiB
TypeScript

import type { MarginPreset, PageId } from "../engine/geometry";
/** Page and template choices frozen with an invoice at issue (stored as JSON). */
export interface RenderPrefsV1 {
version: 1;
templateId: string;
pageSize: PageId;
margins: MarginPreset;
logo?: {
/** The logo already contains the business name, so a template drops its own name lockup. */
useWordmark?: boolean;
/** Templates without a reference logo placeholder (Linea, ...) show the logo only when this is true. */
show?: boolean;
};
}
export const DEFAULT_PREFS: RenderPrefsV1 = {
version: 1,
templateId: "classic",
pageSize: "A4",
margins: "template",
};
const PAGE_SIZE_IDS: readonly PageId[] = ["A4", "LETTER"];
const MARGIN_IDS: readonly MarginPreset[] = ["template", "narrow", "normal"];
/** Tolerant: anything unreadable or out of range falls back to the default for that field. */
export function parsePrefs(json: string | null | undefined): RenderPrefsV1 {
if (!json) return { ...DEFAULT_PREFS };
let raw: unknown;
try {
raw = JSON.parse(json);
} catch {
return { ...DEFAULT_PREFS };
}
if (typeof raw !== "object" || raw === null) return { ...DEFAULT_PREFS };
const o = raw as Record<string, unknown>;
const prefs: RenderPrefsV1 = {
version: 1,
templateId: typeof o.templateId === "string" && o.templateId ? o.templateId : DEFAULT_PREFS.templateId,
pageSize: PAGE_SIZE_IDS.includes(o.pageSize as PageId) ? (o.pageSize as PageId) : DEFAULT_PREFS.pageSize,
margins: MARGIN_IDS.includes(o.margins as MarginPreset) ? (o.margins as MarginPreset) : DEFAULT_PREFS.margins,
};
const logo = o.logo;
if (typeof logo === "object" && logo !== null) {
const { useWordmark, show } = logo as { useWordmark?: unknown; show?: unknown };
const out: NonNullable<RenderPrefsV1["logo"]> = {};
if (typeof useWordmark === "boolean") out.useWordmark = useWordmark;
if (typeof show === "boolean") out.show = show;
if (Object.keys(out).length > 0) prefs.logo = out;
}
return prefs;
}
export const prefsToJson = (prefs: RenderPrefsV1): string => JSON.stringify(prefs);