Standalone layouts for Canva templates 3 and 8, with the user's logo sized to the placeholder box, a CapRule decor primitive, optional TableRows rule/heading options, and a separate 1..45-row sweep test file.
143 lines
6.0 KiB
TypeScript
143 lines
6.0 KiB
TypeScript
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;
|