sharp 0.27.2 → 0.28.3

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 CHANGED
@@ -19,6 +19,14 @@ rotation, extraction, compositing and gamma correction are available.
19
19
  Most modern macOS, Windows and Linux systems running Node.js v10+
20
20
  do not require any additional install or runtime dependencies.
21
21
 
22
+ ## Documentation
23
+
24
+ Visit [sharp.pixelplumbing.com](https://sharp.pixelplumbing.com/) for complete
25
+ [installation instructions](https://sharp.pixelplumbing.com/install),
26
+ [API documentation](https://sharp.pixelplumbing.com/api-constructor),
27
+ [benchmark tests](https://sharp.pixelplumbing.com/performance) and
28
+ [changelog](https://sharp.pixelplumbing.com/changelog).
29
+
22
30
  ## Examples
23
31
 
24
32
  ```sh
@@ -43,6 +51,7 @@ sharp(inputBuffer)
43
51
  sharp('input.jpg')
44
52
  .rotate()
45
53
  .resize(200)
54
+ .jpeg({ mozjpeg: true })
46
55
  .toBuffer()
47
56
  .then( data => { ... })
48
57
  .catch( err => { ... });
@@ -84,23 +93,15 @@ readableStream
84
93
  .pipe(writableStream);
85
94
  ```
86
95
 
87
- [![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.svg?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
88
- [![N-API v3](https://img.shields.io/badge/N--API-v3-green.svg)](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api_version_matrix)
89
-
90
- ### Documentation
91
-
92
- Visit [sharp.pixelplumbing.com](https://sharp.pixelplumbing.com/) for complete
93
- [installation instructions](https://sharp.pixelplumbing.com/install),
94
- [API documentation](https://sharp.pixelplumbing.com/api-constructor),
95
- [benchmark tests](https://sharp.pixelplumbing.com/performance) and
96
- [changelog](https://sharp.pixelplumbing.com/changelog).
97
-
98
- ### Contributing
96
+ ## Contributing
99
97
 
100
98
  A [guide for contributors](https://github.com/lovell/sharp/blob/master/.github/CONTRIBUTING.md)
101
99
  covers reporting bugs, requesting features and submitting code changes.
102
100
 
103
- ### Licensing
101
+ [![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.svg?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
102
+ [![N-API v3](https://img.shields.io/badge/N--API-v3-green.svg)](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api_version_matrix)
103
+
104
+ ## Licensing
104
105
 
105
106
  Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Lovell Fuller and contributors.
106
107
 
package/binding.gyp CHANGED
@@ -140,8 +140,7 @@
140
140
  'link_settings': {
141
141
  'library_dirs': ['<(sharp_vendor_dir)/lib'],
142
142
  'libraries': [
143
- 'libvips-cpp.42.dylib',
144
- 'libvips.42.dylib'
143
+ 'libvips-cpp.42.dylib'
145
144
  ]
146
145
  },
147
146
  'xcode_settings': {
@@ -153,13 +152,12 @@
153
152
  }],
154
153
  ['OS == "linux"', {
155
154
  'defines': [
156
- '_GLIBCXX_USE_CXX11_ABI=0'
155
+ '_GLIBCXX_USE_CXX11_ABI=1'
157
156
  ],
158
157
  'link_settings': {
159
158
  'library_dirs': ['<(sharp_vendor_dir)/lib'],
160
159
  'libraries': [
161
- '-l:libvips-cpp.so.42',
162
- '-l:libvips.so.42'
160
+ '-l:libvips-cpp.so.42'
163
161
  ],
164
162
  'ldflags': [
165
163
  # Ensure runtime linking is relative to sharp.node
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ const libvips = require('../lib/libvips');
4
+
5
+ try {
6
+ if (!(libvips.useGlobalLibvips() || libvips.hasVendoredLibvips())) {
7
+ process.exitCode = 1;
8
+ }
9
+ } catch (err) {
10
+ process.exitCode = 1;
11
+ }
@@ -4,7 +4,6 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
6
  const libvips = require('../lib/libvips');
7
- const npmLog = require('npmlog');
8
7
 
9
8
  const minimumLibvipsVersion = libvips.minimumLibvipsVersion;
10
9
 
@@ -12,13 +11,13 @@ const platform = process.env.npm_config_platform || process.platform;
12
11
  if (platform === 'win32') {
13
12
  const buildDir = path.join(__dirname, '..', 'build');
14
13
  const buildReleaseDir = path.join(buildDir, 'Release');
15
- npmLog.info('sharp', `Creating ${buildReleaseDir}`);
14
+ libvips.log(`Creating ${buildReleaseDir}`);
16
15
  try {
17
16
  libvips.mkdirSync(buildDir);
18
17
  libvips.mkdirSync(buildReleaseDir);
19
18
  } catch (err) {}
20
19
  const vendorLibDir = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, 'lib');
21
- npmLog.info('sharp', `Copying DLLs from ${vendorLibDir} to ${buildReleaseDir}`);
20
+ libvips.log(`Copying DLLs from ${vendorLibDir} to ${buildReleaseDir}`);
22
21
  try {
23
22
  fs
24
23
  .readdirSync(vendorLibDir)
@@ -32,6 +31,7 @@ if (platform === 'win32') {
32
31
  );
33
32
  });
34
33
  } catch (err) {
35
- npmLog.error('sharp', err.message);
34
+ libvips.log(err);
35
+ process.exit(1);
36
36
  }
37
37
  }
@@ -7,8 +7,8 @@ const stream = require('stream');
7
7
  const zlib = require('zlib');
8
8
 
9
9
  const detectLibc = require('detect-libc');
10
- const npmLog = require('npmlog');
11
- const semver = require('semver');
10
+ const semverLessThan = require('semver/functions/lt');
11
+ const semverSatisfies = require('semver/functions/satisfies');
12
12
  const simpleGet = require('simple-get');
13
13
  const tarFs = require('tar-fs');
14
14
 
@@ -22,34 +22,58 @@ const minimumGlibcVersionByArch = {
22
22
  x64: '2.17'
23
23
  };
24
24
 
25
+ const hasSharpPrebuild = [
26
+ 'darwin-x64',
27
+ 'linux-arm64',
28
+ 'linux-x64',
29
+ 'linuxmusl-x64',
30
+ 'linuxmusl-arm64',
31
+ 'win32-ia32',
32
+ 'win32-x64'
33
+ ];
34
+
25
35
  const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
26
36
  const distHost = process.env.npm_config_sharp_libvips_binary_host || 'https://github.com/lovell/sharp-libvips/releases/download';
27
37
  const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `${distHost}/v${minimumLibvipsVersionLabelled}/`;
28
38
  const supportsBrotli = ('BrotliDecompress' in zlib);
39
+ const installationForced = !!(process.env.npm_config_sharp_install_force || process.env.SHARP_INSTALL_FORCE);
29
40
 
30
41
  const fail = function (err) {
31
- npmLog.error('sharp', err.message);
42
+ libvips.log(err);
32
43
  if (err.code === 'EACCES') {
33
- npmLog.info('sharp', 'Are you trying to install as a root or sudo user? Try again with the --unsafe-perm flag');
44
+ libvips.log('Are you trying to install as a root or sudo user? Try again with the --unsafe-perm flag');
34
45
  }
35
- npmLog.info('sharp', 'Attempting to build from source via node-gyp but this may fail due to the above error');
36
- npmLog.info('sharp', 'Please see https://sharp.pixelplumbing.com/install for required dependencies');
46
+ libvips.log('Please see https://sharp.pixelplumbing.com/install for required dependencies');
37
47
  process.exit(1);
38
48
  };
39
49
 
40
- const extractTarball = function (tarPath) {
50
+ const handleError = function (err) {
51
+ if (installationForced) {
52
+ libvips.log(`Installation warning: ${err.message}`);
53
+ } else {
54
+ throw err;
55
+ }
56
+ };
57
+
58
+ const extractTarball = function (tarPath, platformAndArch) {
41
59
  const vendorPath = path.join(__dirname, '..', 'vendor');
42
60
  libvips.mkdirSync(vendorPath);
43
61
  const versionedVendorPath = path.join(vendorPath, minimumLibvipsVersion);
44
62
  libvips.mkdirSync(versionedVendorPath);
63
+
64
+ const ignoreVendorInclude = hasSharpPrebuild.includes(platformAndArch) && !process.env.npm_config_build_from_source;
65
+ const ignore = function (name) {
66
+ return ignoreVendorInclude && name.includes('include/');
67
+ };
68
+
45
69
  stream.pipeline(
46
70
  fs.createReadStream(tarPath),
47
71
  supportsBrotli ? new zlib.BrotliDecompress() : new zlib.Gunzip(),
48
- tarFs.extract(versionedVendorPath),
72
+ tarFs.extract(versionedVendorPath, { ignore }),
49
73
  function (err) {
50
74
  if (err) {
51
75
  if (/unexpected end of file/.test(err.message)) {
52
- npmLog.error('sharp', `Please delete ${tarPath} as it is not a valid tarball`);
76
+ fail(new Error(`Please delete ${tarPath} as it is not a valid tarball`));
53
77
  }
54
78
  fail(err);
55
79
  }
@@ -62,11 +86,11 @@ try {
62
86
 
63
87
  if (useGlobalLibvips) {
64
88
  const globalLibvipsVersion = libvips.globalLibvipsVersion();
65
- npmLog.info('sharp', `Detected globally-installed libvips v${globalLibvipsVersion}`);
66
- npmLog.info('sharp', 'Building from source via node-gyp');
89
+ libvips.log(`Detected globally-installed libvips v${globalLibvipsVersion}`);
90
+ libvips.log('Building from source via node-gyp');
67
91
  process.exit(1);
68
92
  } else if (libvips.hasVendoredLibvips()) {
69
- npmLog.info('sharp', `Using existing vendored libvips v${minimumLibvipsVersion}`);
93
+ libvips.log(`Using existing vendored libvips v${minimumLibvipsVersion}`);
70
94
  } else {
71
95
  // Is this arch/platform supported?
72
96
  const arch = process.env.npm_config_arch || process.arch;
@@ -80,20 +104,21 @@ try {
80
104
  if (platformAndArch === 'freebsd-x64' || platformAndArch === 'openbsd-x64' || platformAndArch === 'sunos-x64') {
81
105
  throw new Error(`BSD/SunOS systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
82
106
  }
83
- if (detectLibc.family === detectLibc.GLIBC && detectLibc.version) {
84
- if (semver.lt(`${detectLibc.version}.0`, `${minimumGlibcVersionByArch[arch]}.0`)) {
85
- throw new Error(`Use with glibc ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
107
+ // Linux libc version check
108
+ if (detectLibc.family === detectLibc.GLIBC && detectLibc.version && minimumGlibcVersionByArch[arch]) {
109
+ if (semverLessThan(`${detectLibc.version}.0`, `${minimumGlibcVersionByArch[arch]}.0`)) {
110
+ handleError(new Error(`Use with glibc ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
86
111
  }
87
112
  }
88
113
  if (detectLibc.family === detectLibc.MUSL && detectLibc.version) {
89
- if (!semver.satisfies(detectLibc.version, '>=1.1.24 <1.2.0')) {
90
- throw new Error(`Use with musl ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
114
+ if (semverLessThan(detectLibc.version, '1.1.24')) {
115
+ handleError(new Error(`Use with musl ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
91
116
  }
92
117
  }
93
-
118
+ // Node.js minimum version check
94
119
  const supportedNodeVersion = process.env.npm_package_engines_node || require('../package.json').engines.node;
95
- if (!semver.satisfies(process.versions.node, supportedNodeVersion)) {
96
- throw new Error(`Expected Node.js version ${supportedNodeVersion} but found ${process.versions.node}`);
120
+ if (!semverSatisfies(process.versions.node, supportedNodeVersion)) {
121
+ handleError(new Error(`Expected Node.js version ${supportedNodeVersion} but found ${process.versions.node}`));
97
122
  }
98
123
 
99
124
  const extension = supportsBrotli ? 'br' : 'gz';
@@ -102,13 +127,11 @@ try {
102
127
  const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.' + extension;
103
128
  const tarPathCache = path.join(libvips.cachePath(), tarFilename);
104
129
  if (fs.existsSync(tarPathCache)) {
105
- npmLog.info('sharp', `Using cached ${tarPathCache}`);
106
- extractTarball(tarPathCache);
130
+ libvips.log(`Using cached ${tarPathCache}`);
131
+ extractTarball(tarPathCache, platformAndArch);
107
132
  } else {
108
- const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`);
109
- const tmpFile = fs.createWriteStream(tarPathTemp);
110
133
  const url = distBaseUrl + tarFilename;
111
- npmLog.info('sharp', `Downloading ${url}`);
134
+ libvips.log(`Downloading ${url}`);
112
135
  simpleGet({ url: url, agent: agent() }, function (err, response) {
113
136
  if (err) {
114
137
  fail(err);
@@ -117,24 +140,39 @@ try {
117
140
  } else if (response.statusCode !== 200) {
118
141
  fail(new Error(`Status ${response.statusCode} ${response.statusMessage}`));
119
142
  } else {
143
+ const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`);
144
+ const tmpFileStream = fs.createWriteStream(tarPathTemp);
120
145
  response
121
- .on('error', fail)
122
- .pipe(tmpFile);
146
+ .on('error', function (err) {
147
+ tmpFileStream.destroy(err);
148
+ })
149
+ .on('close', function () {
150
+ if (!response.complete) {
151
+ tmpFileStream.destroy(new Error('Download incomplete (connection was terminated)'));
152
+ }
153
+ })
154
+ .pipe(tmpFileStream);
155
+ tmpFileStream
156
+ .on('error', function (err) {
157
+ // Clean up temporary file
158
+ try {
159
+ fs.unlinkSync(tarPathTemp);
160
+ } catch (e) {}
161
+ fail(err);
162
+ })
163
+ .on('close', function () {
164
+ try {
165
+ // Attempt to rename
166
+ fs.renameSync(tarPathTemp, tarPathCache);
167
+ } catch (err) {
168
+ // Fall back to copy and unlink
169
+ fs.copyFileSync(tarPathTemp, tarPathCache);
170
+ fs.unlinkSync(tarPathTemp);
171
+ }
172
+ extractTarball(tarPathCache, platformAndArch);
173
+ });
123
174
  }
124
175
  });
125
- tmpFile
126
- .on('error', fail)
127
- .on('close', function () {
128
- try {
129
- // Attempt to rename
130
- fs.renameSync(tarPathTemp, tarPathCache);
131
- } catch (err) {
132
- // Fall back to copy and unlink
133
- fs.copyFileSync(tarPathTemp, tarPathCache);
134
- fs.unlinkSync(tarPathTemp);
135
- }
136
- extractTarball(tarPathCache);
137
- });
138
176
  }
139
177
  }
140
178
  } catch (err) {
package/lib/agent.js CHANGED
@@ -25,7 +25,7 @@ module.exports = function () {
25
25
  ? tunnelAgent.httpsOverHttps
26
26
  : tunnelAgent.httpsOverHttp;
27
27
  const proxyAuth = proxy.username && proxy.password
28
- ? `${proxy.username}:${proxy.password}`
28
+ ? `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`
29
29
  : null;
30
30
  return tunnel({
31
31
  proxy: {
package/lib/channel.js CHANGED
@@ -15,6 +15,8 @@ const bool = {
15
15
  /**
16
16
  * Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
17
17
  *
18
+ * See also {@link /api-operation#flatten|flatten}.
19
+ *
18
20
  * @example
19
21
  * sharp('rgba.png')
20
22
  * .removeAlpha()
@@ -30,21 +32,39 @@ function removeAlpha () {
30
32
  }
31
33
 
32
34
  /**
33
- * Ensure alpha channel, if missing. The added alpha channel will be fully opaque. This is a no-op if the image already has an alpha channel.
35
+ * Ensure the output image has an alpha transparency channel.
36
+ * If missing, the added alpha channel will have the specified
37
+ * transparency level, defaulting to fully-opaque (1).
38
+ * This is a no-op if the image already has an alpha channel.
34
39
  *
35
40
  * @since 0.21.2
36
41
  *
37
42
  * @example
38
- * sharp('rgb.jpg')
43
+ * // rgba.png will be a 4 channel image with a fully-opaque alpha channel
44
+ * await sharp('rgb.jpg')
39
45
  * .ensureAlpha()
40
- * .toFile('rgba.png', function(err, info) {
41
- * // rgba.png is a 4 channel image with a fully opaque alpha channel
42
- * });
46
+ * .toFile('rgba.png')
47
+ *
48
+ * @example
49
+ * // rgba is a 4 channel image with a fully-transparent alpha channel
50
+ * const rgba = await sharp(rgb)
51
+ * .ensureAlpha(0)
52
+ * .toBuffer();
43
53
  *
54
+ * @param {number} [alpha=1] - alpha transparency level (0=fully-transparent, 1=fully-opaque)
44
55
  * @returns {Sharp}
56
+ * @throws {Error} Invalid alpha transparency level
45
57
  */
46
- function ensureAlpha () {
47
- this.options.ensureAlpha = true;
58
+ function ensureAlpha (alpha) {
59
+ if (is.defined(alpha)) {
60
+ if (is.number(alpha) && is.inRange(alpha, 0, 1)) {
61
+ this.options.ensureAlpha = alpha;
62
+ } else {
63
+ throw is.invalidParameterError('alpha', 'number between 0 and 1', alpha);
64
+ }
65
+ } else {
66
+ this.options.ensureAlpha = 1;
67
+ }
48
68
  return this;
49
69
  }
50
70
 
package/lib/composite.js CHANGED
@@ -89,6 +89,8 @@ const blend = {
89
89
  * @param {Number} [images[].raw.width]
90
90
  * @param {Number} [images[].raw.height]
91
91
  * @param {Number} [images[].raw.channels]
92
+ * @param {boolean} [images[].failOnError=true] - @see {@link /api-constructor#parameters|constructor parameters}
93
+ * @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor#parameters|constructor parameters}
92
94
  * @returns {Sharp}
93
95
  * @throws {Error} Invalid parameters
94
96
  */
@@ -18,12 +18,10 @@ try {
18
18
  help.push(`- Ensure "${process.platform}" is used at install time as well as runtime`);
19
19
  } else if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) {
20
20
  help.push('- Run "brew update && brew upgrade vips"');
21
- } else if (/Cannot find module/.test(err.message)) {
22
- help.push('- Run "npm rebuild --verbose sharp" and look for errors');
23
21
  } else {
24
22
  help.push(
25
23
  '- Remove the "node_modules/sharp" directory then run',
26
- ' "npm install --ignore-scripts=false --verbose" and look for errors'
24
+ ' "npm install --ignore-scripts=false --verbose sharp" and look for errors'
27
25
  );
28
26
  }
29
27
  help.push(
@@ -134,12 +132,15 @@ const debuglog = util.debuglog('sharp');
134
132
  * @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
135
133
  * @param {number} [options.pages=1] - number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages.
136
134
  * @param {number} [options.page=0] - page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based.
135
+ * @param {number} [options.subifd=-1] - subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image.
137
136
  * @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
138
137
  * @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`).
139
138
  * @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
140
139
  * @param {number} [options.raw.width] - integral number of pixels wide.
141
140
  * @param {number} [options.raw.height] - integral number of pixels high.
142
141
  * @param {number} [options.raw.channels] - integral number of channels, between 1 and 4.
142
+ * @param {boolean} [options.raw.premultiplied] - specifies that the raw input has already been premultiplied, set to `true`
143
+ * to avoid sharp premultiplying the image. (optional, default `false`)
143
144
  * @param {Object} [options.create] - describes a new image to be created.
144
145
  * @param {number} [options.create.width] - integral number of pixels wide.
145
146
  * @param {number} [options.create.height] - integral number of pixels high.
@@ -215,6 +216,9 @@ const Sharp = function (input, options) {
215
216
  gammaOut: 0,
216
217
  greyscale: false,
217
218
  normalise: false,
219
+ claheWidth: 0,
220
+ claheHeight: 0,
221
+ claheMaxSlope: 3,
218
222
  brightness: 1,
219
223
  saturation: 1,
220
224
  hue: 0,
@@ -223,7 +227,7 @@ const Sharp = function (input, options) {
223
227
  joinChannelIn: [],
224
228
  extractChannel: -1,
225
229
  removeAlpha: false,
226
- ensureAlpha: false,
230
+ ensureAlpha: -1,
227
231
  colourspace: 'srgb',
228
232
  composite: [],
229
233
  // output
@@ -232,7 +236,9 @@ const Sharp = function (input, options) {
232
236
  streamOut: false,
233
237
  withMetadata: false,
234
238
  withMetadataOrientation: -1,
239
+ withMetadataDensity: 0,
235
240
  withMetadataIcc: '',
241
+ withMetadataStrs: {},
236
242
  resolveWithObject: false,
237
243
  // output format
238
244
  jpegQuality: 80,
@@ -244,7 +250,7 @@ const Sharp = function (input, options) {
244
250
  jpegOptimiseCoding: true,
245
251
  jpegQuantisationTable: 0,
246
252
  pngProgressive: false,
247
- pngCompressionLevel: 9,
253
+ pngCompressionLevel: 6,
248
254
  pngAdaptiveFiltering: false,
249
255
  pngPalette: false,
250
256
  pngQuality: 100,
@@ -281,6 +287,7 @@ const Sharp = function (input, options) {
281
287
  tileSkipBlanks: -1,
282
288
  tileBackground: [255, 255, 255, 255],
283
289
  tileCentre: false,
290
+ tileId: 'https://example.com/iiif',
284
291
  linearA: 1,
285
292
  linearB: 0,
286
293
  // Function to notify of libvips warnings
package/lib/input.js CHANGED
@@ -9,9 +9,9 @@ const sharp = require('../build/Release/sharp.node');
9
9
  * @private
10
10
  */
11
11
  function _inputOptionsFromObject (obj) {
12
- const { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages } = obj;
13
- return [raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages].some(is.defined)
14
- ? { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages }
12
+ const { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages, subifd } = obj;
13
+ return [raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages, subifd].some(is.defined)
14
+ ? { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages, subifd }
15
15
  : undefined;
16
16
  }
17
17
 
@@ -30,9 +30,15 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
30
30
  inputDescriptor.file = input;
31
31
  } else if (is.buffer(input)) {
32
32
  // Buffer
33
+ if (input.length === 0) {
34
+ throw Error('Input Buffer is empty');
35
+ }
33
36
  inputDescriptor.buffer = input;
34
37
  } else if (is.uint8Array(input)) {
35
38
  // Uint8Array or Uint8ClampedArray
39
+ if (input.length === 0) {
40
+ throw Error('Input Bit Array is empty');
41
+ }
36
42
  inputDescriptor.buffer = Buffer.from(input.buffer);
37
43
  } else if (is.plainObject(input) && !is.defined(inputOptions)) {
38
44
  // Plain Object descriptor, e.g. create
@@ -97,6 +103,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
97
103
  inputDescriptor.rawWidth = inputOptions.raw.width;
98
104
  inputDescriptor.rawHeight = inputOptions.raw.height;
99
105
  inputDescriptor.rawChannels = inputOptions.raw.channels;
106
+ inputDescriptor.rawPremultiplied = !!inputOptions.raw.premultiplied;
100
107
  } else {
101
108
  throw new Error('Expected width, height and channels for raw pixel input');
102
109
  }
@@ -131,6 +138,14 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
131
138
  throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level);
132
139
  }
133
140
  }
141
+ // Sub Image File Directory (TIFF)
142
+ if (is.defined(inputOptions.subifd)) {
143
+ if (is.integer(inputOptions.subifd) && is.inRange(inputOptions.subifd, -1, 100000)) {
144
+ inputDescriptor.subifd = inputOptions.subifd;
145
+ } else {
146
+ throw is.invalidParameterError('subifd', 'integer between -1 and 100000', inputOptions.subifd);
147
+ }
148
+ }
134
149
  // Create new image
135
150
  if (is.defined(inputOptions.create)) {
136
151
  if (
@@ -255,6 +270,7 @@ function _isStreamInput () {
255
270
  * - `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
256
271
  * - `pagePrimary`: Number of the primary page in a HEIF image
257
272
  * - `levels`: Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide
273
+ * - `subifds`: Number of Sub Image File Directories in an OME-TIFF image
258
274
  * - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
259
275
  * - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
260
276
  * - `orientation`: Number value of the EXIF Orientation header, if present
package/lib/libvips.js CHANGED
@@ -4,13 +4,15 @@ const fs = require('fs');
4
4
  const os = require('os');
5
5
  const path = require('path');
6
6
  const spawnSync = require('child_process').spawnSync;
7
- const semver = require('semver');
7
+ const semverCoerce = require('semver/functions/coerce');
8
+ const semverGreaterThanOrEqualTo = require('semver/functions/gte');
9
+
8
10
  const platform = require('./platform');
9
11
 
10
12
  const env = process.env;
11
13
  const minimumLibvipsVersionLabelled = env.npm_package_config_libvips || /* istanbul ignore next */
12
14
  require('../package.json').config.libvips;
13
- const minimumLibvipsVersion = semver.coerce(minimumLibvipsVersionLabelled).version;
15
+ const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
14
16
 
15
17
  const spawnSyncOptions = {
16
18
  encoding: 'utf8',
@@ -37,6 +39,14 @@ const cachePath = function () {
37
39
  return libvipsCachePath;
38
40
  };
39
41
 
42
+ const log = function (item) {
43
+ if (item instanceof Error) {
44
+ console.error(`sharp: Installation error: ${item.message}`);
45
+ } else {
46
+ console.log(`sharp: ${item}`);
47
+ }
48
+ };
49
+
40
50
  const isRosetta = function () {
41
51
  /* istanbul ignore next */
42
52
  if (process.platform === 'darwin' && process.arch === 'x64') {
@@ -97,13 +107,14 @@ const useGlobalLibvips = function () {
97
107
  }
98
108
  const globalVipsVersion = globalLibvipsVersion();
99
109
  return !!globalVipsVersion && /* istanbul ignore next */
100
- semver.gte(globalVipsVersion, minimumLibvipsVersion);
110
+ semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion);
101
111
  };
102
112
 
103
113
  module.exports = {
104
114
  minimumLibvipsVersion,
105
115
  minimumLibvipsVersionLabelled,
106
116
  cachePath,
117
+ log,
107
118
  globalLibvipsVersion,
108
119
  hasVendoredLibvips,
109
120
  pkgConfigPath,