Add the Northline template family: Purple Pop and Citrus Split
Purple Pop (cream page, purple pills, asterisk and dome decor, opt-in logo) and Citrus Split (grey split panel, halftone chevrons, icon contacts, logo in the measured slot) are rebuilt from the reference PDFs on the engine with A4/Letter support, GST slots and zero audit errors. Adds static DM Sans, Jost and Poppins 800 as look-alikes for the commercial fonts. The picker now lists ten templates.
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
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 would need these tokens at factor 1.
|
||||
*/
|
||||
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;
|
||||
Reference in New Issue
Block a user