sharp 0.29.3 → 0.30.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 CHANGED
@@ -1,10 +1,10 @@
1
1
  # sharp
2
2
 
3
- <img src="https://cdn.jsdelivr.net/gh/lovell/sharp@master/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
3
+ <img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
4
4
 
5
5
  The typical use case for this high speed Node.js module
6
6
  is to convert large images in common formats to
7
- smaller, web-friendly JPEG, PNG, WebP and AVIF images of varying dimensions.
7
+ smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions.
8
8
 
9
9
  Resizing an image is typically 4x-5x faster than using the
10
10
  quickest ImageMagick and GraphicsMagick settings
@@ -95,15 +95,15 @@ readableStream
95
95
 
96
96
  ## Contributing
97
97
 
98
- A [guide for contributors](https://github.com/lovell/sharp/blob/master/.github/CONTRIBUTING.md)
98
+ A [guide for contributors](https://github.com/lovell/sharp/blob/main/.github/CONTRIBUTING.md)
99
99
  covers reporting bugs, requesting features and submitting code changes.
100
100
 
101
- [![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.svg?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
101
+ [![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.svg?branch=main)](https://coveralls.io/r/lovell/sharp?branch=main)
102
102
  [![Node-API v5](https://img.shields.io/badge/Node--API-v5-green.svg)](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api_version_matrix)
103
103
 
104
104
  ## Licensing
105
105
 
106
- Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Lovell Fuller and contributors.
106
+ Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Lovell Fuller and contributors.
107
107
 
108
108
  Licensed under the Apache License, Version 2.0 (the "License");
109
109
  you may not use this file except in compliance with the License.
@@ -5,6 +5,7 @@ const os = require('os');
5
5
  const path = require('path');
6
6
  const stream = require('stream');
7
7
  const zlib = require('zlib');
8
+ const { createHash } = require('crypto');
8
9
 
9
10
  const detectLibc = require('detect-libc');
10
11
  const semverLessThan = require('semver/functions/lt');
@@ -18,7 +19,7 @@ const platform = require('../lib/platform');
18
19
 
19
20
  const minimumGlibcVersionByArch = {
20
21
  arm: '2.28',
21
- arm64: '2.29',
22
+ arm64: '2.17',
22
23
  x64: '2.17'
23
24
  };
24
25
 
@@ -55,6 +56,33 @@ const handleError = function (err) {
55
56
  }
56
57
  };
57
58
 
59
+ const verifyIntegrity = function (platformAndArch) {
60
+ const expected = libvips.integrity(platformAndArch);
61
+ if (installationForced || !expected) {
62
+ libvips.log(`Integrity check skipped for ${platformAndArch}`);
63
+ return new stream.PassThrough();
64
+ }
65
+ const hash = createHash('sha512');
66
+ return new stream.Transform({
67
+ transform: function (chunk, _encoding, done) {
68
+ hash.update(chunk);
69
+ done(null, chunk);
70
+ },
71
+ flush: function (done) {
72
+ const digest = `sha512-${hash.digest('base64')}`;
73
+ if (expected !== digest) {
74
+ libvips.removeVendoredLibvips();
75
+ libvips.log(`Integrity expected: ${expected}`);
76
+ libvips.log(`Integrity received: ${digest}`);
77
+ done(new Error(`Integrity check failed for ${platformAndArch}`));
78
+ } else {
79
+ libvips.log(`Integrity check passed for ${platformAndArch}`);
80
+ done();
81
+ }
82
+ }
83
+ });
84
+ };
85
+
58
86
  const extractTarball = function (tarPath, platformAndArch) {
59
87
  const versionedVendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platformAndArch);
60
88
  libvips.mkdirSync(versionedVendorPath);
@@ -66,6 +94,7 @@ const extractTarball = function (tarPath, platformAndArch) {
66
94
 
67
95
  stream.pipeline(
68
96
  fs.createReadStream(tarPath),
97
+ verifyIntegrity(platformAndArch),
69
98
  new zlib.BrotliDecompress(),
70
99
  tarFs.extract(versionedVendorPath, { ignore }),
71
100
  function (err) {
@@ -103,14 +132,16 @@ try {
103
132
  throw new Error(`BSD/SunOS systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
104
133
  }
105
134
  // Linux libc version check
106
- if (detectLibc.family === detectLibc.GLIBC && detectLibc.version && minimumGlibcVersionByArch[arch]) {
107
- if (semverLessThan(`${detectLibc.version}.0`, `${minimumGlibcVersionByArch[arch]}.0`)) {
108
- handleError(new Error(`Use with glibc ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
135
+ const libcFamily = detectLibc.familySync();
136
+ const libcVersion = detectLibc.versionSync();
137
+ if (libcFamily === detectLibc.GLIBC && libcVersion && minimumGlibcVersionByArch[arch]) {
138
+ if (semverLessThan(`${libcVersion}.0`, `${minimumGlibcVersionByArch[arch]}.0`)) {
139
+ handleError(new Error(`Use with glibc ${libcVersion} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
109
140
  }
110
141
  }
111
- if (detectLibc.family === detectLibc.MUSL && detectLibc.version) {
112
- if (semverLessThan(detectLibc.version, '1.1.24')) {
113
- handleError(new Error(`Use with musl ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
142
+ if (libcFamily === detectLibc.MUSL && libcVersion) {
143
+ if (semverLessThan(libcVersion, '1.1.24')) {
144
+ handleError(new Error(`Use with musl ${libcVersion} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
114
145
  }
115
146
  }
116
147
  // Node.js minimum version check
@@ -120,7 +151,7 @@ try {
120
151
  }
121
152
 
122
153
  // Download to per-process temporary file
123
- const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.br';
154
+ const tarFilename = ['libvips', minimumLibvipsVersionLabelled, platformAndArch].join('-') + '.tar.br';
124
155
  const tarPathCache = path.join(libvips.cachePath(), tarFilename);
125
156
  if (fs.existsSync(tarPathCache)) {
126
157
  libvips.log(`Using cached ${tarPathCache}`);
package/lib/composite.js CHANGED
@@ -162,7 +162,6 @@ function composite (images) {
162
162
  throw is.invalidParameterError('premultiplied', 'boolean', image.premultiplied);
163
163
  }
164
164
  }
165
-
166
165
  return composite;
167
166
  });
168
167
  return this;
@@ -13,7 +13,7 @@ const debuglog = util.debuglog('sharp');
13
13
  /**
14
14
  * Constructor factory to create an instance of `sharp`, to which further methods are chained.
15
15
  *
16
- * JPEG, PNG, WebP, AVIF or TIFF format image data can be streamed out from this object.
16
+ * JPEG, PNG, WebP, GIF, AVIF or TIFF format image data can be streamed out from this object.
17
17
  * When using Stream based output, derived attributes are available from the `info` event.
18
18
  *
19
19
  * Non-critical problems encountered during processing are emitted as `warning` events.
@@ -103,6 +103,7 @@ const debuglog = util.debuglog('sharp');
103
103
  * @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
104
104
  * (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
105
105
  * An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
106
+ * @param {boolean} [options.unlimited=false] - Set this to `true` to remove safety features that help prevent memory exhaustion (SVG, PNG).
106
107
  * @param {boolean} [options.sequentialRead=false] - Set this to `true` to use sequential rather than random access where possible.
107
108
  * This can reduce memory usage and might improve performance on some systems.
108
109
  * @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
@@ -165,6 +166,7 @@ const Sharp = function (input, options) {
165
166
  extendRight: 0,
166
167
  extendBackground: [0, 0, 0, 255],
167
168
  withoutEnlargement: false,
169
+ withoutReduction: false,
168
170
  affineMatrix: [],
169
171
  affineBackground: [0, 0, 0, 255],
170
172
  affineIdx: 0,
@@ -233,6 +235,7 @@ const Sharp = function (input, options) {
233
235
  pngAdaptiveFiltering: false,
234
236
  pngPalette: false,
235
237
  pngQuality: 100,
238
+ pngEffort: 7,
236
239
  pngBitdepth: 8,
237
240
  pngDither: 1,
238
241
  jp2Quality: 80,
@@ -245,7 +248,10 @@ const Sharp = function (input, options) {
245
248
  webpLossless: false,
246
249
  webpNearLossless: false,
247
250
  webpSmartSubsample: false,
248
- webpReductionEffort: 4,
251
+ webpEffort: 4,
252
+ gifBitdepth: 8,
253
+ gifEffort: 7,
254
+ gifDither: 1,
249
255
  tiffQuality: 80,
250
256
  tiffCompression: 'jpeg',
251
257
  tiffPredictor: 'horizontal',
@@ -256,10 +262,11 @@ const Sharp = function (input, options) {
256
262
  tiffTileWidth: 256,
257
263
  tiffXres: 1.0,
258
264
  tiffYres: 1.0,
265
+ tiffResolutionUnit: 'inch',
259
266
  heifQuality: 50,
260
267
  heifLossless: false,
261
268
  heifCompression: 'av1',
262
- heifSpeed: 5,
269
+ heifEffort: 4,
263
270
  heifChromaSubsampling: '4:4:4',
264
271
  rawDepth: 'uchar',
265
272
  tileSize: 256,
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, 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 }
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 }
15
15
  : undefined;
16
16
  }
17
17
 
@@ -23,6 +23,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
23
23
  const inputDescriptor = {
24
24
  failOnError: true,
25
25
  limitInputPixels: Math.pow(0x3FFF, 2),
26
+ unlimited: false,
26
27
  sequentialRead: false
27
28
  };
28
29
  if (is.string(input)) {
@@ -83,6 +84,14 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
83
84
  throw is.invalidParameterError('limitInputPixels', 'integer >= 0', inputOptions.limitInputPixels);
84
85
  }
85
86
  }
87
+ // unlimited
88
+ if (is.defined(inputOptions.unlimited)) {
89
+ if (is.bool(inputOptions.unlimited)) {
90
+ inputDescriptor.unlimited = inputOptions.unlimited;
91
+ } else {
92
+ throw is.invalidParameterError('unlimited', 'boolean', inputOptions.unlimited);
93
+ }
94
+ }
86
95
  // sequentialRead
87
96
  if (is.defined(inputOptions.sequentialRead)) {
88
97
  if (is.bool(inputOptions.sequentialRead)) {
@@ -281,7 +290,11 @@ function _isStreamInput () {
281
290
  }
282
291
 
283
292
  /**
284
- * Fast access to (uncached) image metadata without decoding any compressed image data.
293
+ * Fast access to (uncached) image metadata without decoding any compressed pixel data.
294
+ *
295
+ * This is taken from the header of the input image.
296
+ * It does not include operations, such as resize, to be applied to the output image.
297
+ *
285
298
  * A `Promise` is returned when `callback` is not provided.
286
299
  *
287
300
  * - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
@@ -303,6 +316,7 @@ function _isStreamInput () {
303
316
  * - `subifds`: Number of Sub Image File Directories in an OME-TIFF image
304
317
  * - `background`: Default background colour, if present, for PNG (bKGD) and GIF images, either an RGB Object or a single greyscale value
305
318
  * - `compression`: The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC)
319
+ * - `resolutionUnit`: The unit of resolution (density), either `inch` or `cm`, if present
306
320
  * - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
307
321
  * - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
308
322
  * - `orientation`: Number value of the EXIF Orientation header, if present
@@ -313,6 +327,9 @@ function _isStreamInput () {
313
327
  * - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
314
328
  *
315
329
  * @example
330
+ * const metadata = await sharp(input).metadata();
331
+ *
332
+ * @example
316
333
  * const image = sharp(inputJpg);
317
334
  * image
318
335
  * .metadata()
package/lib/libvips.js CHANGED
@@ -8,10 +8,11 @@ const semverCoerce = require('semver/functions/coerce');
8
8
  const semverGreaterThanOrEqualTo = require('semver/functions/gte');
9
9
 
10
10
  const platform = require('./platform');
11
+ const { config } = require('../package.json');
11
12
 
12
13
  const env = process.env;
13
14
  const minimumLibvipsVersionLabelled = env.npm_package_config_libvips || /* istanbul ignore next */
14
- require('../package.json').config.libvips;
15
+ config.libvips;
15
16
  const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
16
17
 
17
18
  const spawnSyncOptions = {
@@ -19,6 +20,8 @@ const spawnSyncOptions = {
19
20
  shell: true
20
21
  };
21
22
 
23
+ const vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platform());
24
+
22
25
  const mkdirSync = function (dirPath) {
23
26
  try {
24
27
  fs.mkdirSync(dirPath, { recursive: true });
@@ -39,6 +42,10 @@ const cachePath = function () {
39
42
  return libvipsCachePath;
40
43
  };
41
44
 
45
+ const integrity = function (platformAndArch) {
46
+ return env[`npm_package_config_integrity_${platformAndArch.replace('-', '_')}`] || config.integrity[platformAndArch];
47
+ };
48
+
42
49
  const log = function (item) {
43
50
  if (item instanceof Error) {
44
51
  console.error(`sharp: Installation error: ${item.message}`);
@@ -67,16 +74,26 @@ const globalLibvipsVersion = function () {
67
74
  };
68
75
 
69
76
  const hasVendoredLibvips = function () {
70
- const vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platform());
71
77
  return fs.existsSync(vendorPath);
72
78
  };
73
79
 
80
+ /* istanbul ignore next */
81
+ const removeVendoredLibvips = function () {
82
+ const rm = fs.rmSync ? fs.rmSync : fs.rmdirSync;
83
+ rm(vendorPath, { recursive: true, maxRetries: 3, force: true });
84
+ };
85
+
74
86
  const pkgConfigPath = function () {
75
87
  if (process.platform !== 'win32') {
76
88
  const brewPkgConfigPath = spawnSync('which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR', spawnSyncOptions).stdout || '';
77
- return [brewPkgConfigPath.trim(), env.PKG_CONFIG_PATH, '/usr/local/lib/pkgconfig', '/usr/lib/pkgconfig']
78
- .filter(function (p) { return !!p; })
79
- .join(':');
89
+ return [
90
+ brewPkgConfigPath.trim(),
91
+ env.PKG_CONFIG_PATH,
92
+ '/usr/local/lib/pkgconfig',
93
+ '/usr/lib/pkgconfig',
94
+ '/usr/local/libdata/pkgconfig',
95
+ '/usr/libdata/pkgconfig'
96
+ ].filter(Boolean).join(':');
80
97
  } else {
81
98
  return '';
82
99
  }
@@ -99,9 +116,11 @@ module.exports = {
99
116
  minimumLibvipsVersion,
100
117
  minimumLibvipsVersionLabelled,
101
118
  cachePath,
119
+ integrity,
102
120
  log,
103
121
  globalLibvipsVersion,
104
122
  hasVendoredLibvips,
123
+ removeVendoredLibvips,
105
124
  pkgConfigPath,
106
125
  useGlobalLibvips,
107
126
  mkdirSync
package/lib/operation.js CHANGED
@@ -245,8 +245,21 @@ function median (size) {
245
245
 
246
246
  /**
247
247
  * Blur the image.
248
- * When used without parameters, performs a fast, mild blur of the output image.
248
+ *
249
+ * When used without parameters, performs a fast 3x3 box blur (equivalent to a box linear filter).
250
+ *
249
251
  * When a `sigma` is provided, performs a slower, more accurate Gaussian blur.
252
+ *
253
+ * @example
254
+ * const boxBlurred = await sharp(input)
255
+ * .blur()
256
+ * .toBuffer();
257
+ *
258
+ * @example
259
+ * const gaussianBlurred = await sharp(input)
260
+ * .blur(5)
261
+ * .toBuffer();
262
+ *
250
263
  * @param {number} [sigma] a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
251
264
  * @returns {Sharp}
252
265
  * @throws {Error} Invalid parameters