sharp 0.29.0 → 0.30.0

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
@@ -4,7 +4,7 @@
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
@@ -103,7 +103,7 @@ covers reporting bugs, requesting features and submitting code changes.
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.
package/binding.gyp CHANGED
@@ -39,6 +39,7 @@
39
39
  'VCCLCompilerTool': {
40
40
  'ExceptionHandling': 1,
41
41
  'Optimization': 1,
42
+ 'RuntimeLibrary': '2', # /MD
42
43
  'WholeProgramOptimization': 'true'
43
44
  },
44
45
  'VCLibrarianTool': {
@@ -205,6 +206,7 @@
205
206
  'VCCLCompilerTool': {
206
207
  'ExceptionHandling': 1,
207
208
  'Optimization': 1,
209
+ 'RuntimeLibrary': '2', # /MD
208
210
  'WholeProgramOptimization': 'true'
209
211
  },
210
212
  'VCLibrarianTool': {
@@ -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}`);
@@ -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,
@@ -199,6 +201,7 @@ const Sharp = function (input, options) {
199
201
  brightness: 1,
200
202
  saturation: 1,
201
203
  hue: 0,
204
+ lightness: 0,
202
205
  booleanBufferIn: null,
203
206
  booleanFileIn: '',
204
207
  joinChannelIn: [],
@@ -232,14 +235,23 @@ const Sharp = function (input, options) {
232
235
  pngAdaptiveFiltering: false,
233
236
  pngPalette: false,
234
237
  pngQuality: 100,
235
- pngColours: 256,
238
+ pngEffort: 7,
239
+ pngBitdepth: 8,
236
240
  pngDither: 1,
241
+ jp2Quality: 80,
242
+ jp2TileHeight: 512,
243
+ jp2TileWidth: 512,
244
+ jp2Lossless: false,
245
+ jp2ChromaSubsampling: '4:4:4',
237
246
  webpQuality: 80,
238
247
  webpAlphaQuality: 100,
239
248
  webpLossless: false,
240
249
  webpNearLossless: false,
241
250
  webpSmartSubsample: false,
242
- webpReductionEffort: 4,
251
+ webpEffort: 4,
252
+ gifBitdepth: 8,
253
+ gifEffort: 7,
254
+ gifDither: 1,
243
255
  tiffQuality: 80,
244
256
  tiffCompression: 'jpeg',
245
257
  tiffPredictor: 'horizontal',
@@ -250,10 +262,11 @@ const Sharp = function (input, options) {
250
262
  tiffTileWidth: 256,
251
263
  tiffXres: 1.0,
252
264
  tiffYres: 1.0,
265
+ tiffResolutionUnit: 'inch',
253
266
  heifQuality: 50,
254
267
  heifLossless: false,
255
268
  heifCompression: 'av1',
256
- heifSpeed: 5,
269
+ heifEffort: 4,
257
270
  heifChromaSubsampling: '4:4:4',
258
271
  rawDepth: 'uchar',
259
272
  tileSize: 256,
@@ -267,6 +280,7 @@ const Sharp = function (input, options) {
267
280
  tileBackground: [255, 255, 255, 255],
268
281
  tileCentre: false,
269
282
  tileId: 'https://example.com/iiif',
283
+ timeoutSeconds: 0,
270
284
  linearA: 1,
271
285
  linearB: 0,
272
286
  // Function to notify of libvips warnings
@@ -282,7 +296,8 @@ const Sharp = function (input, options) {
282
296
  this.options.input = this._createInputDescriptor(input, options, { allowStream: true });
283
297
  return this;
284
298
  };
285
- util.inherits(Sharp, stream.Duplex);
299
+ Object.setPrototypeOf(Sharp.prototype, stream.Duplex.prototype);
300
+ Object.setPrototypeOf(Sharp, stream.Duplex);
286
301
 
287
302
  /**
288
303
  * Take a "snapshot" of the Sharp instance, returning a new instance.
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,10 +74,15 @@ 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 || '';
@@ -99,9 +111,11 @@ module.exports = {
99
111
  minimumLibvipsVersion,
100
112
  minimumLibvipsVersionLabelled,
101
113
  cachePath,
114
+ integrity,
102
115
  log,
103
116
  globalLibvipsVersion,
104
117
  hasVendoredLibvips,
118
+ removeVendoredLibvips,
105
119
  pkgConfigPath,
106
120
  useGlobalLibvips,
107
121
  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
@@ -570,14 +583,16 @@ function recomb (inputMatrix) {
570
583
  }
571
584
 
572
585
  /**
573
- * Transforms the image using brightness, saturation and hue rotation.
586
+ * Transforms the image using brightness, saturation, hue rotation, and lightness.
587
+ * Brightness and lightness both operate on luminance, with the difference being that
588
+ * brightness is multiplicative whereas lightness is additive.
574
589
  *
575
590
  * @since 0.22.1
576
591
  *
577
592
  * @example
578
593
  * sharp(input)
579
594
  * .modulate({
580
- * brightness: 2 // increase lightness by a factor of 2
595
+ * brightness: 2 // increase brightness by a factor of 2
581
596
  * });
582
597
  *
583
598
  * sharp(input)
@@ -585,6 +600,11 @@ function recomb (inputMatrix) {
585
600
  * hue: 180 // hue-rotate by 180 degrees
586
601
  * });
587
602
  *
603
+ * sharp(input)
604
+ * .modulate({
605
+ * lightness: 50 // increase lightness by +50
606
+ * });
607
+ *
588
608
  * // decreate brightness and saturation while also hue-rotating by 90 degrees
589
609
  * sharp(input)
590
610
  * .modulate({
@@ -597,6 +617,7 @@ function recomb (inputMatrix) {
597
617
  * @param {number} [options.brightness] Brightness multiplier
598
618
  * @param {number} [options.saturation] Saturation multiplier
599
619
  * @param {number} [options.hue] Degrees for hue rotation
620
+ * @param {number} [options.lightness] Lightness addend
600
621
  * @returns {Sharp}
601
622
  */
602
623
  function modulate (options) {
@@ -624,6 +645,13 @@ function modulate (options) {
624
645
  throw is.invalidParameterError('hue', 'number', options.hue);
625
646
  }
626
647
  }
648
+ if ('lightness' in options) {
649
+ if (is.number(options.lightness)) {
650
+ this.options.lightness = options.lightness;
651
+ } else {
652
+ throw is.invalidParameterError('lightness', 'number', options.lightness);
653
+ }
654
+ }
627
655
  return this;
628
656
  }
629
657