1 | #!/usr/bin/env node
|
2 |
|
3 | const fs = require('fs')
|
4 | const path = require('path')
|
5 | const program = require('commander')
|
6 | const imaginary = require('..')
|
7 |
|
8 | program
|
9 | .version(imaginary.VERSION)
|
10 |
|
11 | program
|
12 | .command('crop [image]')
|
13 | .description('Crop any image in order to fit the given width or height pixels')
|
14 | .option('-s, --server [url]', 'imaginary server URL', imaginary.URL)
|
15 | .option('-k, --key [code]', 'Optional imaginary server API key')
|
16 | .option('-w, --width [pixels]', 'Image width resolution')
|
17 | .option('-h, --height [pixels]', 'Image height resolution')
|
18 | .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80)
|
19 | .option('-c, --compression [level]', 'PNG compression level', 6)
|
20 | .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp')
|
21 | .option('-o, --output [path]', 'Output image file path')
|
22 | .action(command('crop'))
|
23 |
|
24 | program
|
25 | .command('smartcrop [image]')
|
26 | .description('Smart crop any image in order to fit the given width or height pixels. Requires imaginary v1.0.8+')
|
27 | .option('-s, --server [url]', 'imaginary service URL', imaginary.URL)
|
28 | .option('-k, --key [code]', 'Optional imaginary server API key')
|
29 | .option('-w, --width [pixels]', 'Image width resolution')
|
30 | .option('-h, --height [pixels]', 'Image height resolution')
|
31 | .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80)
|
32 | .option('-c, --compression [level]', 'PNG compression level', 6)
|
33 | .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp')
|
34 | .option('-o, --output [path]', 'Output image file path')
|
35 | .action(command('smartcrop'))
|
36 |
|
37 | program
|
38 | .command('resize [image]')
|
39 | .description('Resize the image to the given width or height in pixels')
|
40 | .option('-s, --server [url]', 'imaginary server URL', imaginary.URL)
|
41 | .option('-k, --key [code]', 'Optional imaginary server API key')
|
42 | .option('-w, --width [pixels]', 'Image width resolution')
|
43 | .option('-h, --height [pixels]', 'Image height resolution')
|
44 | .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80)
|
45 | .option('-c, --compression [level]', 'PNG compression level', 6)
|
46 | .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp')
|
47 | .option('-o, --output [path]', 'Output image file path')
|
48 | .action(command('resize'))
|
49 |
|
50 | program
|
51 | .command('embed [image]')
|
52 | .description('Embed the image to the given width or height in pixels')
|
53 | .option('-k, --key [code]', 'Optional imaginary server API key')
|
54 | .option('-s, --server [url]', 'imaginary server URL', imaginary.URL)
|
55 | .option('-w, --width [pixels]', 'Image width resolution')
|
56 | .option('-h, --height [pixels]', 'Image height resolution')
|
57 | .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80)
|
58 | .option('-c, --compression [level]', 'PNG compression level', 6)
|
59 | .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp')
|
60 | .option('-o, --output [path]', 'Output image file path')
|
61 | .action(command('embed'))
|
62 |
|
63 | program
|
64 | .command('enlarge [image]')
|
65 | .description('Enlarge the image to the given width and height in pixels')
|
66 | .option('-k, --key [code]', 'Optional imaginary server API key')
|
67 | .option('-s, --server [url]', 'imaginary server URL', imaginary.URL)
|
68 | .option('-w, --width [pixels]', 'Image width resolution')
|
69 | .option('-h, --height [pixels]', 'Image height resolution')
|
70 | .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80)
|
71 | .option('-c, --compression [level]', 'PNG compression level', 6)
|
72 | .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp')
|
73 | .option('-o, --output [path]', 'Output image file path')
|
74 | .action(command('enlarge'))
|
75 |
|
76 | program
|
77 | .command('extract [image]')
|
78 | .description('Extract area from an image by top/left and width/height')
|
79 | .option('-k, --key [code]', 'Optional imaginary server API key')
|
80 | .option('-s, --server [url]', 'imaginary server URL', imaginary.URL)
|
81 | .option('-w, --width [pixels]', 'Image width resolution')
|
82 | .option('-h, --height [pixels]', 'Image height resolution')
|
83 | .option('-t, --top [pixels]', 'Area top margin')
|
84 | .option('-l, --left [pixels]', 'Area left margin')
|
85 | .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80)
|
86 | .option('-c, --compression [level]', 'PNG compression level', 6)
|
87 | .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp')
|
88 | .option('-o, --output [path]', 'Output image file path')
|
89 | .action(command('extract'))
|
90 |
|
91 | program
|
92 | .command('rotate [image]')
|
93 | .description('Rotate the image by degrees')
|
94 | .option('-s, --server [url]', 'imaginary server URL', imaginary.URL)
|
95 | .option('-k, --key [code]', 'Optional imaginary server API key')
|
96 | .option('-w, --width [pixels]', 'Image width resolution')
|
97 | .option('-h, --height [pixels]', 'Image height resolution')
|
98 | .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80)
|
99 | .option('-c, --compression [level]', 'PNG compression level', 6)
|
100 | .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp')
|
101 | .option('-r, --rotate [angle]', 'Image rotation angle. Must be multiple of 90. Example: 180')
|
102 | .option('-o, --output [path]', 'Output image file path')
|
103 | .action(command('rotate'))
|
104 |
|
105 | program
|
106 | .command('flip [image]')
|
107 | .description('Flip an image')
|
108 | .option('-s, --server [url]', 'imaginary server URL', imaginary.URL)
|
109 | .option('-k, --key [code]', 'Optional imaginary server API key')
|
110 | .option('-w, --width [pixels]', 'Image width resolution')
|
111 | .option('-h, --height [pixels]', 'Image height resolution')
|
112 | .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80)
|
113 | .option('-c, --compression [level]', 'PNG compression level', 6)
|
114 | .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp')
|
115 | .option('-o, --output [path]', 'Output image file path')
|
116 | .action(command('flip'))
|
117 |
|
118 | program
|
119 | .command('flop [image]')
|
120 | .description('Flop an image')
|
121 | .option('-s, --server [url]', 'imaginary server URL', imaginary.URL)
|
122 | .option('-k, --key [code]', 'Optional imaginary server API key')
|
123 | .option('-w, --width [pixels]', 'Image width resolution')
|
124 | .option('-h, --height [pixels]', 'Image height resolution')
|
125 | .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80)
|
126 | .option('-c, --compression [level]', 'PNG compression level', 6)
|
127 | .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp')
|
128 | .option('-o, --output [path]', 'Output image file path')
|
129 | .action(command('flop'))
|
130 |
|
131 | program
|
132 | .command('zoom [image]')
|
133 | .description('Zoom the image to the given width or height in pixels')
|
134 | .option('-s, --server [url]', 'imaginary server URL', imaginary.URL)
|
135 | .option('-k, --key [code]', 'Optional imaginary server API key')
|
136 | .option('-w, --width [pixels]', 'Image width resolution')
|
137 | .option('-h, --height [pixels]', 'Image height resolution')
|
138 | .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80)
|
139 | .option('-c, --compression [level]', 'PNG compression level', 6)
|
140 | .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp')
|
141 | .option('-f, --factor [level]', 'Zoom factor level between 1-5', 1)
|
142 | .option('-o, --output [path]', 'Output image file path')
|
143 | .action(command('zoom'))
|
144 |
|
145 | program
|
146 | .command('watermark [image]')
|
147 | .description('Add a text watermark in the image')
|
148 | .option('-s, --server [url]', 'imaginary server URL', imaginary.URL)
|
149 | .option('-k, --key [code]', 'Optional imaginary server API key')
|
150 | .option('-w, --width [pixels]', 'Image width resolution')
|
151 | .option('-h, --height [pixels]', 'Image height resolution')
|
152 | .option('-q, --quality [num]', 'JPEG image quality between 1-100', 80)
|
153 | .option('-c, --compression [level]', 'PNG compression level', 6)
|
154 | .option('-t, --type [name]', 'Specify the image format to output. Possible values are: jpeg, png and webp')
|
155 | .option('-x, --text [text]', 'Watermark text content. Example: copyright (c)')
|
156 | .option('-m, --margin [num]', 'Text margin in pixels. Example: 100')
|
157 | .option('-d, --dip [num]', 'DPI value for watermark. Example: 150')
|
158 | .option('-y, --textwidth [num]', 'Text width for watermark. Example: 200')
|
159 | .option('-s, --opacity [num]', 'Opacity level for watermark text. Example: 0.2')
|
160 | .option('-r, --noreplicate', 'Disable text replication in watermark')
|
161 | .option('-f, --font [value]', 'Watermark text font type and format. Example: sans bold 12')
|
162 | .option('-m, --color [value]', 'Watermark text RGB decimal base color. Example: 255,200,150')
|
163 | .option('-o, --output [path]', 'Output image file path')
|
164 | .action(command('watermark'))
|
165 |
|
166 | program
|
167 | .command('pipeline [image]')
|
168 | .description('Pipeline processing based on a JSON file transformation. Requires imaginary v1.0.8+')
|
169 | .option('-s, --server [url]', 'imaginary server URL', imaginary.URL)
|
170 | .option('-k, --key [code]', 'Optional imaginary server API key')
|
171 | .option('-j, --json [path]', 'Operations JSON file path to use. Optionally loaded from stdin.')
|
172 | .option('-p, --operations [json]', 'Operations JSON string to use.')
|
173 | .option('-o, --output [path]', 'Output image file path')
|
174 | .action(pipeline)
|
175 |
|
176 | program
|
177 | .command('info [image]')
|
178 | .description('Retrieve image information as JSON')
|
179 | .option('-s, --server [url]', 'imaginary server URL', imaginary.URL)
|
180 | .option('-k, --key [code]', 'Optional imaginary server API key')
|
181 | .option('-o, --output [path]', 'Output image info file (JSON)')
|
182 | .action(command('info'))
|
183 |
|
184 | program.on('--help', function () {
|
185 | console.log(' Examples:')
|
186 | console.log('')
|
187 | console.log(' $ imaginary crop -w 300 -h 260 -o out.jpg image.jpg')
|
188 | console.log(' $ imaginary smartcrop -w 300 -h 260 -o out.jpg image.jpg')
|
189 | console.log(' $ imaginary pipeline -j operations.json -o out.jpg image.jpg')
|
190 | console.log(' $ imaginary resize -w 300 -o out.jpg http://server.net/image.jpg')
|
191 | console.log(' $ imaginary zoom -f 2 -w 300 -o out.jpg http://server.net/image.jpg')
|
192 | console.log(' $ imaginary watermark --text "copyright" -o out.jpg http://server.net/image.jpg')
|
193 | console.log('')
|
194 | })
|
195 |
|
196 | program.parse(process.argv)
|
197 |
|
198 | function command (action) {
|
199 | return function (image, flags) {
|
200 | const output = flags.output || path.basename(image)
|
201 | const opts = params(flags)
|
202 |
|
203 | imaginary(image, flags.server)[action](opts)
|
204 | .on('error', exitWithError)
|
205 | .pipe(fs.createWriteStream(output))
|
206 | }
|
207 | }
|
208 |
|
209 | function pipeline (image, flags) {
|
210 | const output = flags.output || path.basename(image)
|
211 | const opts = params(flags)
|
212 |
|
213 | // Load JSON operations from disk
|
214 | if (opts.json) {
|
215 | opts.operations = JSON.parse(fs.readFileSync(opts.json))
|
216 | }
|
217 |
|
218 | // Parse operations if a string if given
|
219 | if (opts.operations && typeof opts.operations === 'string') {
|
220 | opts.operations = JSON.parse(opts.operations)
|
221 | }
|
222 |
|
223 | // Validate proper input object
|
224 | if (!Array.isArray(opts.operations)) {
|
225 | return exitWithError('operations must be a JSON list of objects')
|
226 | }
|
227 |
|
228 | imaginary(image, flags.server).pipeline(opts.operations, opts)
|
229 | .on('error', exitWithError)
|
230 | .pipe(fs.createWriteStream(output))
|
231 | }
|
232 |
|
233 | function params (opts) {
|
234 | const buf = {}
|
235 |
|
236 | Object.keys(opts).forEach(function (key) {
|
237 | if (omit(key)) {
|
238 | buf[key] = opts[key]
|
239 | }
|
240 | })
|
241 |
|
242 | return buf
|
243 | }
|
244 |
|
245 | function omit (key) {
|
246 | const omitFields = ['output', 'server', 'options', 'parent', 'commands']
|
247 |
|
248 | return !~omitFields.indexOf(key) &&
|
249 | key.charAt(0) !== '_' &&
|
250 | key.length > 2
|
251 | }
|
252 |
|
253 | function exitWithError (err) {
|
254 | console.error('Cannot process image:', err.message || err)
|
255 | process.exit(1)
|
256 | }
|