@pi-r/jimp 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/README.md +5 -0
- package/index.js +974 -0
- package/package.json +29 -0
- package/types/index.d.ts +19 -0
- package/util.d.ts +10 -0
- package/util.js +95 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2023 An Pham
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/index.js
ADDED
|
@@ -0,0 +1,974 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const child_process = require("child_process");
|
|
6
|
+
const jimp = require("jimp");
|
|
7
|
+
const gifwrap = require("gifwrap");
|
|
8
|
+
const bmp = require("bmp-js");
|
|
9
|
+
const types_1 = require("@e-mc/types");
|
|
10
|
+
const Image = require('@e-mc/image');
|
|
11
|
+
const util_1 = require("./util");
|
|
12
|
+
const CACHE_TRANSFORM = {};
|
|
13
|
+
let CACHE_INIT = false;
|
|
14
|
+
let TEMP_DIR = '';
|
|
15
|
+
const METHOD_ALIAS = {
|
|
16
|
+
contain: 'ct',
|
|
17
|
+
cover: 'cv',
|
|
18
|
+
resize: 're',
|
|
19
|
+
scale: 'sc',
|
|
20
|
+
scaleToFit: 'sf',
|
|
21
|
+
autocrop: 'au',
|
|
22
|
+
crop: 'cr',
|
|
23
|
+
blit: 'bt',
|
|
24
|
+
composite: 'cp',
|
|
25
|
+
mask: 'ma',
|
|
26
|
+
convolute: 'cl',
|
|
27
|
+
flip: 'fl',
|
|
28
|
+
mirror: 'mi',
|
|
29
|
+
rotate: 'ro',
|
|
30
|
+
brightness: 'br',
|
|
31
|
+
contrast: 'cn',
|
|
32
|
+
dither565: 'dt',
|
|
33
|
+
greyscale: 'gr',
|
|
34
|
+
invert: 'in',
|
|
35
|
+
normalize: 'no',
|
|
36
|
+
fade: 'fa',
|
|
37
|
+
opacity: 'op',
|
|
38
|
+
opaque: 'oq',
|
|
39
|
+
background: 'bg',
|
|
40
|
+
gaussian: 'ga',
|
|
41
|
+
blur: 'bl',
|
|
42
|
+
posterize: 'po',
|
|
43
|
+
sepia: 'se',
|
|
44
|
+
pixelate: 'px',
|
|
45
|
+
displace: 'dp',
|
|
46
|
+
color: 'co'
|
|
47
|
+
};
|
|
48
|
+
function getMethodName(value) {
|
|
49
|
+
const name = value.toLowerCase();
|
|
50
|
+
if (METHOD_ALIAS[name]) {
|
|
51
|
+
return name;
|
|
52
|
+
}
|
|
53
|
+
if (name.length === 2) {
|
|
54
|
+
for (const alias in METHOD_ALIAS) {
|
|
55
|
+
if (METHOD_ALIAS[alias] === name) {
|
|
56
|
+
return alias;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function performCommand(host, instance, localUri, command, outputType, finalAs, buffer, parent) {
|
|
62
|
+
return jimp.read((buffer || localUri))
|
|
63
|
+
.then(img => {
|
|
64
|
+
return transformCommand(localUri, new JimpHandler(img, instance, host), command, outputType, finalAs, parent);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async function transformCommand(localFile, handler, command, outputType, outputAs, parent) {
|
|
68
|
+
if (command) {
|
|
69
|
+
handler.instance.setCommand(command, outputAs);
|
|
70
|
+
}
|
|
71
|
+
await handler.method();
|
|
72
|
+
handler.resize();
|
|
73
|
+
handler.crop();
|
|
74
|
+
if (outputType === jimp.MIME_JPEG) {
|
|
75
|
+
if (!outputAs) {
|
|
76
|
+
handler.quality();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
handler.opacity();
|
|
81
|
+
}
|
|
82
|
+
switch (handler.rotateCount) {
|
|
83
|
+
case 0:
|
|
84
|
+
return handler;
|
|
85
|
+
case 1:
|
|
86
|
+
return handler.rotate();
|
|
87
|
+
default:
|
|
88
|
+
return handler.rotate(localFile, (err, result) => {
|
|
89
|
+
if (!err && handler.host) {
|
|
90
|
+
try {
|
|
91
|
+
handler.host.add(result, parent);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function setImageCache(tempKey, tempFile, output, localFile) {
|
|
100
|
+
try {
|
|
101
|
+
if (typeof output === 'string') {
|
|
102
|
+
fs.copyFileSync(output, tempFile);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
fs.writeFileSync(tempFile, output);
|
|
106
|
+
}
|
|
107
|
+
const stored = getCacheData.call(this);
|
|
108
|
+
if (localFile) {
|
|
109
|
+
const { ctimeMs, mtimeMs, size } = fs.statSync(localFile);
|
|
110
|
+
stored[tempKey] = { tempKey, tempFile, ctimeMs, localFile, mtimeMs, size };
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
stored[tempKey] = { tempKey, tempFile, ctimeMs: Date.now() };
|
|
114
|
+
}
|
|
115
|
+
if (this.settings.jimp.cache_expires) {
|
|
116
|
+
fs.writeFile(tempFile + '.json', JSON.stringify(stored[tempKey]), 'utf-8', () => { });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
TEMP_DIR = '';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function getImageCache(tempKey) {
|
|
124
|
+
const stored = getCacheData.call(this);
|
|
125
|
+
const data = stored[tempKey];
|
|
126
|
+
if (data) {
|
|
127
|
+
const { tempFile, localFile, ctimeMs, mtimeMs, size } = data;
|
|
128
|
+
try {
|
|
129
|
+
if (!localFile) {
|
|
130
|
+
return [fs.readFileSync(tempFile), '', ctimeMs];
|
|
131
|
+
}
|
|
132
|
+
const stat = fs.statSync(localFile);
|
|
133
|
+
if (stat.mtimeMs === mtimeMs && stat.size === size) {
|
|
134
|
+
return [fs.readFileSync(tempFile), '', ctimeMs];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
TEMP_DIR = '';
|
|
139
|
+
}
|
|
140
|
+
removeFile(tempFile);
|
|
141
|
+
delete stored[tempKey];
|
|
142
|
+
}
|
|
143
|
+
TEMP_DIR || (TEMP_DIR = this.getTempDir({ moduleDir: true, increment: 5 }));
|
|
144
|
+
return [null, TEMP_DIR ? path.join(TEMP_DIR, (0, types_1.generateUUID)()) : ''];
|
|
145
|
+
}
|
|
146
|
+
function getCacheData() {
|
|
147
|
+
var _a;
|
|
148
|
+
if (!CACHE_INIT) {
|
|
149
|
+
TEMP_DIR = this.getTempDir({ moduleDir: true, increment: 5 });
|
|
150
|
+
const settings = (_a = this.settings).jimp || (_a.jimp = {});
|
|
151
|
+
const expires = (0, types_1.parseExpires)(settings.cache_expires || 0);
|
|
152
|
+
if (expires === 0) {
|
|
153
|
+
settings.cache_expires = 0;
|
|
154
|
+
}
|
|
155
|
+
if (TEMP_DIR) {
|
|
156
|
+
if (expires === 0) {
|
|
157
|
+
Image.removeDir(TEMP_DIR, true);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
const current = Date.now();
|
|
161
|
+
try {
|
|
162
|
+
fs.readdirSync(TEMP_DIR, { withFileTypes: true }).forEach(item => {
|
|
163
|
+
if (item.isFile() && item.name.endsWith('.json')) {
|
|
164
|
+
const pathname = path.join(TEMP_DIR, item.name);
|
|
165
|
+
try {
|
|
166
|
+
const data = JSON.parse(fs.readFileSync(pathname, 'utf-8'));
|
|
167
|
+
if ((0, types_1.isPlainObject)(data) && fs.existsSync(data.tempFile)) {
|
|
168
|
+
if (data.ctimeMs + expires > current) {
|
|
169
|
+
CACHE_TRANSFORM[data.tempKey] = data;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
removeFile(data.tempFile);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
}
|
|
177
|
+
removeFile(pathname);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
CACHE_INIT = true;
|
|
186
|
+
}
|
|
187
|
+
return CACHE_TRANSFORM;
|
|
188
|
+
}
|
|
189
|
+
function formatMessage(value, startTime, failed, cTimeMs) {
|
|
190
|
+
if (cTimeMs) {
|
|
191
|
+
this.formatMessage(2048 /* LOG_TYPE.IMAGE */, "jimp" /* STRINGS.MODULE_NAME */, [value, 'cache'], new Date(cTimeMs).toLocaleString(), { ...Image.LOG_STYLE_NOTICE, hintBold: true });
|
|
192
|
+
}
|
|
193
|
+
else if (startTime) {
|
|
194
|
+
this.writeTimeProcess("jimp" /* STRINGS.MODULE_NAME */, value, startTime, { type: 2048 /* LOG_TYPE.IMAGE */, failed });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function getTempPath(ext) {
|
|
198
|
+
const tempDir = TEMP_DIR || this.getTempDir({ moduleDir: true, createDir: true }) || this.getTempDir();
|
|
199
|
+
return path.join(tempDir, (0, types_1.generateUUID)() + '.' + ext);
|
|
200
|
+
}
|
|
201
|
+
const removeFile = (pathname) => fs.unlink(pathname, () => { });
|
|
202
|
+
class JimpHandler {
|
|
203
|
+
constructor(handler, instance, host) {
|
|
204
|
+
this.handler = handler;
|
|
205
|
+
this.instance = instance;
|
|
206
|
+
this._host = null;
|
|
207
|
+
if (host) {
|
|
208
|
+
this._host = host;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async rotate(localFile, callback) {
|
|
212
|
+
if (this.aborted) {
|
|
213
|
+
return this;
|
|
214
|
+
}
|
|
215
|
+
const data = this.instance.rotateData;
|
|
216
|
+
if (data) {
|
|
217
|
+
const { values, color } = data;
|
|
218
|
+
const handler = this.handler;
|
|
219
|
+
if (!isNaN(color)) {
|
|
220
|
+
handler.background(color);
|
|
221
|
+
}
|
|
222
|
+
const tasks = [];
|
|
223
|
+
const length = values.length;
|
|
224
|
+
const deg = values[0];
|
|
225
|
+
if (length > 1 && localFile) {
|
|
226
|
+
const leading = localFile.substring(0, localFile.lastIndexOf('.') + 1);
|
|
227
|
+
const ext = path.extname(localFile);
|
|
228
|
+
for (let i = 1; i < length; ++i) {
|
|
229
|
+
const value = values[i];
|
|
230
|
+
const img = handler.clone().rotate(value);
|
|
231
|
+
const output = leading + value + ext;
|
|
232
|
+
tasks.push(img.writeAsync(output)
|
|
233
|
+
.then(() => this.finalize(output, callback))
|
|
234
|
+
.catch(err => this.instance.writeFail(["Unable to rotate image" /* ERR_IMAGE.ROTATE */, "jimp" /* STRINGS.MODULE_NAME */], err, 2048 /* LOG_TYPE.IMAGE */)));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (deg) {
|
|
238
|
+
handler.rotate(deg);
|
|
239
|
+
}
|
|
240
|
+
if (tasks.length) {
|
|
241
|
+
return Promise.all(tasks).then(() => this);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return this;
|
|
245
|
+
}
|
|
246
|
+
async method() {
|
|
247
|
+
if (this.aborted) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const data = this.instance.methodData;
|
|
251
|
+
if (data) {
|
|
252
|
+
const handler = this.handler;
|
|
253
|
+
for (const [name, args = []] of data) {
|
|
254
|
+
try {
|
|
255
|
+
const alias = getMethodName(name);
|
|
256
|
+
if (!alias) {
|
|
257
|
+
throw (0, types_1.errorValue)("Invalid method name" /* ERR_IMAGE.METHOD_NAME */, name);
|
|
258
|
+
}
|
|
259
|
+
if (alias === 'composite') {
|
|
260
|
+
const [src, x, y, opts] = args;
|
|
261
|
+
if ((0, types_1.isString)(src) && typeof x === 'number' && typeof y === 'number') {
|
|
262
|
+
handler.composite(await jimp.read(src), x, y, opts);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
throw (0, types_1.errorValue)("Invalid parameters" /* ERR_MESSAGE.PARAMETERS */, alias);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
handler[alias](...args);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
this.instance.writeFail(["Unknown" /* ERR_MESSAGE.UNKNOWN */, "jimp" /* STRINGS.MODULE_NAME */ + ': ' + name], err, 2048 /* LOG_TYPE.IMAGE */);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
resize() {
|
|
279
|
+
if (this.aborted) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const data = this.instance.resizeData;
|
|
283
|
+
if (data) {
|
|
284
|
+
const { width, height, color, algorithm, align, mode } = data;
|
|
285
|
+
const handler = this.handler;
|
|
286
|
+
if (!isNaN(color)) {
|
|
287
|
+
handler.background(color);
|
|
288
|
+
}
|
|
289
|
+
let resizeMode = jimp.RESIZE_NEAREST_NEIGHBOR, flags = 0;
|
|
290
|
+
switch (algorithm) {
|
|
291
|
+
case 'bilinear':
|
|
292
|
+
resizeMode = jimp.RESIZE_BILINEAR;
|
|
293
|
+
break;
|
|
294
|
+
case 'bicubic':
|
|
295
|
+
resizeMode = jimp.RESIZE_BICUBIC;
|
|
296
|
+
break;
|
|
297
|
+
case 'hermite':
|
|
298
|
+
resizeMode = jimp.RESIZE_HERMITE;
|
|
299
|
+
break;
|
|
300
|
+
case 'bezier':
|
|
301
|
+
resizeMode = jimp.RESIZE_BEZIER;
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
switch (align[0]) {
|
|
305
|
+
case 'left':
|
|
306
|
+
flags |= jimp.HORIZONTAL_ALIGN_LEFT;
|
|
307
|
+
break;
|
|
308
|
+
case 'center':
|
|
309
|
+
flags |= jimp.HORIZONTAL_ALIGN_CENTER;
|
|
310
|
+
break;
|
|
311
|
+
case 'right':
|
|
312
|
+
flags |= jimp.HORIZONTAL_ALIGN_RIGHT;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
switch (align[1]) {
|
|
316
|
+
case 'top':
|
|
317
|
+
flags |= jimp.VERTICAL_ALIGN_TOP;
|
|
318
|
+
break;
|
|
319
|
+
case 'middle':
|
|
320
|
+
flags |= jimp.VERTICAL_ALIGN_MIDDLE;
|
|
321
|
+
break;
|
|
322
|
+
case 'bottom':
|
|
323
|
+
flags |= jimp.VERTICAL_ALIGN_BOTTOM;
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
switch (mode) {
|
|
327
|
+
case 'contain':
|
|
328
|
+
handler.contain(width, height, flags);
|
|
329
|
+
break;
|
|
330
|
+
case 'cover':
|
|
331
|
+
handler.cover(width, height, flags);
|
|
332
|
+
break;
|
|
333
|
+
case 'scale':
|
|
334
|
+
handler.scaleToFit(width, height);
|
|
335
|
+
break;
|
|
336
|
+
default:
|
|
337
|
+
handler.resize(width === Infinity ? jimp.AUTO : width, height === Infinity ? jimp.AUTO : height, resizeMode);
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
background(value) {
|
|
343
|
+
if (Array.isArray(value)) {
|
|
344
|
+
value = parseInt('0x' + value.map(item => item.toString(16)).join(''), 16);
|
|
345
|
+
}
|
|
346
|
+
this.handler.background(value);
|
|
347
|
+
}
|
|
348
|
+
finalize(output, callback) {
|
|
349
|
+
var _a;
|
|
350
|
+
if (this.aborted) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const instance = this.instance;
|
|
354
|
+
if (instance.outputAs === 'webp' && path.extname(output).toLowerCase() !== '.webp') {
|
|
355
|
+
const webp = (_a = instance.settings).webp || (_a.webp = {});
|
|
356
|
+
const data = instance.qualityData;
|
|
357
|
+
const replace = instance.getCommand().indexOf('@') !== -1;
|
|
358
|
+
const filename = (0, util_1.renameExt)(output, 'webp', replace);
|
|
359
|
+
const args = [(0, util_1.normalizePath)(output)];
|
|
360
|
+
if (data) {
|
|
361
|
+
const { value, preset, nearLossless } = data;
|
|
362
|
+
if (preset) {
|
|
363
|
+
args.push('-preset', preset);
|
|
364
|
+
}
|
|
365
|
+
if (value > 100) {
|
|
366
|
+
args.push('-lossless');
|
|
367
|
+
}
|
|
368
|
+
else if (value >= 0) {
|
|
369
|
+
args.push('-q', value.toString());
|
|
370
|
+
}
|
|
371
|
+
if (nearLossless >= 0) {
|
|
372
|
+
args.push('-near_lossless', Math.min(nearLossless, 100).toString());
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (Array.isArray(webp.cwebp)) {
|
|
376
|
+
for (let i = 0; i < webp.cwebp.length; ++i) {
|
|
377
|
+
const arg = webp.cwebp[i];
|
|
378
|
+
switch (arg) {
|
|
379
|
+
case '-h':
|
|
380
|
+
case '-H':
|
|
381
|
+
case '-version':
|
|
382
|
+
continue;
|
|
383
|
+
case '-o':
|
|
384
|
+
case '--':
|
|
385
|
+
++i;
|
|
386
|
+
break;
|
|
387
|
+
case '-preset':
|
|
388
|
+
case '-q':
|
|
389
|
+
case '-near_lossless':
|
|
390
|
+
if (args.includes(arg) || arg !== '-preset' && args.includes('-lossless')) {
|
|
391
|
+
++i;
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
default:
|
|
395
|
+
args.push(arg);
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
args.push('-o', (0, util_1.normalizePath)(filename));
|
|
401
|
+
try {
|
|
402
|
+
child_process.execFile((0, util_1.getWebP_bin)('cwebp', webp.path), args, { shell: true, signal: this.instance.signal }, err => {
|
|
403
|
+
if (err) {
|
|
404
|
+
this.instance.writeFail(["Unable to convert file" /* ERR_MESSAGE.CONVERT_FILE */, path.basename(filename)], err, 2048 /* LOG_TYPE.IMAGE */);
|
|
405
|
+
}
|
|
406
|
+
else if (webp !== output) {
|
|
407
|
+
const tempFile = output;
|
|
408
|
+
queueMicrotask(() => {
|
|
409
|
+
fs.unlink(tempFile, error => !error && fs.rmdir(path.dirname(tempFile), () => { }));
|
|
410
|
+
});
|
|
411
|
+
output = filename;
|
|
412
|
+
}
|
|
413
|
+
if (callback) {
|
|
414
|
+
callback(err, output);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
catch (err) {
|
|
419
|
+
this.instance.checkPackage(err, 'cwebp-bin@6.1.2', "Unknown" /* ERR_MESSAGE.UNKNOWN */, { type: 2048 /* LOG_TYPE.IMAGE */, passThrough: !!callback });
|
|
420
|
+
if (callback) {
|
|
421
|
+
callback(err, '');
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
else if (callback) {
|
|
426
|
+
callback(null, output);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
getBuffer(tempFile, saveAs) {
|
|
430
|
+
const empty = () => tempFile ? '' : null;
|
|
431
|
+
const output = getTempPath.call(this.instance, this.instance.outputAs || (saveAs && util_1.MIME_OUTPUT.has('image/' + (saveAs === 'jpg' ? 'jpeg' : saveAs)) ? saveAs : this.handler.getMIME().split('/').pop()));
|
|
432
|
+
return !output ? Promise.resolve(empty()) : new Promise(resolve => {
|
|
433
|
+
this.handler.write(output, error => {
|
|
434
|
+
if (error) {
|
|
435
|
+
resolve(empty());
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
this.finalize(output, (error, result) => {
|
|
439
|
+
if (error) {
|
|
440
|
+
resolve(empty());
|
|
441
|
+
}
|
|
442
|
+
else if (tempFile) {
|
|
443
|
+
resolve(output);
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
try {
|
|
447
|
+
resolve(fs.readFileSync(result));
|
|
448
|
+
}
|
|
449
|
+
catch (err) {
|
|
450
|
+
this.instance.writeFail(["Unable to read file" /* ERR_MESSAGE.READ_FILE */, path.basename(result)], err, 32 /* LOG_TYPE.FILE */);
|
|
451
|
+
resolve(null);
|
|
452
|
+
}
|
|
453
|
+
queueMicrotask(() => fs.unlink(result, err => !err && this.instance.emit('file:delete', result)));
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
crop() {
|
|
460
|
+
if (this.aborted) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
const data = this.instance.cropData;
|
|
464
|
+
if (data) {
|
|
465
|
+
this.handler = this.handler.crop(data.x, data.y, data.width, data.height);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
opacity() {
|
|
469
|
+
if (this.aborted) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const value = this.instance.opacityValue ?? NaN;
|
|
473
|
+
if (value >= 0) {
|
|
474
|
+
this.handler = this.handler.opacity(value);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
quality() {
|
|
478
|
+
if (this.aborted) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const data = this.instance.qualityData;
|
|
482
|
+
if (data && !isNaN(data.value)) {
|
|
483
|
+
this.handler = this.handler.quality(data.value);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
write(output, callback) {
|
|
487
|
+
if (this.aborted) {
|
|
488
|
+
if (callback) {
|
|
489
|
+
callback((0, types_1.createAbortError)(), '');
|
|
490
|
+
}
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
this.handler.write(output, err => {
|
|
494
|
+
if (!err) {
|
|
495
|
+
this.finalize(output, callback);
|
|
496
|
+
}
|
|
497
|
+
else if (callback) {
|
|
498
|
+
callback(err, '');
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
writeAsync(output, callback) {
|
|
503
|
+
if (this.aborted) {
|
|
504
|
+
if (callback) {
|
|
505
|
+
callback((0, types_1.createAbortError)(), '');
|
|
506
|
+
}
|
|
507
|
+
return Promise.resolve();
|
|
508
|
+
}
|
|
509
|
+
return this.handler.writeAsync(output)
|
|
510
|
+
.then(() => this.finalize(output, callback))
|
|
511
|
+
.catch(err => {
|
|
512
|
+
if (callback) {
|
|
513
|
+
callback(err, '');
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
this.instance.writeFail(["Unable to write file" /* ERR_MESSAGE.WRITE_FILE */, path.basename(output)], err, 2048 /* LOG_TYPE.IMAGE */);
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
get host() {
|
|
521
|
+
return this._host || this.instance.host;
|
|
522
|
+
}
|
|
523
|
+
get aborted() {
|
|
524
|
+
return this.instance.aborted;
|
|
525
|
+
}
|
|
526
|
+
get rotateCount() {
|
|
527
|
+
return this.instance.rotateData?.values.length || 0;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
// @ts-ignore
|
|
531
|
+
class Jimp extends Image {
|
|
532
|
+
constructor() {
|
|
533
|
+
super(...arguments);
|
|
534
|
+
this._moduleName = "jimp" /* STRINGS.MODULE_NAME */;
|
|
535
|
+
this._threadable = true;
|
|
536
|
+
}
|
|
537
|
+
static async transform(file, command, options = {}) {
|
|
538
|
+
const [outputType, saveAs, finalAs] = (0, util_1.parseFormat)(command = command.trim(), options.mimeType);
|
|
539
|
+
const empty = () => options.tempFile ? '' : null;
|
|
540
|
+
if (outputType) {
|
|
541
|
+
const instance = new Jimp(options.module);
|
|
542
|
+
let buffer = null;
|
|
543
|
+
if (Buffer.isBuffer(file)) {
|
|
544
|
+
const tempDir = TEMP_DIR || instance.getTempDir();
|
|
545
|
+
if (!this.createDir(tempDir)) {
|
|
546
|
+
return empty();
|
|
547
|
+
}
|
|
548
|
+
try {
|
|
549
|
+
const { ext } = await this.resolveMime(file) || { ext: 'unknown' };
|
|
550
|
+
buffer = file;
|
|
551
|
+
fs.writeFileSync(file = path.join(tempDir, (0, types_1.generateUUID)() + '.' + ext), buffer);
|
|
552
|
+
}
|
|
553
|
+
catch {
|
|
554
|
+
return empty();
|
|
555
|
+
}
|
|
556
|
+
options.cache = false;
|
|
557
|
+
}
|
|
558
|
+
const filename = path.basename(file);
|
|
559
|
+
const broadcastId = options.broadcastId;
|
|
560
|
+
if (broadcastId) {
|
|
561
|
+
if ((0, types_1.isPlainObject)(broadcastId)) {
|
|
562
|
+
instance.broadcastId = broadcastId.value;
|
|
563
|
+
if (broadcastId.stripAnsi === false) {
|
|
564
|
+
instance.supports('stripAnsi', false);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
instance.broadcastId = broadcastId;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
const writeMessage = (failed, cTimeMs) => {
|
|
572
|
+
if (cTimeMs || options.startTime) {
|
|
573
|
+
formatMessage.call(instance, filename + (0, util_1.showOutputType)(options.mimeType, outputType, finalAs), options.startTime, failed, cTimeMs);
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
let tempKey, tempFile;
|
|
577
|
+
if (options.cache) {
|
|
578
|
+
let ctimeMs;
|
|
579
|
+
[buffer, tempFile, ctimeMs] = getImageCache.call(instance, tempKey = file + command + (options.mimeType || ''));
|
|
580
|
+
if (buffer) {
|
|
581
|
+
writeMessage(false, ctimeMs);
|
|
582
|
+
return Promise.resolve(buffer);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
instance.formatMessage(Image.LOG_TYPE.IMAGE, "jimp" /* STRINGS.MODULE_NAME */, ["Transforming image..." /* STRINGS.TRANSFORM */, filename], command);
|
|
586
|
+
Image.initCpuUsage(instance);
|
|
587
|
+
return performCommand(null, instance, file, command, outputType, finalAs)
|
|
588
|
+
.then(async (handler) => {
|
|
589
|
+
const result = await handler.getBuffer(options.tempFile, saveAs);
|
|
590
|
+
instance.flushLog();
|
|
591
|
+
writeMessage(!result || instance.errors.length > 0);
|
|
592
|
+
if (result && tempKey && tempFile) {
|
|
593
|
+
setImageCache.call(instance, tempKey, tempFile, result, file);
|
|
594
|
+
}
|
|
595
|
+
return result;
|
|
596
|
+
})
|
|
597
|
+
.catch(() => empty())
|
|
598
|
+
.finally(() => buffer && !options.cache && removeFile(file));
|
|
599
|
+
}
|
|
600
|
+
return empty();
|
|
601
|
+
}
|
|
602
|
+
parseRotate(value) {
|
|
603
|
+
const data = super.parseRotate(value);
|
|
604
|
+
if (data && this.settings.jimp?.rotate_clockwise) {
|
|
605
|
+
const values = data.values;
|
|
606
|
+
for (let i = 0; i < values.length; ++i) {
|
|
607
|
+
values[i] *= -1;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
return data;
|
|
611
|
+
}
|
|
612
|
+
async using(data, command) {
|
|
613
|
+
if (this.aborted) {
|
|
614
|
+
return Promise.reject((0, types_1.createAbortError)());
|
|
615
|
+
}
|
|
616
|
+
return new Promise(async (resolve, reject) => {
|
|
617
|
+
var _a;
|
|
618
|
+
const { host, file } = data;
|
|
619
|
+
const localUri = host.getLocalUri(data);
|
|
620
|
+
const mimeType = host.getMimeType(data);
|
|
621
|
+
if (!localUri || !util_1.MIME_INPUT.has(mimeType)) {
|
|
622
|
+
reject((0, types_1.errorValue)("Unknown" /* ERR_MESSAGE.UNKNOWN */, !localUri ? 'URI' : 'MIME'));
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (!this.canRead(localUri, { ownPermissionOnly: true })) {
|
|
626
|
+
reject((0, types_1.errorValue)("Not permitted to read file" /* ERR_MESSAGE.UNSUPPORTED_READ */, localUri));
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
const [outputType, saveAs, finalAs] = (0, util_1.parseFormat)(command = command.trim(), mimeType, true);
|
|
630
|
+
if (!outputType) {
|
|
631
|
+
reject((0, types_1.errorValue)("Invalid format" /* ERR_MESSAGE.FORMAT */, /^\w+/.exec(command)?.[0] || "Unknown" /* ERR_MESSAGE.UNKNOWN */));
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
const replace = command.indexOf('@') !== -1;
|
|
635
|
+
const output = host.addCopy(data.getObject({ command, outputType }), saveAs, replace);
|
|
636
|
+
if (!output) {
|
|
637
|
+
reject((0, types_1.errorValue)("Not able to copy file" /* ERR_MESSAGE.NOT_COPYABLE */, outputType));
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
if (!this.canWrite(output, { ownPermissionOnly: true })) {
|
|
641
|
+
reject((0, types_1.errorValue)("Not permitted to write file" /* ERR_MESSAGE.UNSUPPORTED_WRITE */, output));
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
const startTime = process.hrtime();
|
|
645
|
+
const success = (result, ctimeMs) => {
|
|
646
|
+
const filename = path.basename(result);
|
|
647
|
+
if (file.document) {
|
|
648
|
+
host.writeImage(file.document, data.getObject({ command, output: result }));
|
|
649
|
+
}
|
|
650
|
+
if (host.getLocalUri(data) !== result) {
|
|
651
|
+
if (command.indexOf('%') !== -1) {
|
|
652
|
+
const files = host.filesToCompare.get(file);
|
|
653
|
+
if (files) {
|
|
654
|
+
files.push(result);
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
host.filesToCompare.set(file, [result]);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
else if (replace) {
|
|
661
|
+
host.replace(file, result);
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
host.add(result, file);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
if (replace && file.localUri !== output && !host.assets.find(item => item.localUri === output && !item.invalid)) {
|
|
668
|
+
host.filesToRemove.add(output);
|
|
669
|
+
}
|
|
670
|
+
formatMessage.call(this, (0, util_1.showInputType)(mimeType, outputType, finalAs) + filename, startTime, false, ctimeMs);
|
|
671
|
+
resolve();
|
|
672
|
+
};
|
|
673
|
+
let tempKey, tempFile;
|
|
674
|
+
if (this.settings.cache && (file.etag || file.buffer)) {
|
|
675
|
+
let buffer, ctimeMs;
|
|
676
|
+
[buffer, tempFile] = getImageCache.call(this, tempKey = (file.etag || Image.asHash(file.buffer)) + command + mimeType);
|
|
677
|
+
if (buffer) {
|
|
678
|
+
const result = finalAs === 'webp' ? (0, util_1.renameExt)(output, 'webp', replace) : output;
|
|
679
|
+
fs.writeFileSync(result, file.buffer = buffer);
|
|
680
|
+
success(result, ctimeMs);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
const finalize = (value) => {
|
|
685
|
+
if (tempFile && tempKey) {
|
|
686
|
+
setImageCache.call(this, tempKey, tempFile, value);
|
|
687
|
+
}
|
|
688
|
+
if (replace) {
|
|
689
|
+
delete file.buffer;
|
|
690
|
+
}
|
|
691
|
+
success(value);
|
|
692
|
+
};
|
|
693
|
+
const transformBuffer = (bmpFile) => {
|
|
694
|
+
startMessage();
|
|
695
|
+
performCommand(host, this, localUri, command, outputType, finalAs, bmpFile || file.buffer, file)
|
|
696
|
+
.then((img) => {
|
|
697
|
+
if (typeof bmpFile === 'string') {
|
|
698
|
+
removeFile(bmpFile);
|
|
699
|
+
}
|
|
700
|
+
const errorResponse = (err) => {
|
|
701
|
+
this.writeFail(["Unable to finalize image" /* ERR_IMAGE.FINALIZE */, "jimp" /* STRINGS.MODULE_NAME */], err, { type: 2048 /* LOG_TYPE.IMAGE */, startTime });
|
|
702
|
+
resolve();
|
|
703
|
+
};
|
|
704
|
+
if (outputType === jimp.MIME_GIF) {
|
|
705
|
+
try {
|
|
706
|
+
const { GifUtil, GifFrame } = gifwrap;
|
|
707
|
+
const frame = new GifFrame(img.handler.bitmap);
|
|
708
|
+
GifUtil.quantizeSorokin(frame, 256);
|
|
709
|
+
GifUtil.write(output, [frame])
|
|
710
|
+
.then(() => finalize(output))
|
|
711
|
+
.catch(err => errorResponse(err));
|
|
712
|
+
}
|
|
713
|
+
catch (err) {
|
|
714
|
+
errorResponse(err);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
img.write(output, (err, result) => {
|
|
719
|
+
if (!err && result) {
|
|
720
|
+
finalize(result);
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
errorResponse(err || new Error("Unknown" /* ERR_MESSAGE.UNKNOWN */));
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
})
|
|
728
|
+
.catch(err => {
|
|
729
|
+
this.writeFail(["Unable to read buffer" /* ERR_MESSAGE.READ_BUFFER */, path.basename(localUri)], err, { type: 2048 /* LOG_TYPE.IMAGE */, startTime });
|
|
730
|
+
resolve();
|
|
731
|
+
});
|
|
732
|
+
};
|
|
733
|
+
const errorResponse = (err) => {
|
|
734
|
+
this.writeFail(["Unable to convert file" /* ERR_MESSAGE.CONVERT_FILE */, path.basename(localUri)], err, { type: 2048 /* LOG_TYPE.IMAGE */, startTime });
|
|
735
|
+
resolve();
|
|
736
|
+
};
|
|
737
|
+
const rotateAnim = (cmd) => {
|
|
738
|
+
const rotate = cmd.rotate;
|
|
739
|
+
if (rotate) {
|
|
740
|
+
rotate.values = [rotate.values.pop()];
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
const hasTransform = (cmd) => !!(cmd.rotate || cmd.opacity >= 0 && cmd.opacity < 1 || cmd.resize || cmd.crop || cmd.method);
|
|
744
|
+
const startMessage = () => host.formatMessage(2048 /* LOG_TYPE.IMAGE */, "jimp" /* STRINGS.MODULE_NAME */, ["Transforming image..." /* STRINGS.TRANSFORM */, path.basename(localUri)], command);
|
|
745
|
+
if (mimeType === jimp.MIME_GIF) {
|
|
746
|
+
if (outputType === jimp.MIME_GIF || outputType === "image/webp" /* STRINGS.MIME_WEBP */) {
|
|
747
|
+
const cmd = this.parseCommand(command);
|
|
748
|
+
const transformWebP = (target, modified) => {
|
|
749
|
+
var _a;
|
|
750
|
+
if (finalAs === 'webp') {
|
|
751
|
+
if (!modified) {
|
|
752
|
+
startMessage();
|
|
753
|
+
}
|
|
754
|
+
const { path: webp_path, gif2webp } = (_a = this.settings).webp || (_a.webp = {});
|
|
755
|
+
const webp = (0, util_1.renameExt)(output, 'webp', replace);
|
|
756
|
+
const args = [(0, util_1.normalizePath)(target)];
|
|
757
|
+
const quality = cmd.quality;
|
|
758
|
+
if (quality) {
|
|
759
|
+
if (!isNaN(quality.value)) {
|
|
760
|
+
args.push('-q', quality.value.toString());
|
|
761
|
+
}
|
|
762
|
+
if (!isNaN(quality.method)) {
|
|
763
|
+
args.push('-m', quality.method.toString());
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
if (Array.isArray(gif2webp)) {
|
|
767
|
+
for (let i = 0, length = gif2webp.length; i < length; ++i) {
|
|
768
|
+
const arg = gif2webp[i];
|
|
769
|
+
switch (arg) {
|
|
770
|
+
case '-h':
|
|
771
|
+
case '-version':
|
|
772
|
+
continue;
|
|
773
|
+
case '-o':
|
|
774
|
+
++i;
|
|
775
|
+
continue;
|
|
776
|
+
case '-q':
|
|
777
|
+
case '-m':
|
|
778
|
+
if (args.includes(arg)) {
|
|
779
|
+
++i;
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
break;
|
|
783
|
+
case '--':
|
|
784
|
+
i = length;
|
|
785
|
+
continue;
|
|
786
|
+
}
|
|
787
|
+
args.push(arg);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
args.push('-o', (0, util_1.normalizePath)(webp));
|
|
791
|
+
try {
|
|
792
|
+
child_process.execFile((0, util_1.getWebP_bin)('gif2webp', webp_path), args, { shell: true, signal: this.signal }, (err, stdout) => {
|
|
793
|
+
if (!err) {
|
|
794
|
+
this.addLog(types_1.STATUS_TYPE.INFO, stdout);
|
|
795
|
+
finalize(webp);
|
|
796
|
+
}
|
|
797
|
+
else {
|
|
798
|
+
errorResponse(err);
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
catch (err) {
|
|
803
|
+
if (this.checkPackage(err, 'gif2webp-bin@3', 2048 /* LOG_TYPE.IMAGE */)) {
|
|
804
|
+
resolve();
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
errorResponse(err);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
else if (modified) {
|
|
812
|
+
finalize(output);
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
resolve();
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
if (hasTransform(cmd)) {
|
|
819
|
+
try {
|
|
820
|
+
startMessage();
|
|
821
|
+
const { GifUtil, BitmapImage } = gifwrap;
|
|
822
|
+
GifUtil.read(file.buffer || localUri)
|
|
823
|
+
.then(gif => {
|
|
824
|
+
rotateAnim(cmd);
|
|
825
|
+
Promise.all(gif.frames.map(frame => {
|
|
826
|
+
const handler = new JimpHandler(GifUtil.shareAsJimp(jimp, frame), this);
|
|
827
|
+
return transformCommand(localUri, handler, cmd, jimp.MIME_GIF);
|
|
828
|
+
}))
|
|
829
|
+
.then(items => {
|
|
830
|
+
const frames = gif.frames;
|
|
831
|
+
for (let i = 0, length = items.length; i < length; ++i) {
|
|
832
|
+
const img = new BitmapImage(items[i].handler.bitmap);
|
|
833
|
+
GifUtil.quantizeSorokin(img, 256);
|
|
834
|
+
frames[i].bitmap = img.bitmap;
|
|
835
|
+
}
|
|
836
|
+
GifUtil.write(output, frames, gif)
|
|
837
|
+
.then(() => transformWebP(output, true))
|
|
838
|
+
.catch(err => errorResponse(err));
|
|
839
|
+
})
|
|
840
|
+
.catch(err => errorResponse(err));
|
|
841
|
+
})
|
|
842
|
+
.catch(err => errorResponse(err));
|
|
843
|
+
}
|
|
844
|
+
catch (err) {
|
|
845
|
+
errorResponse(err);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
else {
|
|
849
|
+
transformWebP(localUri, false);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
transformBuffer();
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
else if (mimeType === "image/webp" /* STRINGS.MIME_WEBP */) {
|
|
857
|
+
const tryWebpMux = async () => {
|
|
858
|
+
let loaded;
|
|
859
|
+
try {
|
|
860
|
+
const webp = new (require('node-webpmux').Image)();
|
|
861
|
+
loaded = true;
|
|
862
|
+
if (Image.supported(18, 1)) {
|
|
863
|
+
this.formatMessage(2048 /* LOG_TYPE.IMAGE */, 'WARN', 'node-webpmux is not compatible with global fetch', 'NodeJS: --no-experimental-fetch', { ...Image.LOG_STYLE_WARN });
|
|
864
|
+
}
|
|
865
|
+
await webp.initLib();
|
|
866
|
+
await webp.load(host.getBuffer(file));
|
|
867
|
+
if (!(webp.hasAnim && (outputType === "image/webp" /* STRINGS.MIME_WEBP */ || outputType === jimp.MIME_GIF))) {
|
|
868
|
+
transformBuffer(bmp.encode({ width: webp.width, height: webp.height, data: Image.toABGR(await (!webp.hasAnim ? webp.getImageData() : webp.getFrameData(0))) }).data);
|
|
869
|
+
return true;
|
|
870
|
+
}
|
|
871
|
+
const cmd = this.parseCommand(command);
|
|
872
|
+
if (hasTransform(cmd)) {
|
|
873
|
+
startMessage();
|
|
874
|
+
Promise.all(webp.frames.map(async (frame, index) => {
|
|
875
|
+
const handler = new JimpHandler(await jimp.read(bmp.encode({ width: frame.width, height: frame.height, data: Image.toABGR(await webp.getFrameData(index)) }).data), this);
|
|
876
|
+
handler.background(webp.anim.bgColor);
|
|
877
|
+
return transformCommand(localUri, handler, cmd, jimp.MIME_BMP);
|
|
878
|
+
}))
|
|
879
|
+
.then(items => {
|
|
880
|
+
const length = items.length;
|
|
881
|
+
if (outputType === jimp.MIME_GIF) {
|
|
882
|
+
try {
|
|
883
|
+
const { GifFrame, GifUtil, BitmapImage } = gifwrap;
|
|
884
|
+
const frames = new Array(length);
|
|
885
|
+
for (let i = 0; i < length; ++i) {
|
|
886
|
+
const { x, y, delay } = webp.frames[i];
|
|
887
|
+
const img = new BitmapImage(items[i].handler.bitmap);
|
|
888
|
+
GifUtil.quantizeSorokin(img, 256);
|
|
889
|
+
frames[i] = new GifFrame(new BitmapImage(img), { xOffset: x, yOffset: y, delayCentisecs: delay / 10 });
|
|
890
|
+
}
|
|
891
|
+
GifUtil.write(output, frames, { loops: webp.anim.loops })
|
|
892
|
+
.then(() => finalize(output))
|
|
893
|
+
.catch(err => errorResponse(err));
|
|
894
|
+
}
|
|
895
|
+
catch (err) {
|
|
896
|
+
errorResponse(err);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
else {
|
|
900
|
+
const { value: q = NaN, method: m = NaN, preset } = cmd.quality || {};
|
|
901
|
+
const quality = !isNaN(q) ? q : undefined;
|
|
902
|
+
const method = !isNaN(m) ? m : undefined;
|
|
903
|
+
rotateAnim(cmd);
|
|
904
|
+
const frames = new Array(length);
|
|
905
|
+
let w = 0, h = 0;
|
|
906
|
+
for (let i = 0; i < length; ++i) {
|
|
907
|
+
const { width, height, data: buffer } = items[i].handler.bitmap;
|
|
908
|
+
frames[i] = webp.setFrameData(i, buffer, { width, height, quality, method, preset });
|
|
909
|
+
w = Math.max(w, width);
|
|
910
|
+
h = Math.max(h, height);
|
|
911
|
+
}
|
|
912
|
+
Promise.all(frames)
|
|
913
|
+
.then(() => {
|
|
914
|
+
webp.save(output, { width: w, height: h, bgColor: cmd.rotate ? [0, 0, 0, 0] : undefined })
|
|
915
|
+
.then(() => finalize(output))
|
|
916
|
+
.catch(err => errorResponse(err));
|
|
917
|
+
})
|
|
918
|
+
.catch(err => errorResponse(err));
|
|
919
|
+
}
|
|
920
|
+
})
|
|
921
|
+
.catch(err => errorResponse(err));
|
|
922
|
+
}
|
|
923
|
+
else {
|
|
924
|
+
resolve();
|
|
925
|
+
}
|
|
926
|
+
return true;
|
|
927
|
+
}
|
|
928
|
+
catch (err) {
|
|
929
|
+
if (loaded) {
|
|
930
|
+
this.writeFail(["Unknown" /* ERR_MESSAGE.UNKNOWN */, 'node-webpmux'], err, { type: 2048 /* LOG_TYPE.IMAGE */, startTime });
|
|
931
|
+
}
|
|
932
|
+
else if (this.checkPackage(err, 'node-webpmux@3', 2048 /* LOG_TYPE.IMAGE */)) {
|
|
933
|
+
resolve();
|
|
934
|
+
return true;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
return false;
|
|
938
|
+
};
|
|
939
|
+
const { path: webp_path } = (_a = this.settings).webp || (_a.webp = {});
|
|
940
|
+
const bmpFile = getTempPath.call(this, 'bmp');
|
|
941
|
+
try {
|
|
942
|
+
child_process.execFile((0, util_1.getWebP_bin)('dwebp', webp_path), [(0, util_1.normalizePath)(localUri), '-bmp', '-o', (0, util_1.normalizePath)(bmpFile)], { shell: true, signal: this.signal }, err => {
|
|
943
|
+
if (!err) {
|
|
944
|
+
transformBuffer(bmpFile);
|
|
945
|
+
}
|
|
946
|
+
else {
|
|
947
|
+
removeFile(bmpFile);
|
|
948
|
+
tryWebpMux().then(valid => !valid && errorResponse(err));
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
catch (err) {
|
|
953
|
+
if (!await tryWebpMux()) {
|
|
954
|
+
if (this.checkPackage(err, 'dwebp-bin@1', 2048 /* LOG_TYPE.IMAGE */)) {
|
|
955
|
+
resolve();
|
|
956
|
+
}
|
|
957
|
+
else {
|
|
958
|
+
errorResponse(err);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
else {
|
|
964
|
+
transformBuffer();
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
get settings() {
|
|
969
|
+
var _a;
|
|
970
|
+
return (_a = this.module).settings || (_a.settings = {});
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
module.exports = Jimp;
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pi-r/jimp",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Jimp image constructor for E-mc.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/anpham6/pi-r.git",
|
|
12
|
+
"directory": "src/module/jimp"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"squared",
|
|
16
|
+
"e-mc",
|
|
17
|
+
"squared-functions"
|
|
18
|
+
],
|
|
19
|
+
"author": "An Pham <anpham6@gmail.com>",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"homepage": "https://github.com/anpham6/pi-r#readme",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@e-mc/image": "^0.4.0",
|
|
24
|
+
"@e-mc/types": "^0.4.0",
|
|
25
|
+
"bmp-js": "^0.1.0",
|
|
26
|
+
"gifwrap": "^0.9.4",
|
|
27
|
+
"jimp": "^0.22.7"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { IHost, IImage, ImageConstructor } from '@e-mc/types/lib';
|
|
2
|
+
import type { ImageModule } from '@e-mc/types/lib/settings';
|
|
3
|
+
|
|
4
|
+
import type { ImageHandler } from '@e-mc/image/types';
|
|
5
|
+
|
|
6
|
+
import type * as jimp from 'jimp';
|
|
7
|
+
|
|
8
|
+
export interface IJimpHandler<T extends IHost = IHost, U extends ImageModule = ImageModule> extends ImageHandler<jimp, T, IImage<T, U>> {
|
|
9
|
+
method(): Promise<void>;
|
|
10
|
+
rotate(localFile?: string, callback?: ResultCallback<string>): Promise<this>;
|
|
11
|
+
background(value: number | [number, number, number, number]): void;
|
|
12
|
+
writeAsync(output: string, callback?: ResultCallback): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface JimpImageConstructor extends ImageConstructor {
|
|
16
|
+
new(handler: jimp, instance: IImage, host?: Null<IHost>): IJimpHandler;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type ResultCallback<T = unknown, U = void, V = unknown> = (err: V, result: T) => U;
|
package/util.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare namespace util {
|
|
2
|
+
function parseFormat(command: string, mimeType?: string, gif?: boolean): [string, string, string];
|
|
3
|
+
function renameExt(output: string, ext: string, replace?: boolean): string;
|
|
4
|
+
function normalizePath(value: string): string;
|
|
5
|
+
function getWebP_bin(name: string, pathname: string | undefined): string;
|
|
6
|
+
function showInputType(value: string | undefined, outputType: string, finalAs: string): string;
|
|
7
|
+
function showOutputType(value: string | undefined, outputType: string, finalAs: string): string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export = util;
|
package/util.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.showOutputType = exports.showInputType = exports.getWebP_bin = exports.normalizePath = exports.renameExt = exports.parseFormat = exports.MIME_OUTPUT = exports.MIME_INPUT = void 0;
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const jimp = require("jimp");
|
|
7
|
+
const types_1 = require("@e-mc/types");
|
|
8
|
+
const Image = require("@e-mc/image");
|
|
9
|
+
exports.MIME_INPUT = new Set([jimp.MIME_PNG, jimp.MIME_JPEG, jimp.MIME_BMP, jimp.MIME_GIF, jimp.MIME_TIFF, "image/webp" /* STRINGS.MIME_WEBP */]);
|
|
10
|
+
exports.MIME_OUTPUT = new Set([jimp.MIME_PNG, jimp.MIME_JPEG, jimp.MIME_BMP, jimp.MIME_GIF, "image/webp" /* STRINGS.MIME_WEBP */]);
|
|
11
|
+
function parseFormat(command, mimeType, gif) {
|
|
12
|
+
command = command.toLowerCase();
|
|
13
|
+
for (let mime of exports.MIME_OUTPUT) {
|
|
14
|
+
let saveAs = mime.split('/')[1];
|
|
15
|
+
if (command.startsWith(saveAs)) {
|
|
16
|
+
let finalAs = '';
|
|
17
|
+
if (saveAs !== 'gif') {
|
|
18
|
+
switch (saveAs) {
|
|
19
|
+
case 'jpeg':
|
|
20
|
+
saveAs = 'jpg';
|
|
21
|
+
break;
|
|
22
|
+
case 'webp':
|
|
23
|
+
if (mimeType === "image/webp" /* STRINGS.MIME_WEBP */) {
|
|
24
|
+
try {
|
|
25
|
+
require('node-webpmux');
|
|
26
|
+
mime = "image/webp" /* STRINGS.MIME_WEBP */;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (mimeType === jimp.MIME_JPEG) {
|
|
33
|
+
mime = jimp.MIME_JPEG;
|
|
34
|
+
saveAs = 'jpg';
|
|
35
|
+
}
|
|
36
|
+
else if (gif && mimeType === jimp.MIME_GIF) {
|
|
37
|
+
mime = jimp.MIME_GIF;
|
|
38
|
+
saveAs = 'gif';
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
mime = jimp.MIME_PNG;
|
|
42
|
+
saveAs = 'png';
|
|
43
|
+
}
|
|
44
|
+
finalAs = 'webp';
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (!gif) {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
return [mime, saveAs, finalAs];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return ['', '', ''];
|
|
55
|
+
}
|
|
56
|
+
exports.parseFormat = parseFormat;
|
|
57
|
+
function renameExt(output, ext, replace) {
|
|
58
|
+
let result = (0, types_1.renameExt)(output.replace('.__copy__.', '.'), ext);
|
|
59
|
+
if (!replace) {
|
|
60
|
+
const pathname = result;
|
|
61
|
+
let i = 0;
|
|
62
|
+
while (Image.isPath(result)) {
|
|
63
|
+
result = pathname.substring(0, pathname.lastIndexOf('.') + 1) + `(${++i}).` + ext;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
exports.renameExt = renameExt;
|
|
69
|
+
function normalizePath(value) {
|
|
70
|
+
return '"' + value.replace(/"/g, '\\"') + '"';
|
|
71
|
+
}
|
|
72
|
+
exports.normalizePath = normalizePath;
|
|
73
|
+
function getWebP_bin(name, pathname) {
|
|
74
|
+
if (pathname && fs.existsSync(pathname)) {
|
|
75
|
+
name += process.platform === 'win32' ? '.exe' : '';
|
|
76
|
+
const bin = path.join(pathname, name);
|
|
77
|
+
return Image.sanitizeCmd(fs.existsSync(bin) ? bin : path.join(pathname, 'bin', name));
|
|
78
|
+
}
|
|
79
|
+
return require(name + '-bin');
|
|
80
|
+
}
|
|
81
|
+
exports.getWebP_bin = getWebP_bin;
|
|
82
|
+
function showInputType(value, outputType, finalAs) {
|
|
83
|
+
if (finalAs) {
|
|
84
|
+
outputType = 'image/' + finalAs;
|
|
85
|
+
}
|
|
86
|
+
return value && outputType !== value ? value.split('/').pop() + ' -> ' : '';
|
|
87
|
+
}
|
|
88
|
+
exports.showInputType = showInputType;
|
|
89
|
+
function showOutputType(value, outputType, finalAs) {
|
|
90
|
+
if (finalAs) {
|
|
91
|
+
outputType = 'image/' + finalAs;
|
|
92
|
+
}
|
|
93
|
+
return value !== outputType ? ' -> ' + outputType.split('/').pop() : '';
|
|
94
|
+
}
|
|
95
|
+
exports.showOutputType = showOutputType;
|