import type { Sides } from "../../engine/geometry"; import { defineRoles, type TypeToken } from "../../fonts/roles"; import type { GapRange } from "../neutral/variant"; const P = "Poppins" as const; export const INK = "#000000"; /** * Monolith's type (docs/templates/03-monolith.md): Poppins Bold for the title, headings, labels and the Total row, * Medium for every data line, Regular for the date. Everything is black. Poppins ascent 1.05, descent 0.35. */ export const MONOLITH_ROLES = defineRoles({ /** "INVOICE" / "TAX INVOICE": 60 pt in the design, shrunk to the room the right-hand stack leaves. */ title: { face: P, weight: 700, size: 60.02, leading: 66, caps: true, minSize: 30 }, /** The vendor name (or its logo) at the top right. */ brand: { face: P, weight: 700, size: 12.5, leading: 21, minSize: 8 }, metaDate: { face: P, weight: 400, size: 12.5, leading: 21, minSize: 8 }, metaNo: { face: P, weight: 700, size: 12.5, leading: 21, minSize: 8 }, /** BILLED TO:, SUPPLIER:, PAYMENT INFORMATION :. */ heading: { face: P, weight: 700, size: 14.5, leading: 20, caps: true }, body: { face: P, weight: 500, size: 12.5, leading: 17.25, minSize: 8 }, bodyStrong: { face: P, weight: 700, size: 12.5, leading: 17.25, minSize: 8 }, colon: { face: P, weight: 400, size: 12.5, leading: 17.25 }, /** The footer's vendor name. */ vendor: { face: P, weight: 700, size: 12.5, leading: 20 }, tableHead: { face: P, weight: 700, size: 12.5, leading: 17.25, caps: true }, tableBody: { face: P, weight: 500, size: 12.5, leading: 17.25 }, tableSub: { face: P, weight: 500, size: 10, leading: 13.5 }, totalLabel: { face: P, weight: 700, size: 12.5, leading: 17.25 }, totalValue: { face: P, weight: 500, size: 12.5, leading: 17.25 }, total: { face: P, weight: 700, size: 12.5, leading: 17.25 }, small: { face: P, weight: 500, size: 10.5, leading: 14.5 }, smallStrong: { face: P, weight: 700, size: 10.5, leading: 14.5, caps: true }, signName: { face: P, weight: 700, size: 11, leading: 15 }, signDesignation: { face: P, weight: 500, size: 10, leading: 14 }, fine: { face: P, weight: 500, size: 9, leading: 12.5 }, footer: { face: P, weight: 500, size: 7 }, }); export type MonolithRoles = typeof MONOLITH_ROLES; /** * Canva A4 numbers (docs 03): content x 35..560.3, title origin 16 pt inside the left edge, the right-hand column * ends on the right edge; y values are the design's distances turned into flow gaps. */ export interface MonolithVariant { roles: MonolithRoles; ink: string; margins: Sides; marginFloor: Sides; /** Title origin inside the content edge. */ titleIndent: number; /** Space between the title and the right-hand stack at its narrowest. */ titleGap: number; stack: { /** Narrowest the right-hand stack gets (the logo allowance's width). */ minW: number; /** Widest share of the content width. */ maxShare: number; /** Top of the vendor name's line box below the header's top (cap height lines up with the title's). */ nameTop: number; /** Top of the logo box below the header's top: the allowance's top edge. */ logoTop: number; /** Space between the logo box's bottom and the date line. */ logoGap: number; /** Gap between a mark and the vendor name beside it, and the narrowest the name may get. */ markGap: number; minNameW: number; }; rule: number; heavyRule: number; /** Header bottom to its rule. */ headRuleGap: number; /** The rule to the BILLED TO heading, and the last info line to the next rule. */ /** Heading to its first line. */ headingGap: number; /** Width of the right-hand columns (supplier ids, footer vendor, signature) and the space beside them. */ sideW: number; sideGap: number; gaps: { /** The empty band between the info row and the table (88 pt in the design). */ band: GapRange; /** The rule to BILLED TO, and the last info line to the next rule. */ infoTop: GapRange; infoBottom: GapRange; /** The signature band to the footer, and the footer text to its bottom rule. */ closeBand: GapRange; closeBottom: GapRange; }; table: { /** Header band height inside the heavy rules. */ headH: number; descInset: number; cols: { price: number; qty: number; amount: number }; /** Right padding of the price and amount columns. */ numPad: number; /** Vertical row padding by item count: generous like the design for a few rows, tighter for many. */ padY: (rows: number) => number; padYMin: number; }; totals: { boxW: number; labelW: number; valueW: number; top: number; rowGap: number; ruleGap: number; totalGap: number; valuePad: number; leftGap: number; wordsMax: number }; closing: { /** The footer's top rule to its heading, and the heading to its first line. */ top: number; headingGap: number; labelW: number; colonW: number; notesBesideMax: number; signatureImageH: number; }; footerLeftW: number; } export const MONOLITH_VARIANT: MonolithVariant = { roles: MONOLITH_ROLES, ink: INK, margins: { top: 33, right: 35, bottom: 22, left: 35 }, marginFloor: { top: 24, right: 30, bottom: 18, left: 30 }, titleIndent: 15.7, titleGap: 20, stack: { minW: 160, maxShare: 0.46, nameTop: 17.6, logoTop: 21.75, logoGap: 4, markGap: 8, minNameW: 70 }, rule: 0.75, heavyRule: 1.5, headRuleGap: 3.15, headingGap: 7.2, sideW: 165, sideGap: 24, gaps: { band: { nominal: 88, min: 24 }, infoTop: { nominal: 21, min: 14 }, infoBottom: { nominal: 21.5, min: 14 }, closeBand: { nominal: 18, min: 8 }, closeBottom: { nominal: 14.8, min: 8 } }, table: { headH: 39.3, descInset: 9.4, cols: { price: 98, qty: 74, amount: 98 }, numPad: 10, padY: (rows) => (rows <= 3 ? 16 : rows <= 5 ? 12 : 8), padYMin: 6, }, totals: { boxW: 215.5, labelW: 108, valueW: 107.5, top: 6.15, rowGap: 5.75, ruleGap: 3, totalGap: 6.35, valuePad: 10, leftGap: 16, wordsMax: 120 }, closing: { top: 6.9, headingGap: 12.9, labelW: 114, colonW: 21, notesBesideMax: 110, signatureImageH: 45 }, footerLeftW: 336, }; export const leading = (t: TypeToken) => t.leading as number;