Public Access
Add patch script to dither badge photos
Inserts the ditherPhoto call into drawBadge right after photoSize is worked out, so it happens once at the final size and before either the portrait or landscape draw site. Skipped on the measuring pass. Idempotent, backs up first, and matches the size line by pattern so changing the photo multipliers later won't break it.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* One-shot patch: dither visitor photos before they go on the badge canvas.
|
||||
*
|
||||
* node scripts/dither-badge-photos.mjs # patch
|
||||
* node scripts/dither-badge-photos.mjs --check # report only
|
||||
*
|
||||
* A thermal head has no grey. Drawn straight, a photo gets thresholded by the
|
||||
* plane conversion and most of a face lands on one side of that threshold, so
|
||||
* it prints as a solid black mass. This inserts a Floyd-Steinberg pass at
|
||||
* exactly the size the photo will occupy.
|
||||
*
|
||||
* Idempotent, writes printer.js.bak, refuses to run if the anchor is missing.
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const target = path.resolve(here, '..', 'src', 'printer.js');
|
||||
const checkOnly = process.argv.includes('--check');
|
||||
|
||||
function fail(message) {
|
||||
console.error(`\n FAILED: ${message}\n`);
|
||||
console.error(' Nothing was changed.\n');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(target)) fail(`${target} does not exist. Run this from the repo root.`);
|
||||
|
||||
let source = fs.readFileSync(target, 'utf8');
|
||||
const original = source;
|
||||
|
||||
if (source.includes('ditherPhoto(')) {
|
||||
console.log('\n Already patched — printer.js dithers photos. Nothing to do.\n');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const changes = [];
|
||||
|
||||
/* -- 1. Import ---------------------------------------------------- */
|
||||
|
||||
const importAnchor = "import { photoAbsolutePath } from './photos.js';";
|
||||
if (!source.includes(importAnchor)) fail('could not find the photos.js import');
|
||||
|
||||
source = source.replace(
|
||||
importAnchor,
|
||||
`${importAnchor}\nimport { ditherPhoto } from './printing/dither.js';`
|
||||
);
|
||||
changes.push('added the ditherPhoto import');
|
||||
|
||||
/* -- 2. Dither once, at final size, before either draw site ------- */
|
||||
|
||||
// Matches the existing line regardless of the exact multipliers, so tweaking
|
||||
// the photo size later doesn't break this patch.
|
||||
const sizeAnchor = /const photoSize = photo \? Math\.round\([^;]+\) : 0;/;
|
||||
const match = source.match(sizeAnchor);
|
||||
if (!match) fail('could not find the photoSize calculation in drawBadge');
|
||||
|
||||
const insertion = `${match[0]}
|
||||
|
||||
// Thermal heads print pure black or nothing, so a photo has to be reduced to
|
||||
// 1-bit before it lands on the canvas — otherwise the plane conversion
|
||||
// thresholds it into a solid blob. Done once here rather than at each draw
|
||||
// site, and at exactly photoSize: rescaling a dithered image resamples the
|
||||
// dot pattern back into greys and undoes the whole thing.
|
||||
//
|
||||
// Skipped on the measuring pass, which never paints.
|
||||
if (photo && photoSize > 0 && startY !== null) {
|
||||
photo = ditherPhoto(photo, photoSize);
|
||||
}`;
|
||||
|
||||
source = source.replace(sizeAnchor, insertion);
|
||||
changes.push('drawBadge dithers the photo at its final size');
|
||||
|
||||
/* -- 3. Sanity check ---------------------------------------------- */
|
||||
|
||||
if (!/let photo = null;/.test(source)) {
|
||||
fail('photo is not declared with let — it cannot be reassigned. Patch by hand.');
|
||||
}
|
||||
|
||||
console.log('\n Changes to src/printer.js:');
|
||||
for (const c of changes) console.log(` - ${c}`);
|
||||
|
||||
if (checkOnly) {
|
||||
console.log('\n --check given, nothing written.\n');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (source === original) fail('nothing actually changed');
|
||||
|
||||
fs.writeFileSync(`${target}.bak`, original);
|
||||
fs.writeFileSync(target, source);
|
||||
|
||||
console.log(`\n Wrote ${target}`);
|
||||
console.log(` Backup at ${target}.bak`);
|
||||
console.log('\n Next: docker compose build && docker compose up -d\n');
|
||||
Reference in New Issue
Block a user