import type { Sides } from "../../engine/geometry"; import type { TypeToken } from "../../fonts/roles"; import type { GapRange } from "../neutral/variant"; /** * Gotham (commercial) is drawn with Montserrat, which runs about 4% wider at the same size. Every Gotham-derived * size is multiplied by this when a token is defined, so the substitute takes the width the design has. A real * Gotham imported over the licensed name takes the design size back: each such token carries it as `ownFont.size`. */ export const GOTHAM_SIZE = 0.96; const round2 = (n: number) => Math.round(n * 100) / 100; /** The size a Gotham design value takes in Montserrat. */ export const gotham = (designSize: number) => round2(designSize * GOTHAM_SIZE); /** Type roles of Purple Pop (DM Sans, Gotham -> Montserrat, the wordmark in Poppins 800). */ export interface PurpleRoles { /** The oversized "Invoice" wordmark. */ wordmark: TypeToken; /** "TAX" above the wordmark on a tax invoice. */ taxCaption: TypeToken; brand: TypeToken; /** "INVOICE NO : 012345" in the grey pill. */ pill: TypeToken; /** "BILLED TO:", "AMOUNT DUE:", "PAYMENT METHOD". */ label: TypeToken; clientName: TypeToken; clientLine: TypeToken; amount: TypeToken; date: TypeToken; tableHead: TypeToken; tableBody: TypeToken; tableSub: TypeToken; totalLabel: TypeToken; totalValue: TypeToken; grand: TypeToken; grandValue: TypeToken; words: TypeToken; wordsLabel: TypeToken; termsLabel: TypeToken; bullet: TypeToken; bank: TypeToken; contact: TypeToken; /** GSTIN and PAN under the vendor name. */ small: TypeToken; fine: TypeToken; signName: TypeToken; footer: TypeToken; } /** Type roles of Citrus Split (Now -> Jost for the wordmark, DM Sans, Gotham -> Montserrat). */ export interface CitrusRoles { title: TypeToken; taxCaption: TypeToken; brand: TypeToken; metaLabel: TypeToken; metaValue: TypeToken; metaStrong: TypeToken; billedLabel: TypeToken; clientName: TypeToken; clientLine: TypeToken; headService: TypeToken; headNum: TypeToken; tableBody: TypeToken; tableSub: TypeToken; totalLabel: TypeToken; totalValue: TypeToken; grand: TypeToken; grandValue: TypeToken; words: TypeToken; wordsLabel: TypeToken; sectionLabel: TypeToken; bank: TypeToken; bullet: TypeToken; contact: TypeToken; small: TypeToken; fine: TypeToken; signName: TypeToken; footer: TypeToken; } export type NorthlineId = "purple-pop" | "citrus-split"; interface Common { margins: Sides; marginFloor: Sides; signatureImageH: number; chromeLeftW: number; } /** * Purple Pop: y values are design distances measured on the reference A4 page (docs/templates/12-purple-pop.md) * converted to flow gaps; x values are relative to the content edge (the left margin, x 54.8), so the same * tokens serve every margin preset and both page sizes. */ export interface PurpleVariant extends Common { id: "purple-pop"; roles: PurpleRoles; ink: string; purple: string; yellow: string; grey: string; /** Alternate item row fill (the grey at 68% on cream, flattened). */ rowGrey: string; cream: string; watermark: string; /** Header: inset of the vendor name and of the pill's right edge from the content edges. */ head: { nameInset: number; pillInset: number; pillH: number; pillPadX: number; nameMaxW: number; minNameW: number }; /** Client column: label inset, pill padding, the column's width and the gap to the wordmark. */ client: { inset: number; pillH: number; pillPadX: number; colW: number; colGap: number; wordmarkInset: number }; rule: { w: number; left: number; right: number }; /** Amount due / dates band. */ band: { amountInset: number; datesInset: number; gap: number }; /** Table: left offset of the narrow variant, the right inset, the pill paddings and the column widths. */ table: { narrowLeft: number; fullLeft: number; right: number; inset: number; headH: number; rowH: number; radiusHead: number; radiusRow: number; cols: { qty: number; rate: number; amount: number }; narrowDescMin: number }; asterisk: { size: number; offsetX: number; offsetY: number }; dome: { x: number; w: number }; totals: { boxW: number; labelInset: number; valueInset: number; rowGap: number; pillH: number; pillValueInset: number; wordsGap: number }; terms: { labelInset: number; labelW: number; signW: number; besideMax: number }; footer: { ruleLeft: number; ruleRight: number; bankW: number; contactLeft: number; contactW: number; addressLeft: number; iconW: number; iconGap: number }; gaps: { /** Header row (or its GSTIN/PAN lines) to the client row. */ client: GapRange; rule: GapRange; band: GapRange; table: GapRange; tail: GapRange; termsAbove: number; ruleGap: number; }; } /** * Citrus Split (docs/templates/13-citrus-split.md). x values are relative to the content edge (the left * margin, x 73.3 on the reference page); the grey stripe is anchored to the RIGHT margin, so on Letter the extra * width goes to the left column. */ export interface CitrusVariant extends Common { id: "citrus-split"; roles: CitrusRoles; ink: string; text: string; blush: string; panel: string; orange: string; label: string; /** Orange of the icon glyphs. */ icon: string; stripeW: number; /** The header's panel cell: inset of the logo from the stripe's left edge, and the gap between a mark and the name. */ head: { panelInset: number; markGap: number }; /** Client block: inset of its text from the content edge and the gap to the stripe. */ client: { inset: number; gap: number; labelGap: number; nameGap: number }; /** Meta row: inset of the first cell, minimum cell widths, panel-side start, gap between a label and its value. */ meta: { inset: number; noMin: number; dateMin: number; dueW: number; panelInset: number; labelGap: number }; /** Table: left offset from the content edge, header height, the SERVICE text inset, body text inset and row padding, columns. */ table: { left: number; headH: number; headInset: number; bodyInset: number; padY: number; padYMin: number; descRight: number; cols: { qty: number; rate: number; amount: number } }; separator: { color: string; h: number }; totals: { labelInset: number; valueInset: number; rowGap: number; barH: number; barInset: number; barGap: number; sepGap: number; wordsGap: number }; closing: { methodInset: number; bankInset: number; bulletInset: number; bulletText: number; bulletGap: number; sectionGap: number; labelGap: number; iconSize: number; iconGap: number; iconInset: number; contactGap: number; signW: number; clientTop: number; termsBesideMax: number }; gaps: { /** Client block above the table (only when the invoice is more than a page). */ client: GapRange; meta: GapRange; table: GapRange; tail: GapRange; }; } export type NorthlineVariant = PurpleVariant | CitrusVariant;