sharp 0.30.3 → 0.30.6
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/install/libvips.js +9 -1
- package/lib/composite.js +2 -2
- package/lib/constructor.js +2 -5
- package/lib/input.js +19 -11
- package/lib/libvips.js +11 -2
- package/lib/operation.js +11 -0
- package/lib/output.js +6 -1
- package/lib/platform.js +5 -3
- package/lib/sharp.js +1 -1
- package/lib/utility.js +17 -4
- package/package.json +9 -7
- package/src/common.cc +13 -13
- package/src/common.h +3 -3
- package/src/pipeline.cc +19 -11
package/install/libvips.js
CHANGED
|
@@ -35,6 +35,7 @@ const hasSharpPrebuild = [
|
|
|
35
35
|
];
|
|
36
36
|
|
|
37
37
|
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
|
|
38
|
+
const localLibvipsDir = process.env.npm_config_sharp_libvips_local_prebuilds || '';
|
|
38
39
|
const distHost = process.env.npm_config_sharp_libvips_binary_host || 'https://github.com/lovell/sharp-libvips/releases/download';
|
|
39
40
|
const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `${distHost}/v${minimumLibvipsVersionLabelled}/`;
|
|
40
41
|
const installationForced = !!(process.env.npm_config_sharp_install_force || process.env.SHARP_INSTALL_FORCE);
|
|
@@ -42,7 +43,9 @@ const installationForced = !!(process.env.npm_config_sharp_install_force || proc
|
|
|
42
43
|
const fail = function (err) {
|
|
43
44
|
libvips.log(err);
|
|
44
45
|
if (err.code === 'EACCES') {
|
|
45
|
-
libvips.log('Are you trying to install as a root or sudo user?
|
|
46
|
+
libvips.log('Are you trying to install as a root or sudo user?');
|
|
47
|
+
libvips.log('- For npm <= v6, try again with the "--unsafe-perm" flag');
|
|
48
|
+
libvips.log('- For npm >= v8, the user must own the directory "npm install" is run in');
|
|
46
49
|
}
|
|
47
50
|
libvips.log('Please see https://sharp.pixelplumbing.com/install for required dependencies');
|
|
48
51
|
process.exit(1);
|
|
@@ -156,6 +159,11 @@ try {
|
|
|
156
159
|
if (fs.existsSync(tarPathCache)) {
|
|
157
160
|
libvips.log(`Using cached ${tarPathCache}`);
|
|
158
161
|
extractTarball(tarPathCache, platformAndArch);
|
|
162
|
+
} else if (localLibvipsDir) {
|
|
163
|
+
// If localLibvipsDir is given try to use binaries from local directory
|
|
164
|
+
const tarPathLocal = path.join(path.resolve(localLibvipsDir), `v${minimumLibvipsVersionLabelled}`, tarFilename);
|
|
165
|
+
libvips.log(`Using local libvips from ${tarPathLocal}`);
|
|
166
|
+
extractTarball(tarPathLocal, platformAndArch);
|
|
159
167
|
} else {
|
|
160
168
|
const url = distBaseUrl + tarFilename;
|
|
161
169
|
libvips.log(`Downloading ${url}`);
|
package/lib/composite.js
CHANGED
|
@@ -50,7 +50,7 @@ const blend = {
|
|
|
50
50
|
* `hard-light`, `soft-light`, `difference`, `exclusion`.
|
|
51
51
|
*
|
|
52
52
|
* More information about blend modes can be found at
|
|
53
|
-
* https://libvips.
|
|
53
|
+
* https://www.libvips.org/API/current/libvips-conversion.html#VipsBlendMode
|
|
54
54
|
* and https://www.cairographics.org/operators/
|
|
55
55
|
*
|
|
56
56
|
* @since 0.22.0
|
|
@@ -105,7 +105,7 @@ const blend = {
|
|
|
105
105
|
* @param {Number} [images[].raw.height]
|
|
106
106
|
* @param {Number} [images[].raw.channels]
|
|
107
107
|
* @param {boolean} [images[].animated=false] - Set to `true` to read all frames/pages of an animated image.
|
|
108
|
-
* @param {
|
|
108
|
+
* @param {string} [images[].failOn='warning'] - @see {@link /api-constructor#parameters|constructor parameters}
|
|
109
109
|
* @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor#parameters|constructor parameters}
|
|
110
110
|
* @returns {Sharp}
|
|
111
111
|
* @throws {Error} Invalid parameters
|
package/lib/constructor.js
CHANGED
|
@@ -98,8 +98,7 @@ const debuglog = util.debuglog('sharp');
|
|
|
98
98
|
* a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
|
|
99
99
|
* JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
|
100
100
|
* @param {Object} [options] - if present, is an Object with optional attributes.
|
|
101
|
-
* @param {
|
|
102
|
-
* Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid.
|
|
101
|
+
* @param {string} [options.failOn='warning'] - level of sensitivity to invalid images, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels.
|
|
103
102
|
* @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
|
|
104
103
|
* (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
|
|
105
104
|
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
|
|
@@ -320,9 +319,7 @@ Object.setPrototypeOf(Sharp, stream.Duplex);
|
|
|
320
319
|
* // Using Promises to know when the pipeline is complete
|
|
321
320
|
* const fs = require("fs");
|
|
322
321
|
* const got = require("got");
|
|
323
|
-
* const sharpStream = sharp({
|
|
324
|
-
* failOnError: false
|
|
325
|
-
* });
|
|
322
|
+
* const sharpStream = sharp({ failOn: 'none' });
|
|
326
323
|
*
|
|
327
324
|
* const promises = [];
|
|
328
325
|
*
|
package/lib/input.js
CHANGED
|
@@ -9,9 +9,9 @@ const sharp = require('./sharp');
|
|
|
9
9
|
* @private
|
|
10
10
|
*/
|
|
11
11
|
function _inputOptionsFromObject (obj) {
|
|
12
|
-
const { raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd } = obj;
|
|
13
|
-
return [raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd].some(is.defined)
|
|
14
|
-
? { raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd }
|
|
12
|
+
const { raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd } = obj;
|
|
13
|
+
return [raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd].some(is.defined)
|
|
14
|
+
? { raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd }
|
|
15
15
|
: undefined;
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -21,7 +21,7 @@ function _inputOptionsFromObject (obj) {
|
|
|
21
21
|
*/
|
|
22
22
|
function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
23
23
|
const inputDescriptor = {
|
|
24
|
-
|
|
24
|
+
failOn: 'warning',
|
|
25
25
|
limitInputPixels: Math.pow(0x3FFF, 2),
|
|
26
26
|
unlimited: false,
|
|
27
27
|
sequentialRead: false
|
|
@@ -39,7 +39,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
39
39
|
if (input.length === 0) {
|
|
40
40
|
throw Error('Input Bit Array is empty');
|
|
41
41
|
}
|
|
42
|
-
inputDescriptor.buffer = Buffer.from(input.buffer);
|
|
42
|
+
inputDescriptor.buffer = Buffer.from(input.buffer, input.byteOffset, input.byteLength);
|
|
43
43
|
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
|
|
44
44
|
// Plain Object descriptor, e.g. create
|
|
45
45
|
inputOptions = input;
|
|
@@ -56,14 +56,22 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
56
56
|
}`);
|
|
57
57
|
}
|
|
58
58
|
if (is.object(inputOptions)) {
|
|
59
|
-
//
|
|
59
|
+
// Deprecated: failOnError
|
|
60
60
|
if (is.defined(inputOptions.failOnError)) {
|
|
61
61
|
if (is.bool(inputOptions.failOnError)) {
|
|
62
|
-
inputDescriptor.
|
|
62
|
+
inputDescriptor.failOn = inputOptions.failOnError ? 'warning' : 'none';
|
|
63
63
|
} else {
|
|
64
64
|
throw is.invalidParameterError('failOnError', 'boolean', inputOptions.failOnError);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
+
// failOn
|
|
68
|
+
if (is.defined(inputOptions.failOn)) {
|
|
69
|
+
if (is.string(inputOptions.failOn) && is.inArray(inputOptions.failOn, ['none', 'truncated', 'error', 'warning'])) {
|
|
70
|
+
inputDescriptor.failOn = inputOptions.failOn;
|
|
71
|
+
} else {
|
|
72
|
+
throw is.invalidParameterError('failOn', 'one of: none, truncated, error, warning', inputOptions.failOn);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
67
75
|
// Density
|
|
68
76
|
if (is.defined(inputOptions.density)) {
|
|
69
77
|
if (is.inRange(inputOptions.density, 1, 100000)) {
|
|
@@ -78,10 +86,10 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
78
86
|
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels
|
|
79
87
|
? Math.pow(0x3FFF, 2)
|
|
80
88
|
: 0;
|
|
81
|
-
} else if (is.integer(inputOptions.limitInputPixels) && inputOptions.limitInputPixels
|
|
89
|
+
} else if (is.integer(inputOptions.limitInputPixels) && is.inRange(inputOptions.limitInputPixels, 0, Number.MAX_SAFE_INTEGER)) {
|
|
82
90
|
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels;
|
|
83
91
|
} else {
|
|
84
|
-
throw is.invalidParameterError('limitInputPixels', 'integer
|
|
92
|
+
throw is.invalidParameterError('limitInputPixels', 'positive integer', inputOptions.limitInputPixels);
|
|
85
93
|
}
|
|
86
94
|
}
|
|
87
95
|
// unlimited
|
|
@@ -301,9 +309,9 @@ function _isStreamInput () {
|
|
|
301
309
|
* - `size`: Total size of image in bytes, for Stream and Buffer input only
|
|
302
310
|
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
|
|
303
311
|
* - `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)
|
|
304
|
-
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://libvips.
|
|
312
|
+
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/VipsImage.html#VipsInterpretation)
|
|
305
313
|
* - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
|
|
306
|
-
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://libvips.
|
|
314
|
+
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://www.libvips.org/API/current/VipsImage.html#VipsBandFormat)
|
|
307
315
|
* - `density`: Number of pixels per inch (DPI), if present
|
|
308
316
|
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
|
309
317
|
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
package/lib/libvips.js
CHANGED
|
@@ -65,7 +65,13 @@ const isRosetta = function () {
|
|
|
65
65
|
|
|
66
66
|
const globalLibvipsVersion = function () {
|
|
67
67
|
if (process.platform !== 'win32') {
|
|
68
|
-
const globalLibvipsVersion = spawnSync(
|
|
68
|
+
const globalLibvipsVersion = spawnSync('pkg-config --modversion vips-cpp', {
|
|
69
|
+
...spawnSyncOptions,
|
|
70
|
+
env: {
|
|
71
|
+
...env,
|
|
72
|
+
PKG_CONFIG_PATH: pkgConfigPath()
|
|
73
|
+
}
|
|
74
|
+
}).stdout;
|
|
69
75
|
/* istanbul ignore next */
|
|
70
76
|
return (globalLibvipsVersion || '').trim();
|
|
71
77
|
} else {
|
|
@@ -85,7 +91,10 @@ const removeVendoredLibvips = function () {
|
|
|
85
91
|
|
|
86
92
|
const pkgConfigPath = function () {
|
|
87
93
|
if (process.platform !== 'win32') {
|
|
88
|
-
const brewPkgConfigPath = spawnSync(
|
|
94
|
+
const brewPkgConfigPath = spawnSync(
|
|
95
|
+
'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
|
|
96
|
+
spawnSyncOptions
|
|
97
|
+
).stdout || '';
|
|
89
98
|
return [
|
|
90
99
|
brewPkgConfigPath.trim(),
|
|
91
100
|
env.PKG_CONFIG_PATH,
|
package/lib/operation.js
CHANGED
|
@@ -420,6 +420,17 @@ function gamma (gamma, gammaOut) {
|
|
|
420
420
|
|
|
421
421
|
/**
|
|
422
422
|
* Produce the "negative" of the image.
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* const output = await sharp(input)
|
|
426
|
+
* .negate()
|
|
427
|
+
* .toBuffer();
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* const output = await sharp(input)
|
|
431
|
+
* .negate({ alpha: false })
|
|
432
|
+
* .toBuffer();
|
|
433
|
+
*
|
|
423
434
|
* @param {Object} [options]
|
|
424
435
|
* @param {Boolean} [options.alpha=true] Whether or not to negate any alpha channel
|
|
425
436
|
* @returns {Sharp}
|
package/lib/output.js
CHANGED
|
@@ -82,6 +82,8 @@ function toFile (fileOut, callback) {
|
|
|
82
82
|
* Write output to a Buffer.
|
|
83
83
|
* JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
|
|
84
84
|
*
|
|
85
|
+
* Use {@link toFormat} or one of the format-specific functions such as {@link jpeg}, {@link png} etc. to set the output format.
|
|
86
|
+
*
|
|
85
87
|
* If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
|
|
86
88
|
*
|
|
87
89
|
* By default all metadata will be removed, which includes EXIF-based orientation.
|
|
@@ -108,6 +110,7 @@ function toFile (fileOut, callback) {
|
|
|
108
110
|
*
|
|
109
111
|
* @example
|
|
110
112
|
* sharp(input)
|
|
113
|
+
* .png()
|
|
111
114
|
* .toBuffer({ resolveWithObject: true })
|
|
112
115
|
* .then(({ data, info }) => { ... })
|
|
113
116
|
* .catch(err => { ... });
|
|
@@ -151,6 +154,8 @@ function toBuffer (options, callback) {
|
|
|
151
154
|
* The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
|
152
155
|
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
|
153
156
|
*
|
|
157
|
+
* EXIF metadata is unsupported for TIFF output.
|
|
158
|
+
*
|
|
154
159
|
* @example
|
|
155
160
|
* sharp('input.jpg')
|
|
156
161
|
* .withMetadata()
|
|
@@ -514,7 +519,7 @@ function webp (options) {
|
|
|
514
519
|
* // Convert PNG to GIF
|
|
515
520
|
* await sharp(pngBuffer)
|
|
516
521
|
* .gif()
|
|
517
|
-
* .toBuffer()
|
|
522
|
+
* .toBuffer();
|
|
518
523
|
*
|
|
519
524
|
* @example
|
|
520
525
|
* // Convert animated WebP to animated GIF
|
package/lib/platform.js
CHANGED
|
@@ -7,10 +7,12 @@ const env = process.env;
|
|
|
7
7
|
module.exports = function () {
|
|
8
8
|
const arch = env.npm_config_arch || process.arch;
|
|
9
9
|
const platform = env.npm_config_platform || process.platform;
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const libc = process.env.npm_config_libc ||
|
|
11
|
+
/* istanbul ignore next */
|
|
12
|
+
(detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '');
|
|
13
|
+
const libcId = platform !== 'linux' || libc === detectLibc.GLIBC ? '' : libc;
|
|
12
14
|
|
|
13
|
-
const platformId = [`${platform}${
|
|
15
|
+
const platformId = [`${platform}${libcId}`];
|
|
14
16
|
|
|
15
17
|
if (arch === 'arm') {
|
|
16
18
|
const fallback = process.versions.electron ? '7' : '6';
|
package/lib/sharp.js
CHANGED
|
@@ -13,7 +13,7 @@ try {
|
|
|
13
13
|
} else {
|
|
14
14
|
const [platform, arch] = platformAndArch.split('-');
|
|
15
15
|
help.push(
|
|
16
|
-
'- Install with
|
|
16
|
+
'- Install with verbose logging and look for errors: "npm install --ignore-scripts=false --foreground-scripts --verbose sharp"',
|
|
17
17
|
`- Install for the current ${platformAndArch} runtime: "npm install --platform=${platform} --arch=${arch} sharp"`
|
|
18
18
|
);
|
|
19
19
|
}
|
package/lib/utility.js
CHANGED
|
@@ -103,7 +103,11 @@ cache(true);
|
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
105
|
* Gets or, when a concurrency is provided, sets
|
|
106
|
-
* the number of threads
|
|
106
|
+
* the maximum number of threads _libvips_ should use to process _each image_.
|
|
107
|
+
* These are from a thread pool managed by glib,
|
|
108
|
+
* which helps avoid the overhead of creating new threads.
|
|
109
|
+
*
|
|
110
|
+
* This method always returns the current concurrency.
|
|
107
111
|
*
|
|
108
112
|
* The default value is the number of CPU cores,
|
|
109
113
|
* except when using glibc-based Linux without jemalloc,
|
|
@@ -111,10 +115,19 @@ cache(true);
|
|
|
111
115
|
*
|
|
112
116
|
* A value of `0` will reset this to the number of CPU cores.
|
|
113
117
|
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
118
|
+
* Some image format libraries spawn additional threads,
|
|
119
|
+
* e.g. libaom manages its own 4 threads when encoding AVIF images,
|
|
120
|
+
* and these are independent of the value set here.
|
|
116
121
|
*
|
|
117
|
-
*
|
|
122
|
+
* The maximum number of images that sharp can process in parallel
|
|
123
|
+
* is controlled by libuv's `UV_THREADPOOL_SIZE` environment variable,
|
|
124
|
+
* which defaults to 4.
|
|
125
|
+
*
|
|
126
|
+
* https://nodejs.org/api/cli.html#uv_threadpool_sizesize
|
|
127
|
+
*
|
|
128
|
+
* For example, by default, a machine with 8 CPU cores will process
|
|
129
|
+
* 4 images in parallel and use up to 8 threads per image,
|
|
130
|
+
* so there will be up to 32 concurrent threads.
|
|
118
131
|
*
|
|
119
132
|
* @example
|
|
120
133
|
* const threads = sharp.concurrency(); // 4
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sharp",
|
|
3
3
|
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
|
|
4
|
-
"version": "0.30.
|
|
4
|
+
"version": "0.30.6",
|
|
5
5
|
"author": "Lovell Fuller <npm@lovell.info>",
|
|
6
6
|
"homepage": "https://github.com/lovell/sharp",
|
|
7
7
|
"contributors": [
|
|
@@ -81,7 +81,9 @@
|
|
|
81
81
|
"Taneli Vatanen <taneli.vatanen@gmail.com>",
|
|
82
82
|
"Joris Dugué <zaruike10@gmail.com>",
|
|
83
83
|
"Chris Banks <christopher.bradley.banks@gmail.com>",
|
|
84
|
-
"Ompal Singh <ompal.hitm09@gmail.com>"
|
|
84
|
+
"Ompal Singh <ompal.hitm09@gmail.com>",
|
|
85
|
+
"Brodan <christopher.hranj@gmail.com",
|
|
86
|
+
"Ankur Parihar <ankur.github@gmail.com>"
|
|
85
87
|
],
|
|
86
88
|
"scripts": {
|
|
87
89
|
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)",
|
|
@@ -126,11 +128,11 @@
|
|
|
126
128
|
"vips"
|
|
127
129
|
],
|
|
128
130
|
"dependencies": {
|
|
129
|
-
"color": "^4.2.
|
|
131
|
+
"color": "^4.2.3",
|
|
130
132
|
"detect-libc": "^2.0.1",
|
|
131
|
-
"node-addon-api": "^
|
|
132
|
-
"prebuild-install": "^7.0
|
|
133
|
-
"semver": "^7.3.
|
|
133
|
+
"node-addon-api": "^5.0.0",
|
|
134
|
+
"prebuild-install": "^7.1.0",
|
|
135
|
+
"semver": "^7.3.7",
|
|
134
136
|
"simple-get": "^4.0.1",
|
|
135
137
|
"tar-fs": "^2.1.1",
|
|
136
138
|
"tunnel-agent": "^0.6.0"
|
|
@@ -143,7 +145,7 @@
|
|
|
143
145
|
"exif-reader": "^1.0.3",
|
|
144
146
|
"icc": "^2.0.0",
|
|
145
147
|
"license-checker": "^25.0.1",
|
|
146
|
-
"mocha": "^
|
|
148
|
+
"mocha": "^10.0.0",
|
|
147
149
|
"mock-fs": "^5.1.2",
|
|
148
150
|
"nyc": "^15.1.0",
|
|
149
151
|
"prebuild": "^11.0.3",
|
package/src/common.cc
CHANGED
|
@@ -48,6 +48,9 @@ namespace sharp {
|
|
|
48
48
|
int32_t AttrAsInt32(Napi::Object obj, unsigned int const attr) {
|
|
49
49
|
return obj.Get(attr).As<Napi::Number>().Int32Value();
|
|
50
50
|
}
|
|
51
|
+
int64_t AttrAsInt64(Napi::Object obj, std::string attr) {
|
|
52
|
+
return obj.Get(attr).As<Napi::Number>().Int64Value();
|
|
53
|
+
}
|
|
51
54
|
double AttrAsDouble(Napi::Object obj, std::string attr) {
|
|
52
55
|
return obj.Get(attr).As<Napi::Number>().DoubleValue();
|
|
53
56
|
}
|
|
@@ -85,7 +88,9 @@ namespace sharp {
|
|
|
85
88
|
descriptor->buffer = buffer.Data();
|
|
86
89
|
descriptor->isBuffer = TRUE;
|
|
87
90
|
}
|
|
88
|
-
descriptor->
|
|
91
|
+
descriptor->failOn = static_cast<VipsFailOn>(
|
|
92
|
+
vips_enum_from_nick(nullptr, VIPS_TYPE_FAIL_ON,
|
|
93
|
+
AttrAsStr(input, "failOn").data()));
|
|
89
94
|
// Density for vector-based input
|
|
90
95
|
if (HasAttr(input, "density")) {
|
|
91
96
|
descriptor->density = AttrAsDouble(input, "density");
|
|
@@ -129,7 +134,7 @@ namespace sharp {
|
|
|
129
134
|
}
|
|
130
135
|
}
|
|
131
136
|
// Limit input images to a given number of pixels, where pixels = width * height
|
|
132
|
-
descriptor->limitInputPixels =
|
|
137
|
+
descriptor->limitInputPixels = static_cast<uint64_t>(AttrAsInt64(input, "limitInputPixels"));
|
|
133
138
|
// Allow switch from random to sequential access
|
|
134
139
|
descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
|
|
135
140
|
// Remove safety features and allow unlimited SVG/PNG input
|
|
@@ -329,7 +334,7 @@ namespace sharp {
|
|
|
329
334
|
try {
|
|
330
335
|
vips::VOption *option = VImage::option()
|
|
331
336
|
->set("access", descriptor->access)
|
|
332
|
-
->set("
|
|
337
|
+
->set("fail_on", descriptor->failOn);
|
|
333
338
|
if (descriptor->unlimited && (imageType == ImageType::SVG || imageType == ImageType::PNG)) {
|
|
334
339
|
option->set("unlimited", TRUE);
|
|
335
340
|
}
|
|
@@ -375,12 +380,6 @@ namespace sharp {
|
|
|
375
380
|
VImage::option()->set("mean", descriptor->createNoiseMean)->set("sigma", descriptor->createNoiseSigma)));
|
|
376
381
|
}
|
|
377
382
|
image = image.bandjoin(bands);
|
|
378
|
-
image = image.cast(VipsBandFormat::VIPS_FORMAT_UCHAR);
|
|
379
|
-
if (channels < 3) {
|
|
380
|
-
image = image.colourspace(VIPS_INTERPRETATION_B_W);
|
|
381
|
-
} else {
|
|
382
|
-
image = image.colourspace(VIPS_INTERPRETATION_sRGB);
|
|
383
|
-
}
|
|
384
383
|
} else {
|
|
385
384
|
std::vector<double> background = {
|
|
386
385
|
descriptor->createBackground[0],
|
|
@@ -392,7 +391,8 @@ namespace sharp {
|
|
|
392
391
|
}
|
|
393
392
|
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
|
|
394
393
|
}
|
|
395
|
-
image.get_image()->Type =
|
|
394
|
+
image.get_image()->Type = image.guess_interpretation();
|
|
395
|
+
image = image.cast(VIPS_FORMAT_UCHAR);
|
|
396
396
|
imageType = ImageType::RAW;
|
|
397
397
|
} else {
|
|
398
398
|
// From filesystem
|
|
@@ -402,13 +402,13 @@ namespace sharp {
|
|
|
402
402
|
throw vips::VError("Input file is missing, did you mean "
|
|
403
403
|
"sharp(Buffer.from('" + descriptor->file.substr(0, 8) + "...')?");
|
|
404
404
|
}
|
|
405
|
-
throw vips::VError("Input file is missing");
|
|
405
|
+
throw vips::VError("Input file is missing: " + descriptor->file);
|
|
406
406
|
}
|
|
407
407
|
if (imageType != ImageType::UNKNOWN) {
|
|
408
408
|
try {
|
|
409
409
|
vips::VOption *option = VImage::option()
|
|
410
410
|
->set("access", descriptor->access)
|
|
411
|
-
->set("
|
|
411
|
+
->set("fail_on", descriptor->failOn);
|
|
412
412
|
if (descriptor->unlimited && (imageType == ImageType::SVG || imageType == ImageType::PNG)) {
|
|
413
413
|
option->set("unlimited", TRUE);
|
|
414
414
|
}
|
|
@@ -442,7 +442,7 @@ namespace sharp {
|
|
|
442
442
|
}
|
|
443
443
|
// Limit input images to a given number of pixels, where pixels = width * height
|
|
444
444
|
if (descriptor->limitInputPixels > 0 &&
|
|
445
|
-
static_cast<uint64_t>(image.width() * image.height()) >
|
|
445
|
+
static_cast<uint64_t>(image.width() * image.height()) > descriptor->limitInputPixels) {
|
|
446
446
|
throw vips::VError("Input image exceeds pixel limit");
|
|
447
447
|
}
|
|
448
448
|
return std::make_tuple(image, imageType);
|
package/src/common.h
CHANGED
|
@@ -48,8 +48,8 @@ namespace sharp {
|
|
|
48
48
|
std::string name;
|
|
49
49
|
std::string file;
|
|
50
50
|
char *buffer;
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
VipsFailOn failOn;
|
|
52
|
+
uint64_t limitInputPixels;
|
|
53
53
|
bool unlimited;
|
|
54
54
|
VipsAccess access;
|
|
55
55
|
size_t bufferLength;
|
|
@@ -74,7 +74,7 @@ namespace sharp {
|
|
|
74
74
|
|
|
75
75
|
InputDescriptor():
|
|
76
76
|
buffer(nullptr),
|
|
77
|
-
|
|
77
|
+
failOn(VIPS_FAIL_ON_WARNING),
|
|
78
78
|
limitInputPixels(0x3FFF * 0x3FFF),
|
|
79
79
|
unlimited(FALSE),
|
|
80
80
|
access(VIPS_ACCESS_RANDOM),
|
package/src/pipeline.cc
CHANGED
|
@@ -95,16 +95,18 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
95
95
|
if (baton->rotateBeforePreExtract) {
|
|
96
96
|
if (rotation != VIPS_ANGLE_D0) {
|
|
97
97
|
image = image.rot(rotation);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
98
|
+
}
|
|
99
|
+
if (flip) {
|
|
100
|
+
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
|
101
|
+
}
|
|
102
|
+
if (flop) {
|
|
103
|
+
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
104
|
+
}
|
|
105
|
+
if (rotation != VIPS_ANGLE_D0 || flip || flop) {
|
|
106
106
|
image = sharp::RemoveExifOrientation(image);
|
|
107
107
|
}
|
|
108
|
+
flop = FALSE;
|
|
109
|
+
flip = FALSE;
|
|
108
110
|
if (baton->rotationAngle != 0.0) {
|
|
109
111
|
MultiPageUnsupported(nPages, "Rotate");
|
|
110
112
|
std::vector<double> background;
|
|
@@ -200,7 +202,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
200
202
|
vips::VOption *option = VImage::option()
|
|
201
203
|
->set("access", baton->input->access)
|
|
202
204
|
->set("shrink", jpegShrinkOnLoad)
|
|
203
|
-
->set("
|
|
205
|
+
->set("fail_on", baton->input->failOn);
|
|
204
206
|
if (baton->input->buffer != nullptr) {
|
|
205
207
|
// Reload JPEG buffer
|
|
206
208
|
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
|
@@ -214,7 +216,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
214
216
|
vips::VOption *option = VImage::option()
|
|
215
217
|
->set("access", baton->input->access)
|
|
216
218
|
->set("scale", scale)
|
|
217
|
-
->set("
|
|
219
|
+
->set("fail_on", baton->input->failOn);
|
|
218
220
|
if (inputImageType == sharp::ImageType::WEBP) {
|
|
219
221
|
option->set("n", baton->input->pages);
|
|
220
222
|
option->set("page", baton->input->page);
|
|
@@ -241,8 +243,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
241
243
|
// Reload SVG file
|
|
242
244
|
image = VImage::svgload(const_cast<char*>(baton->input->file.data()), option);
|
|
243
245
|
}
|
|
244
|
-
|
|
245
246
|
sharp::SetDensity(image, baton->input->density);
|
|
247
|
+
if (image.width() > 32767 || image.height() > 32767) {
|
|
248
|
+
throw vips::VError("Input SVG image will exceed 32767x32767 pixel limit when scaled");
|
|
249
|
+
}
|
|
246
250
|
} else if (inputImageType == sharp::ImageType::PDF) {
|
|
247
251
|
option->set("n", baton->input->pages);
|
|
248
252
|
option->set("page", baton->input->page);
|
|
@@ -260,6 +264,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
260
264
|
|
|
261
265
|
sharp::SetDensity(image, baton->input->density);
|
|
262
266
|
}
|
|
267
|
+
} else {
|
|
268
|
+
if (inputImageType == sharp::ImageType::SVG && (image.width() > 32767 || image.height() > 32767)) {
|
|
269
|
+
throw vips::VError("Input SVG image exceeds 32767x32767 pixel limit");
|
|
270
|
+
}
|
|
263
271
|
}
|
|
264
272
|
|
|
265
273
|
// Any pre-shrinking may already have been done
|