sharp 0.33.0-alpha.8 → 0.33.0-rc.2
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/README.md +6 -2
- package/lib/colour.js +4 -6
- package/lib/constructor.js +9 -7
- package/lib/index.d.ts +79 -16
- package/lib/input.js +34 -8
- package/lib/is.js +15 -1
- package/lib/libvips.js +26 -8
- package/lib/output.js +210 -59
- package/lib/resize.js +42 -45
- package/lib/sharp.js +70 -53
- package/lib/utility.js +10 -4
- package/package.json +36 -23
- package/src/binding.gyp +37 -27
- package/src/common.cc +48 -1
- package/src/common.h +18 -3
- package/src/metadata.cc +3 -3
- package/src/operations.cc +35 -18
- package/src/operations.h +3 -3
- package/src/pipeline.cc +64 -54
- package/src/pipeline.h +13 -9
- package/src/stats.cc +3 -3
- package/src/utilities.cc +5 -0
package/lib/output.js
CHANGED
|
@@ -86,7 +86,8 @@ function toFile (fileOut, callback) {
|
|
|
86
86
|
}
|
|
87
87
|
} else {
|
|
88
88
|
this.options.fileOut = fileOut;
|
|
89
|
-
|
|
89
|
+
const stack = Error();
|
|
90
|
+
return this._pipeline(callback, stack);
|
|
90
91
|
}
|
|
91
92
|
return this;
|
|
92
93
|
}
|
|
@@ -157,43 +158,190 @@ function toBuffer (options, callback) {
|
|
|
157
158
|
this.options.resolveWithObject = false;
|
|
158
159
|
}
|
|
159
160
|
this.options.fileOut = '';
|
|
160
|
-
|
|
161
|
+
const stack = Error();
|
|
162
|
+
return this._pipeline(is.fn(options) ? options : callback, stack);
|
|
161
163
|
}
|
|
162
164
|
|
|
163
165
|
/**
|
|
164
|
-
*
|
|
165
|
-
* This will also convert to and add a web-friendly sRGB ICC profile if appropriate,
|
|
166
|
-
* unless a custom output profile is provided.
|
|
167
|
-
*
|
|
168
|
-
* The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
|
169
|
-
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
|
166
|
+
* Keep all EXIF metadata from the input image in the output image.
|
|
170
167
|
*
|
|
171
168
|
* EXIF metadata is unsupported for TIFF output.
|
|
172
169
|
*
|
|
170
|
+
* @since 0.33.0
|
|
171
|
+
*
|
|
173
172
|
* @example
|
|
174
|
-
* sharp(
|
|
175
|
-
* .
|
|
176
|
-
* .
|
|
177
|
-
*
|
|
173
|
+
* const outputWithExif = await sharp(inputWithExif)
|
|
174
|
+
* .keepExif()
|
|
175
|
+
* .toBuffer();
|
|
176
|
+
*
|
|
177
|
+
* @returns {Sharp}
|
|
178
|
+
*/
|
|
179
|
+
function keepExif () {
|
|
180
|
+
this.options.keepMetadata |= 0b00001;
|
|
181
|
+
return this;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Set EXIF metadata in the output image, ignoring any EXIF in the input image.
|
|
186
|
+
*
|
|
187
|
+
* @since 0.33.0
|
|
178
188
|
*
|
|
179
189
|
* @example
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* GPSLongitudeRef: 'W',
|
|
191
|
-
* GPSLongitude: '0/1 7/1 4366/100'
|
|
192
|
-
* }
|
|
190
|
+
* const dataWithExif = await sharp(input)
|
|
191
|
+
* .withExif({
|
|
192
|
+
* IFD0: {
|
|
193
|
+
* Copyright: 'The National Gallery'
|
|
194
|
+
* },
|
|
195
|
+
* IFD3: {
|
|
196
|
+
* GPSLatitudeRef: 'N',
|
|
197
|
+
* GPSLatitude: '51/1 30/1 3230/100',
|
|
198
|
+
* GPSLongitudeRef: 'W',
|
|
199
|
+
* GPSLongitude: '0/1 7/1 4366/100'
|
|
193
200
|
* }
|
|
194
201
|
* })
|
|
195
202
|
* .toBuffer();
|
|
196
203
|
*
|
|
204
|
+
* @param {Object<string, Object<string, string>>} exif Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
|
|
205
|
+
* @returns {Sharp}
|
|
206
|
+
* @throws {Error} Invalid parameters
|
|
207
|
+
*/
|
|
208
|
+
function withExif (exif) {
|
|
209
|
+
if (is.object(exif)) {
|
|
210
|
+
for (const [ifd, entries] of Object.entries(exif)) {
|
|
211
|
+
if (is.object(entries)) {
|
|
212
|
+
for (const [k, v] of Object.entries(entries)) {
|
|
213
|
+
if (is.string(v)) {
|
|
214
|
+
this.options.withExif[`exif-${ifd.toLowerCase()}-${k}`] = v;
|
|
215
|
+
} else {
|
|
216
|
+
throw is.invalidParameterError(`${ifd}.${k}`, 'string', v);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
throw is.invalidParameterError(ifd, 'object', entries);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
throw is.invalidParameterError('exif', 'object', exif);
|
|
225
|
+
}
|
|
226
|
+
this.options.withExifMerge = false;
|
|
227
|
+
return this.keepExif();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Update EXIF metadata from the input image in the output image.
|
|
232
|
+
*
|
|
233
|
+
* @since 0.33.0
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* const dataWithMergedExif = await sharp(inputWithExif)
|
|
237
|
+
* .withExifMerge({
|
|
238
|
+
* IFD0: {
|
|
239
|
+
* Copyright: 'The National Gallery'
|
|
240
|
+
* }
|
|
241
|
+
* })
|
|
242
|
+
* .toBuffer();
|
|
243
|
+
*
|
|
244
|
+
* @param {Object<string, Object<string, string>>} exif Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
|
|
245
|
+
* @returns {Sharp}
|
|
246
|
+
* @throws {Error} Invalid parameters
|
|
247
|
+
*/
|
|
248
|
+
function withExifMerge (exif) {
|
|
249
|
+
this.withExif(exif);
|
|
250
|
+
this.options.withExifMerge = true;
|
|
251
|
+
return this;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Keep ICC profile from the input image in the output image.
|
|
256
|
+
*
|
|
257
|
+
* Where necessary, will attempt to convert the output colour space to match the profile.
|
|
258
|
+
*
|
|
259
|
+
* @since 0.33.0
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* const outputWithIccProfile = await sharp(inputWithIccProfile)
|
|
263
|
+
* .keepIccProfile()
|
|
264
|
+
* .toBuffer();
|
|
265
|
+
*
|
|
266
|
+
* @returns {Sharp}
|
|
267
|
+
*/
|
|
268
|
+
function keepIccProfile () {
|
|
269
|
+
this.options.keepMetadata |= 0b01000;
|
|
270
|
+
return this;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Transform using an ICC profile and attach to the output image.
|
|
275
|
+
*
|
|
276
|
+
* This can either be an absolute filesystem path or
|
|
277
|
+
* built-in profile name (`srgb`, `p3`, `cmyk`).
|
|
278
|
+
*
|
|
279
|
+
* @since 0.33.0
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* const outputWithP3 = await sharp(input)
|
|
283
|
+
* .withIccProfile('p3')
|
|
284
|
+
* .toBuffer();
|
|
285
|
+
*
|
|
286
|
+
* @param {string} icc - Absolute filesystem path to output ICC profile or built-in profile name (srgb, p3, cmyk).
|
|
287
|
+
* @param {Object} [options]
|
|
288
|
+
* @param {number} [options.attach=true] Should the ICC profile be included in the output image metadata?
|
|
289
|
+
* @returns {Sharp}
|
|
290
|
+
* @throws {Error} Invalid parameters
|
|
291
|
+
*/
|
|
292
|
+
function withIccProfile (icc, options) {
|
|
293
|
+
if (is.string(icc)) {
|
|
294
|
+
this.options.withIccProfile = icc;
|
|
295
|
+
} else {
|
|
296
|
+
throw is.invalidParameterError('icc', 'string', icc);
|
|
297
|
+
}
|
|
298
|
+
this.keepIccProfile();
|
|
299
|
+
if (is.object(options)) {
|
|
300
|
+
if (is.defined(options.attach)) {
|
|
301
|
+
if (is.bool(options.attach)) {
|
|
302
|
+
if (!options.attach) {
|
|
303
|
+
this.options.keepMetadata &= ~0b01000;
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
throw is.invalidParameterError('attach', 'boolean', options.attach);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return this;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Keep all metadata (EXIF, ICC, XMP, IPTC) from the input image in the output image.
|
|
315
|
+
*
|
|
316
|
+
* The default behaviour, when `keepMetadata` is not used, is to convert to the device-independent
|
|
317
|
+
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
|
318
|
+
*
|
|
319
|
+
* @since 0.33.0
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* const outputWithMetadata = await sharp(inputWithMetadata)
|
|
323
|
+
* .keepMetadata()
|
|
324
|
+
* .toBuffer();
|
|
325
|
+
*
|
|
326
|
+
* @returns {Sharp}
|
|
327
|
+
*/
|
|
328
|
+
function keepMetadata () {
|
|
329
|
+
this.options.keepMetadata = 0b11111;
|
|
330
|
+
return this;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Keep most metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
|
335
|
+
*
|
|
336
|
+
* This will also convert to and add a web-friendly sRGB ICC profile if appropriate.
|
|
337
|
+
*
|
|
338
|
+
* Allows orientation and density to be set or updated.
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* const outputSrgbWithMetadata = await sharp(inputRgbWithMetadata)
|
|
342
|
+
* .withMetadata()
|
|
343
|
+
* .toBuffer();
|
|
344
|
+
*
|
|
197
345
|
* @example
|
|
198
346
|
* // Set output metadata to 96 DPI
|
|
199
347
|
* const data = await sharp(input)
|
|
@@ -201,15 +349,14 @@ function toBuffer (options, callback) {
|
|
|
201
349
|
* .toBuffer();
|
|
202
350
|
*
|
|
203
351
|
* @param {Object} [options]
|
|
204
|
-
* @param {number} [options.orientation]
|
|
205
|
-
* @param {string} [options.icc='srgb'] Filesystem path to output ICC profile, relative to `process.cwd()`, defaults to built-in sRGB.
|
|
206
|
-
* @param {Object<Object>} [options.exif={}] Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
|
|
352
|
+
* @param {number} [options.orientation] Used to update the EXIF `Orientation` tag, integer between 1 and 8.
|
|
207
353
|
* @param {number} [options.density] Number of pixels per inch (DPI).
|
|
208
354
|
* @returns {Sharp}
|
|
209
355
|
* @throws {Error} Invalid parameters
|
|
210
356
|
*/
|
|
211
357
|
function withMetadata (options) {
|
|
212
|
-
this.
|
|
358
|
+
this.keepMetadata();
|
|
359
|
+
this.withIccProfile('srgb');
|
|
213
360
|
if (is.object(options)) {
|
|
214
361
|
if (is.defined(options.orientation)) {
|
|
215
362
|
if (is.integer(options.orientation) && is.inRange(options.orientation, 1, 8)) {
|
|
@@ -226,30 +373,10 @@ function withMetadata (options) {
|
|
|
226
373
|
}
|
|
227
374
|
}
|
|
228
375
|
if (is.defined(options.icc)) {
|
|
229
|
-
|
|
230
|
-
this.options.withMetadataIcc = options.icc;
|
|
231
|
-
} else {
|
|
232
|
-
throw is.invalidParameterError('icc', 'string filesystem path to ICC profile', options.icc);
|
|
233
|
-
}
|
|
376
|
+
this.withIccProfile(options.icc);
|
|
234
377
|
}
|
|
235
378
|
if (is.defined(options.exif)) {
|
|
236
|
-
|
|
237
|
-
for (const [ifd, entries] of Object.entries(options.exif)) {
|
|
238
|
-
if (is.object(entries)) {
|
|
239
|
-
for (const [k, v] of Object.entries(entries)) {
|
|
240
|
-
if (is.string(v)) {
|
|
241
|
-
this.options.withMetadataStrs[`exif-${ifd.toLowerCase()}-${k}`] = v;
|
|
242
|
-
} else {
|
|
243
|
-
throw is.invalidParameterError(`exif.${ifd}.${k}`, 'string', v);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
} else {
|
|
247
|
-
throw is.invalidParameterError(`exif.${ifd}`, 'object', entries);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
} else {
|
|
251
|
-
throw is.invalidParameterError('exif', 'object', options.exif);
|
|
252
|
-
}
|
|
379
|
+
this.withExifMerge(options.exif);
|
|
253
380
|
}
|
|
254
381
|
}
|
|
255
382
|
return this;
|
|
@@ -780,6 +907,7 @@ function trySetAnimationOptions (source, target) {
|
|
|
780
907
|
* @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
|
|
781
908
|
* @param {string} [options.resolutionUnit='inch'] - resolution unit options: inch, cm
|
|
782
909
|
* @param {number} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
|
|
910
|
+
* @param {boolean} [options.miniswhite=false] - write 1-bit images as miniswhite
|
|
783
911
|
* @returns {Sharp}
|
|
784
912
|
* @throws {Error} Invalid options
|
|
785
913
|
*/
|
|
@@ -817,6 +945,10 @@ function tiff (options) {
|
|
|
817
945
|
throw is.invalidParameterError('tileHeight', 'integer greater than zero', options.tileHeight);
|
|
818
946
|
}
|
|
819
947
|
}
|
|
948
|
+
// miniswhite
|
|
949
|
+
if (is.defined(options.miniswhite)) {
|
|
950
|
+
this._setBooleanOption('tiffMiniswhite', options.miniswhite);
|
|
951
|
+
}
|
|
820
952
|
// pyramid
|
|
821
953
|
if (is.defined(options.pyramid)) {
|
|
822
954
|
this._setBooleanOption('tiffPyramid', options.pyramid);
|
|
@@ -1282,7 +1414,8 @@ function _read () {
|
|
|
1282
1414
|
/* istanbul ignore else */
|
|
1283
1415
|
if (!this.options.streamOut) {
|
|
1284
1416
|
this.options.streamOut = true;
|
|
1285
|
-
|
|
1417
|
+
const stack = Error();
|
|
1418
|
+
this._pipeline(undefined, stack);
|
|
1286
1419
|
}
|
|
1287
1420
|
}
|
|
1288
1421
|
|
|
@@ -1291,18 +1424,30 @@ function _read () {
|
|
|
1291
1424
|
* Supports callback, stream and promise variants
|
|
1292
1425
|
* @private
|
|
1293
1426
|
*/
|
|
1294
|
-
function _pipeline (callback) {
|
|
1427
|
+
function _pipeline (callback, stack) {
|
|
1295
1428
|
if (typeof callback === 'function') {
|
|
1296
1429
|
// output=file/buffer
|
|
1297
1430
|
if (this._isStreamInput()) {
|
|
1298
1431
|
// output=file/buffer, input=stream
|
|
1299
1432
|
this.on('finish', () => {
|
|
1300
1433
|
this._flattenBufferIn();
|
|
1301
|
-
sharp.pipeline(this.options,
|
|
1434
|
+
sharp.pipeline(this.options, (err, data, info) => {
|
|
1435
|
+
if (err) {
|
|
1436
|
+
callback(is.nativeError(err, stack));
|
|
1437
|
+
} else {
|
|
1438
|
+
callback(null, data, info);
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1302
1441
|
});
|
|
1303
1442
|
} else {
|
|
1304
1443
|
// output=file/buffer, input=file/buffer
|
|
1305
|
-
sharp.pipeline(this.options,
|
|
1444
|
+
sharp.pipeline(this.options, (err, data, info) => {
|
|
1445
|
+
if (err) {
|
|
1446
|
+
callback(is.nativeError(err, stack));
|
|
1447
|
+
} else {
|
|
1448
|
+
callback(null, data, info);
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1306
1451
|
}
|
|
1307
1452
|
return this;
|
|
1308
1453
|
} else if (this.options.streamOut) {
|
|
@@ -1313,7 +1458,7 @@ function _pipeline (callback) {
|
|
|
1313
1458
|
this._flattenBufferIn();
|
|
1314
1459
|
sharp.pipeline(this.options, (err, data, info) => {
|
|
1315
1460
|
if (err) {
|
|
1316
|
-
this.emit('error', err);
|
|
1461
|
+
this.emit('error', is.nativeError(err, stack));
|
|
1317
1462
|
} else {
|
|
1318
1463
|
this.emit('info', info);
|
|
1319
1464
|
this.push(data);
|
|
@@ -1329,7 +1474,7 @@ function _pipeline (callback) {
|
|
|
1329
1474
|
// output=stream, input=file/buffer
|
|
1330
1475
|
sharp.pipeline(this.options, (err, data, info) => {
|
|
1331
1476
|
if (err) {
|
|
1332
|
-
this.emit('error', err);
|
|
1477
|
+
this.emit('error', is.nativeError(err, stack));
|
|
1333
1478
|
} else {
|
|
1334
1479
|
this.emit('info', info);
|
|
1335
1480
|
this.push(data);
|
|
@@ -1348,7 +1493,7 @@ function _pipeline (callback) {
|
|
|
1348
1493
|
this._flattenBufferIn();
|
|
1349
1494
|
sharp.pipeline(this.options, (err, data, info) => {
|
|
1350
1495
|
if (err) {
|
|
1351
|
-
reject(err);
|
|
1496
|
+
reject(is.nativeError(err, stack));
|
|
1352
1497
|
} else {
|
|
1353
1498
|
if (this.options.resolveWithObject) {
|
|
1354
1499
|
resolve({ data, info });
|
|
@@ -1364,7 +1509,7 @@ function _pipeline (callback) {
|
|
|
1364
1509
|
return new Promise((resolve, reject) => {
|
|
1365
1510
|
sharp.pipeline(this.options, (err, data, info) => {
|
|
1366
1511
|
if (err) {
|
|
1367
|
-
reject(err);
|
|
1512
|
+
reject(is.nativeError(err, stack));
|
|
1368
1513
|
} else {
|
|
1369
1514
|
if (this.options.resolveWithObject) {
|
|
1370
1515
|
resolve({ data, info });
|
|
@@ -1387,6 +1532,12 @@ module.exports = function (Sharp) {
|
|
|
1387
1532
|
// Public
|
|
1388
1533
|
toFile,
|
|
1389
1534
|
toBuffer,
|
|
1535
|
+
keepExif,
|
|
1536
|
+
withExif,
|
|
1537
|
+
withExifMerge,
|
|
1538
|
+
keepIccProfile,
|
|
1539
|
+
withIccProfile,
|
|
1540
|
+
keepMetadata,
|
|
1390
1541
|
withMetadata,
|
|
1391
1542
|
toFormat,
|
|
1392
1543
|
jpeg,
|
package/lib/resize.js
CHANGED
|
@@ -494,70 +494,67 @@ function extract (options) {
|
|
|
494
494
|
*
|
|
495
495
|
* If the result of this operation would trim an image to nothing then no change is made.
|
|
496
496
|
*
|
|
497
|
-
* The `info` response Object
|
|
498
|
-
* will contain `trimOffsetLeft` and `trimOffsetTop` properties.
|
|
497
|
+
* The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
|
|
499
498
|
*
|
|
500
499
|
* @example
|
|
501
500
|
* // Trim pixels with a colour similar to that of the top-left pixel.
|
|
502
|
-
* sharp(input)
|
|
501
|
+
* await sharp(input)
|
|
503
502
|
* .trim()
|
|
504
|
-
* .toFile(output
|
|
505
|
-
*
|
|
506
|
-
* });
|
|
503
|
+
* .toFile(output);
|
|
504
|
+
*
|
|
507
505
|
* @example
|
|
508
506
|
* // Trim pixels with the exact same colour as that of the top-left pixel.
|
|
509
|
-
* sharp(input)
|
|
510
|
-
* .trim(
|
|
511
|
-
*
|
|
512
|
-
*
|
|
513
|
-
*
|
|
507
|
+
* await sharp(input)
|
|
508
|
+
* .trim({
|
|
509
|
+
* threshold: 0
|
|
510
|
+
* })
|
|
511
|
+
* .toFile(output);
|
|
512
|
+
*
|
|
514
513
|
* @example
|
|
515
|
-
* //
|
|
516
|
-
* sharp(input)
|
|
517
|
-
* .trim(
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
* })
|
|
514
|
+
* // Assume input is line art and trim only pixels with a similar colour to red.
|
|
515
|
+
* const output = await sharp(input)
|
|
516
|
+
* .trim({
|
|
517
|
+
* background: "#FF0000",
|
|
518
|
+
* lineArt: true
|
|
519
|
+
* })
|
|
520
|
+
* .toBuffer();
|
|
521
|
+
*
|
|
521
522
|
* @example
|
|
522
523
|
* // Trim all "yellow-ish" pixels, being more lenient with the higher threshold.
|
|
523
|
-
* sharp(input)
|
|
524
|
+
* const output = await sharp(input)
|
|
524
525
|
* .trim({
|
|
525
526
|
* background: "yellow",
|
|
526
527
|
* threshold: 42,
|
|
527
528
|
* })
|
|
528
|
-
* .
|
|
529
|
-
* ...
|
|
530
|
-
* });
|
|
529
|
+
* .toBuffer();
|
|
531
530
|
*
|
|
532
|
-
* @param {
|
|
533
|
-
* @param {string|Object} [
|
|
534
|
-
* @param {number} [
|
|
531
|
+
* @param {Object} [options]
|
|
532
|
+
* @param {string|Object} [options.background='top-left pixel'] - Background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel.
|
|
533
|
+
* @param {number} [options.threshold=10] - Allowed difference from the above colour, a positive number.
|
|
534
|
+
* @param {boolean} [options.lineArt=false] - Does the input more closely resemble line art (e.g. vector) rather than being photographic?
|
|
535
535
|
* @returns {Sharp}
|
|
536
536
|
* @throws {Error} Invalid parameters
|
|
537
537
|
*/
|
|
538
|
-
function trim (
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
this.options.trimThreshold = trim.threshold;
|
|
538
|
+
function trim (options) {
|
|
539
|
+
this.options.trimThreshold = 10;
|
|
540
|
+
if (is.defined(options)) {
|
|
541
|
+
if (is.object(options)) {
|
|
542
|
+
if (is.defined(options.background)) {
|
|
543
|
+
this._setBackgroundColourOption('trimBackground', options.background);
|
|
544
|
+
}
|
|
545
|
+
if (is.defined(options.threshold)) {
|
|
546
|
+
if (is.number(options.threshold) && options.threshold >= 0) {
|
|
547
|
+
this.options.trimThreshold = options.threshold;
|
|
548
|
+
} else {
|
|
549
|
+
throw is.invalidParameterError('threshold', 'positive number', options.threshold);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (is.defined(options.lineArt)) {
|
|
553
|
+
this._setBooleanOption('trimLineArt', options.lineArt);
|
|
554
|
+
}
|
|
556
555
|
} else {
|
|
557
|
-
throw is.invalidParameterError('
|
|
556
|
+
throw is.invalidParameterError('trim', 'object', options);
|
|
558
557
|
}
|
|
559
|
-
} else {
|
|
560
|
-
throw is.invalidParameterError('trim', 'string, number or object', trim);
|
|
561
558
|
}
|
|
562
559
|
if (isRotationExpected(this.options)) {
|
|
563
560
|
this.options.rotateBeforePreExtract = true;
|
package/lib/sharp.js
CHANGED
|
@@ -9,61 +9,78 @@ const { familySync, versionSync } = require('detect-libc');
|
|
|
9
9
|
|
|
10
10
|
const { runtimePlatformArch, prebuiltPlatforms, minimumLibvipsVersion } = require('./libvips');
|
|
11
11
|
const runtimePlatform = runtimePlatformArch();
|
|
12
|
-
const [isLinux, isMacOs, isWindows] = ['linux', 'darwin', 'win32'].map(os => runtimePlatform.startsWith(os));
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
const paths = [
|
|
14
|
+
`../src/build/Release/sharp-${runtimePlatform}.node`,
|
|
15
|
+
'../src/build/Release/sharp-wasm32.node',
|
|
16
|
+
`@img/sharp-${runtimePlatform}/sharp.node`,
|
|
17
|
+
'@img/sharp-wasm32/sharp.node'
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
let sharp;
|
|
21
|
+
const errors = [];
|
|
22
|
+
for (const path of paths) {
|
|
19
23
|
try {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} catch (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (isLinux && /symbol not found/i.test(errPackage)) {
|
|
39
|
-
try {
|
|
40
|
-
const { engines } = require(`@sharpen/sharp-libvips-${runtimePlatform}/package`);
|
|
41
|
-
const libcFound = `${familySync()} ${versionSync()}`;
|
|
42
|
-
const libcRequires = `${engines.musl ? 'musl' : 'glibc'} ${engines.musl || engines.glibc}`;
|
|
43
|
-
help.push('- Update your OS:');
|
|
44
|
-
help.push(` Found ${libcFound}`);
|
|
45
|
-
help.push(` Requires ${libcRequires}`);
|
|
46
|
-
} catch (errEngines) {}
|
|
47
|
-
}
|
|
48
|
-
if (isMacOs && /Incompatible library version/.test(errLocal.message)) {
|
|
49
|
-
help.push('- Update Homebrew:');
|
|
50
|
-
help.push(' brew update && brew upgrade vips');
|
|
51
|
-
}
|
|
52
|
-
if (errPackage.code === 'ERR_DLOPEN_DISABLED') {
|
|
53
|
-
help.push('- Run Node.js without using the --no-addons flag');
|
|
54
|
-
}
|
|
55
|
-
if (process.versions.pnp) {
|
|
56
|
-
help.push('- Use a supported yarn linker, either pnpm or node-modules:');
|
|
57
|
-
help.push(' yarn config set nodeLinker node-modules');
|
|
58
|
-
}
|
|
59
|
-
// Link to installation docs
|
|
60
|
-
if (isLinux && /Module did not self-register/.test(errLocal.message + errPackage.message)) {
|
|
61
|
-
help.push('- Using worker threads on Linux? See https://sharp.pixelplumbing.com/install#worker-threads');
|
|
62
|
-
} else if (isWindows && /The specified procedure could not be found/.test(errPackage.message)) {
|
|
63
|
-
help.push('- Using the canvas package on Windows? See https://sharp.pixelplumbing.com/install#canvas-and-windows');
|
|
64
|
-
} else {
|
|
65
|
-
help.push('- Consult the installation documentation: https://sharp.pixelplumbing.com/install');
|
|
24
|
+
sharp = require(path);
|
|
25
|
+
break;
|
|
26
|
+
} catch (err) {
|
|
27
|
+
/* istanbul ignore next */
|
|
28
|
+
errors.push(err);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* istanbul ignore next */
|
|
33
|
+
if (sharp) {
|
|
34
|
+
module.exports = sharp;
|
|
35
|
+
} else {
|
|
36
|
+
const [isLinux, isMacOs, isWindows] = ['linux', 'darwin', 'win32'].map(os => runtimePlatform.startsWith(os));
|
|
37
|
+
|
|
38
|
+
const help = [`Could not load the "sharp" module using the ${runtimePlatform} runtime`];
|
|
39
|
+
errors.forEach(err => {
|
|
40
|
+
if (err.code !== 'MODULE_NOT_FOUND') {
|
|
41
|
+
help.push(`${err.code}: ${err.message}`);
|
|
66
42
|
}
|
|
67
|
-
|
|
43
|
+
});
|
|
44
|
+
const messages = errors.map(err => err.message).join(' ');
|
|
45
|
+
help.push('Possible solutions:');
|
|
46
|
+
// Common error messages
|
|
47
|
+
if (prebuiltPlatforms.includes(runtimePlatform)) {
|
|
48
|
+
const [os, cpu] = runtimePlatform.split('-');
|
|
49
|
+
help.push('- Add platform-specific dependencies:');
|
|
50
|
+
help.push(` npm install --os=${os} --cpu=${cpu} sharp`);
|
|
51
|
+
help.push(' or');
|
|
52
|
+
help.push(` npm install --force @img/sharp-${runtimePlatform}`);
|
|
53
|
+
} else {
|
|
54
|
+
help.push(`- Manually install libvips >= ${minimumLibvipsVersion}`);
|
|
55
|
+
help.push('- Add experimental WebAssembly-based dependencies:');
|
|
56
|
+
help.push(' npm install --cpu=wasm32 sharp');
|
|
57
|
+
}
|
|
58
|
+
if (isLinux && /symbol not found/i.test(messages)) {
|
|
59
|
+
try {
|
|
60
|
+
const { engines } = require(`@img/sharp-libvips-${runtimePlatform}/package`);
|
|
61
|
+
const libcFound = `${familySync()} ${versionSync()}`;
|
|
62
|
+
const libcRequires = `${engines.musl ? 'musl' : 'glibc'} ${engines.musl || engines.glibc}`;
|
|
63
|
+
help.push('- Update your OS:');
|
|
64
|
+
help.push(` Found ${libcFound}`);
|
|
65
|
+
help.push(` Requires ${libcRequires}`);
|
|
66
|
+
} catch (errEngines) {}
|
|
67
|
+
}
|
|
68
|
+
if (isMacOs && /Incompatible library version/.test(messages)) {
|
|
69
|
+
help.push('- Update Homebrew:');
|
|
70
|
+
help.push(' brew update && brew upgrade vips');
|
|
71
|
+
}
|
|
72
|
+
if (errors.some(err => err.code === 'ERR_DLOPEN_DISABLED')) {
|
|
73
|
+
help.push('- Run Node.js without using the --no-addons flag');
|
|
74
|
+
}
|
|
75
|
+
if (process.versions.pnp) {
|
|
76
|
+
help.push('- Use a supported yarn linker, either pnpm or node-modules:');
|
|
77
|
+
help.push(' yarn config set nodeLinker node-modules');
|
|
78
|
+
}
|
|
79
|
+
// Link to installation docs
|
|
80
|
+
if (isWindows && /The specified procedure could not be found/.test(messages)) {
|
|
81
|
+
help.push('- Using the canvas package on Windows? See https://sharp.pixelplumbing.com/install#canvas-and-windows');
|
|
82
|
+
} else {
|
|
83
|
+
help.push('- Consult the installation documentation: https://sharp.pixelplumbing.com/install');
|
|
68
84
|
}
|
|
85
|
+
throw new Error(help.join('\n'));
|
|
69
86
|
}
|
package/lib/utility.js
CHANGED
|
@@ -59,11 +59,17 @@ let versions = {
|
|
|
59
59
|
};
|
|
60
60
|
/* istanbul ignore next */
|
|
61
61
|
if (!libvipsVersion.isGlobal) {
|
|
62
|
-
|
|
63
|
-
versions = require(`@sharpen/sharp-${runtimePlatform}/versions`);
|
|
64
|
-
} catch (_) {
|
|
62
|
+
if (!libvipsVersion.isWasm) {
|
|
65
63
|
try {
|
|
66
|
-
versions = require(`@
|
|
64
|
+
versions = require(`@img/sharp-${runtimePlatform}/versions`);
|
|
65
|
+
} catch (_) {
|
|
66
|
+
try {
|
|
67
|
+
versions = require(`@img/sharp-libvips-${runtimePlatform}/versions`);
|
|
68
|
+
} catch (_) {}
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
try {
|
|
72
|
+
versions = require('@img/sharp-wasm32/versions');
|
|
67
73
|
} catch (_) {}
|
|
68
74
|
}
|
|
69
75
|
}
|