Measure reference logo placeholders and add slot-based logo placement

scripts/logo/measure-placeholders rasterises the reference PDFs and measures
each logo placeholder's ink box and density into slots.generated.ts. The
new placement contains the logo in the measured box, shrinks denser logos
by up to 30 percent to match visual weight, picks knockout or a white chip
on dark surfaces, and passes an identity test per template. A contact
sheet compares each placeholder with the Test Vendor logo and three test logos.
This commit is contained in:
2026-10-04 06:40:10 +05:30
parent e1edf02448
commit eddf27629f
11 changed files with 984 additions and 21 deletions
+50
View File
@@ -0,0 +1,50 @@
// Shared helpers for the logo scripts: rasterise a reference PDF page with pdf.js and @napi-rs/canvas.
import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
export const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
export const PDF_DIR = path.join(ROOT, "templates", "reference");
export const SCALE = 4;
export const PAGE = { w: 595.5, h: 842.25 };
export const OUT_DIR = "/tmp/invoice-c2";
export async function loadCanvas() {
try {
return await import("@napi-rs/canvas");
} catch {
console.error("@napi-rs/canvas is not installed (it is an optional dev dependency); run npm install");
process.exit(1);
}
}
/** Renders page 1 of the reference PDF `n` at SCALE x. Returns the canvas. */
export async function renderPage(n, scale = SCALE) {
const { createCanvas } = await loadCanvas();
const pdfjs = await import("pdfjs-dist/legacy/build/pdf.mjs");
const data = new Uint8Array(readFileSync(path.join(PDF_DIR, `${n}.pdf`)));
const doc = await pdfjs.getDocument({ data, isEvalSupported: false, verbosity: 0 }).promise;
const page = await doc.getPage(1);
const vp = page.getViewport({ scale });
const canvas = createCanvas(Math.round(vp.width), Math.round(vp.height));
await page.render({ canvasContext: canvas.getContext("2d"), viewport: vp, canvas }).promise;
return canvas;
}
/** Copies a pt rectangle of a page canvas into a new canvas (same scale). */
export async function cropCanvas(canvas, rect, scale = SCALE) {
const { createCanvas } = await loadCanvas();
const x = Math.round(rect.x * scale);
const y = Math.round(rect.y * scale);
const w = Math.round(rect.w * scale);
const h = Math.round(rect.h * scale);
const out = createCanvas(w, h);
out.getContext("2d").drawImage(canvas, x, y, w, h, 0, 0, w, h);
return out;
}
/** reference PDF number of each template (docs/templates/README.md). */
export const TEMPLATE_PDF = {
marble: 1, highlighter: 2, monolith: 3, linea: 4, monogram: 5, serenity: 6, "crimson-grid": 7,
"cobalt-stripe": 8, "slate-band": 9, "teal-swoosh": 10, "tangerine-ledger": 11, "purple-pop": 12, "citrus-split": 13,
};