#!/usr/bin/env node const fs = require('fs') const path = require('path') const program = require('commander') const imaginary = require('..') program .version(imaginary.VERSION) program .command('crop [image]') .description('Crop any image in order to fit the given width or height pixels') .option('-s, --server [url]', 'imaginary server URL', imaginary.URL) .option('-k, --key [code]', 'Optional imaginary server API key') .option('-w, --width [pixels]', 'Image width resolution') .option('-h, --height [pixels]', 'Image height resolution') .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80) .option('-c, --compression [level]', 'PNG compression level', 6) .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp') .option('-o, --output [path]', 'Output image file path') .action(command('crop')) program .command('smartcrop [image]') .description('Smart crop any image in order to fit the given width or height pixels. Requires imaginary v1.0.8+') .option('-s, --server [url]', 'imaginary service URL', imaginary.URL) .option('-k, --key [code]', 'Optional imaginary server API key') .option('-w, --width [pixels]', 'Image width resolution') .option('-h, --height [pixels]', 'Image height resolution') .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80) .option('-c, --compression [level]', 'PNG compression level', 6) .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp') .option('-o, --output [path]', 'Output image file path') .action(command('smartcrop')) program .command('resize [image]') .description('Resize the image to the given width or height in pixels') .option('-s, --server [url]', 'imaginary server URL', imaginary.URL) .option('-k, --key [code]', 'Optional imaginary server API key') .option('-w, --width [pixels]', 'Image width resolution') .option('-h, --height [pixels]', 'Image height resolution') .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80) .option('-c, --compression [level]', 'PNG compression level', 6) .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp') .option('-o, --output [path]', 'Output image file path') .action(command('resize')) program .command('embed [image]') .description('Embed the image to the given width or height in pixels') .option('-k, --key [code]', 'Optional imaginary server API key') .option('-s, --server [url]', 'imaginary server URL', imaginary.URL) .option('-w, --width [pixels]', 'Image width resolution') .option('-h, --height [pixels]', 'Image height resolution') .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80) .option('-c, --compression [level]', 'PNG compression level', 6) .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp') .option('-o, --output [path]', 'Output image file path') .action(command('embed')) program .command('enlarge [image]') .description('Enlarge the image to the given width and height in pixels') .option('-k, --key [code]', 'Optional imaginary server API key') .option('-s, --server [url]', 'imaginary server URL', imaginary.URL) .option('-w, --width [pixels]', 'Image width resolution') .option('-h, --height [pixels]', 'Image height resolution') .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80) .option('-c, --compression [level]', 'PNG compression level', 6) .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp') .option('-o, --output [path]', 'Output image file path') .action(command('enlarge')) program .command('extract [image]') .description('Extract area from an image by top/left and width/height') .option('-k, --key [code]', 'Optional imaginary server API key') .option('-s, --server [url]', 'imaginary server URL', imaginary.URL) .option('-w, --width [pixels]', 'Image width resolution') .option('-h, --height [pixels]', 'Image height resolution') .option('-t, --top [pixels]', 'Area top margin') .option('-l, --left [pixels]', 'Area left margin') .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80) .option('-c, --compression [level]', 'PNG compression level', 6) .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp') .option('-o, --output [path]', 'Output image file path') .action(command('extract')) program .command('rotate [image]') .description('Rotate the image by degrees') .option('-s, --server [url]', 'imaginary server URL', imaginary.URL) .option('-k, --key [code]', 'Optional imaginary server API key') .option('-w, --width [pixels]', 'Image width resolution') .option('-h, --height [pixels]', 'Image height resolution') .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80) .option('-c, --compression [level]', 'PNG compression level', 6) .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp') .option('-r, --rotate [angle]', 'Image rotation angle. Must be multiple of 90. Example: 180') .option('-o, --output [path]', 'Output image file path') .action(command('rotate')) program .command('flip [image]') .description('Flip an image') .option('-s, --server [url]', 'imaginary server URL', imaginary.URL) .option('-k, --key [code]', 'Optional imaginary server API key') .option('-w, --width [pixels]', 'Image width resolution') .option('-h, --height [pixels]', 'Image height resolution') .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80) .option('-c, --compression [level]', 'PNG compression level', 6) .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp') .option('-o, --output [path]', 'Output image file path') .action(command('flip')) program .command('flop [image]') .description('Flop an image') .option('-s, --server [url]', 'imaginary server URL', imaginary.URL) .option('-k, --key [code]', 'Optional imaginary server API key') .option('-w, --width [pixels]', 'Image width resolution') .option('-h, --height [pixels]', 'Image height resolution') .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80) .option('-c, --compression [level]', 'PNG compression level', 6) .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp') .option('-o, --output [path]', 'Output image file path') .action(command('flop')) program .command('zoom [image]') .description('Zoom the image to the given width or height in pixels') .option('-s, --server [url]', 'imaginary server URL', imaginary.URL) .option('-k, --key [code]', 'Optional imaginary server API key') .option('-w, --width [pixels]', 'Image width resolution') .option('-h, --height [pixels]', 'Image height resolution') .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80) .option('-c, --compression [level]', 'PNG compression level', 6) .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp') .option('-f, --factor [level]', 'Zoom factor level between 1-5', 1) .option('-o, --output [path]', 'Output image file path') .action(command('zoom')) program .command('watermark [image]') .description('Add a text watermark in the image') .option('-s, --server [url]', 'imaginary server URL', imaginary.URL) .option('-k, --key [code]', 'Optional imaginary server API key') .option('-w, --width [pixels]', 'Image width resolution') .option('-h, --height [pixels]', 'Image height resolution') .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80) .option('-c, --compression [level]', 'PNG compression level', 6) .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp') .option('-x, --text [text]', 'Watermark text content. Example: copyright (c)') .option('-m, --margin [num]', 'Text margin in pixels. Example: 100') .option('-d, --dip [num]', 'DPI value for watermark. Example: 150') .option('-y, --textwidth [num]', 'Text width for watermark. Example: 200') .option('-s, --opacity [num]', 'Opacity level for watermark text. Example: 0.2') .option('-r, --noreplicate', 'Disable text replication in watermark') .option('-f, --font [value]', 'Watermark text font type and format. Example: sans bold 12') .option('-m, --color [value]', 'Watermark text RGB decimal base color. Example: 255,200,150') .option('-o, --output [path]', 'Output image file path') .action(command('watermark')) program .command('pipeline [image]') .description('Pipeline processing based on a JSON file transformation. Requires imaginary v1.0.8+') .option('-s, --server [url]', 'imaginary server URL', imaginary.URL) .option('-k, --key [code]', 'Optional imaginary server API key') .option('-j, --json [path]', 'Operations JSON file path to use. Optionally loaded from stdin.') .option('-p, --operations [json]', 'Operations JSON string to use.') .option('-o, --output [path]', 'Output image file path') .action(pipeline) program .command('info [image]') .description('Retrieve image information as JSON') .option('-s, --server [url]', 'imaginary server URL', imaginary.URL) .option('-k, --key [code]', 'Optional imaginary server API key') .option('-o, --output [path]', 'Output image info file (JSON)') .action(command('info')) program.on('--help', function () { console.log(' Examples:') console.log('') console.log(' $ imaginary crop -w 300 -h 260 -o out.jpg image.jpg') console.log(' $ imaginary smartcrop -w 300 -h 260 -o out.jpg image.jpg') console.log(' $ imaginary pipeline -j operations.json -o out.jpg image.jpg') console.log(' $ imaginary resize -w 300 -o out.jpg http://server.net/image.jpg') console.log(' $ imaginary zoom -f 2 -w 300 -o out.jpg http://server.net/image.jpg') console.log(' $ imaginary watermark --text "copyright" -o out.jpg http://server.net/image.jpg') console.log('') }) program.parse(process.argv) function command (action) { return function (image, flags) { const output = flags.output || path.basename(image) const opts = params(flags) imaginary(image, flags.server)[action](opts) .on('error', exitWithError) .pipe(fs.createWriteStream(output)) } } function pipeline (image, flags) { const output = flags.output || path.basename(image) const opts = params(flags) // Load JSON operations from disk if (opts.json) { opts.operations = JSON.parse(fs.readFileSync(opts.json)) } // Parse operations if a string if given if (opts.operations && typeof opts.operations === 'string') { opts.operations = JSON.parse(opts.operations) } // Validate proper input object if (!Array.isArray(opts.operations)) { return exitWithError('operations must be a JSON list of objects') } imaginary(image, flags.server).pipeline(opts.operations, opts) .on('error', exitWithError) .pipe(fs.createWriteStream(output)) } function params (opts) { const buf = {} Object.keys(opts).forEach(function (key) { if (omit(key)) { buf[key] = opts[key] } }) return buf } function omit (key) { const omitFields = ['output', 'server', 'options', 'parent', 'commands'] return !~omitFields.indexOf(key) && key.charAt(0) !== '_' && key.length > 2 } function exitWithError (err) { console.error('Cannot process image:', err.message || err) process.exit(1) }