Measure Canva logo placeholders and add slot-based logo placement

scripts/logo/measure-placeholders rasterises the Canva 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 Arun P logo and three test logos.
This commit is contained in:
2026-10-04 06:40:10 +05:30
parent 74ba04e17f
commit 490c5b9009
11 changed files with 984 additions and 21 deletions
+50
View File
@@ -0,0 +1,50 @@
// Shared helpers for the logo scripts: rasterise a Canva 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", "Modern Neutral Invoice Template");
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 Canva 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;
}
/** Canva 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,
};