From 84f4a381780b2b1e20eb619e85a64b94ade126dc Mon Sep 17 00:00:00 2001 From: Christopher Monsanto Date: Tue, 16 Jun 2020 01:23:50 -0400 Subject: [PATCH] add tools/trim --- pnpm-lock.yaml | 5 +++++ pnpm-workspace.yaml | 1 + tools/trim/image.js | 37 +++++++++++++++++++++++++++++++++++++ tools/trim/index.js | 41 +++++++++++++++++++++++++++++++++++++++++ tools/trim/package.json | 6 ++++++ 5 files changed, 90 insertions(+) create mode 100644 tools/trim/image.js create mode 100644 tools/trim/index.js create mode 100644 tools/trim/package.json diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dd307a28..286cafc6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,11 @@ importers: commander: ^5.0.0 make-promises-safe: ^5.1.0 spritesmith: ^3.4.0 + tools/trim: + dependencies: + commander: 5.1.0 + specifiers: + commander: ^5.1.0 tools/tupctime: dependencies: better-sqlite3: 7.0.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4d5f284e..ac9570f0 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,3 +3,4 @@ packages: - 'tools/tupctime' - 'tools/deploy' - 'tools/deflopt' + - 'tools/trim' diff --git a/tools/trim/image.js b/tools/trim/image.js new file mode 100644 index 00000000..372634dd --- /dev/null +++ b/tools/trim/image.js @@ -0,0 +1,37 @@ + +import cp from 'child_process'; + +export function getDims(input) { + const info = cp.execFileSync('convert', [input, '-format', "%w+%h+%@", 'info:'], + {encoding:'utf8'}); + + const [imageWidth, imageHeight, width, height, left, top] = + info.split(/x|\+/g).map(dim => parseInt(dim)); + + const right = imageWidth - (left + width); + const bottom = imageHeight - (top + height); + + return { + width, + height, + left, + top, + right, + bottom + } +} + +export function crop(input, {width, height, left, top}, output) { + cp.execFileSync('convert', [input, '+repage', '-crop', `${width}x${height}+${left}+${top}`, output]); +} + +// Trim, preserving displacement from center +// Returns crop coords +export function losslessTrim(dims) { + return { + left: Math.min(dims.left, dims.right), + width: dims.width + Math.abs(dims.left - dims.right), + top: Math.min(dims.top, dims.bottom), + height: dims.height + Math.abs(dims.top - dims.bottom), + } +} diff --git a/tools/trim/index.js b/tools/trim/index.js new file mode 100644 index 00000000..28997fbb --- /dev/null +++ b/tools/trim/index.js @@ -0,0 +1,41 @@ + +import program from 'commander'; +import * as image from './image.js'; + +program + .option('-c, --check', 'Check only') + .option('-v, --verbose', 'Print info') + .option('-f, --force', 'Force write even when already cropped') + .parse(process.argv); + +let retVal = 0; + +for (const file of program.args) { + const dims = image.getDims(file); + const alreadyCropped = (dims.left === 0 || dims.right === 0) && + (dims.top === 0 || dims.bottom === 0); + + if (program.verbose) { + let msg = `${file}: ${dims.width}x${dims.height}, `; + if (alreadyCropped) { + msg += `displacement horiz ${dims.left - dims.right}, vert ${dims.top - dims.bottom}`; + } else { + msg += `padding left ${dims.left}, top ${dims.top}, right ${dims.right}, bottom ${dims.bottom}`; + } + console.log(msg); + } + + if (program.check && !alreadyCropped) { + retVal = 1; + if (!program.verbose) { + break; + } + } + + if (!program.check && (!alreadyCropped || program.force)) { + const trimDims = image.losslessTrim(dims); + image.crop(file, trimDims, file); + } +} + +process.exit(retVal); diff --git a/tools/trim/package.json b/tools/trim/package.json new file mode 100644 index 00000000..c47d6951 --- /dev/null +++ b/tools/trim/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "commander": "^5.1.0" + }, + "type": "module" +}