@revizly/sharp 0.34.1-revizly9 → 0.34.4-revizly10

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/lib/input.js CHANGED
@@ -1,8 +1,6 @@
1
1
  // Copyright 2013 Lovell Fuller and others.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- 'use strict';
5
-
6
4
  const is = require('./is');
7
5
  const sharp = require('./sharp');
8
6
 
@@ -22,14 +20,27 @@ const align = {
22
20
  high: 'high'
23
21
  };
24
22
 
23
+ const inputStreamParameters = [
24
+ // Limits and error handling
25
+ 'failOn', 'limitInputPixels', 'unlimited',
26
+ // Format-generic
27
+ 'animated', 'autoOrient', 'density', 'ignoreIcc', 'page', 'pages', 'sequentialRead',
28
+ // Format-specific
29
+ 'jp2', 'openSlide', 'pdf', 'raw', 'svg', 'tiff',
30
+ // Deprecated
31
+ 'failOnError', 'openSlideLevel', 'pdfBackground', 'tiffSubifd'
32
+ ];
33
+
25
34
  /**
26
35
  * Extract input options, if any, from an object.
27
36
  * @private
28
37
  */
29
38
  function _inputOptionsFromObject (obj) {
30
- const { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient } = obj;
31
- return [raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient].some(is.defined)
32
- ? { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient }
39
+ const params = inputStreamParameters
40
+ .filter(p => is.defined(obj[p]))
41
+ .map(p => ([p, obj[p]]));
42
+ return params.length
43
+ ? Object.fromEntries(params)
33
44
  : undefined;
34
45
  }
35
46
 
@@ -41,7 +52,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
41
52
  const inputDescriptor = {
42
53
  autoOrient: false,
43
54
  failOn: 'warning',
44
- limitInputPixels: Math.pow(0x3FFF, 2),
55
+ limitInputPixels: 0x3FFF ** 2,
45
56
  ignoreIcc: false,
46
57
  unlimited: false,
47
58
  sequentialRead: true
@@ -137,7 +148,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
137
148
  if (is.defined(inputOptions.limitInputPixels)) {
138
149
  if (is.bool(inputOptions.limitInputPixels)) {
139
150
  inputDescriptor.limitInputPixels = inputOptions.limitInputPixels
140
- ? Math.pow(0x3FFF, 2)
151
+ ? 0x3FFF ** 2
141
152
  : 0;
142
153
  } else if (is.integer(inputOptions.limitInputPixels) && is.inRange(inputOptions.limitInputPixels, 0, Number.MAX_SAFE_INTEGER)) {
143
154
  inputDescriptor.limitInputPixels = inputOptions.limitInputPixels;
@@ -172,8 +183,6 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
172
183
  inputDescriptor.rawWidth = inputOptions.raw.width;
173
184
  inputDescriptor.rawHeight = inputOptions.raw.height;
174
185
  inputDescriptor.rawChannels = inputOptions.raw.channels;
175
- inputDescriptor.rawPremultiplied = !!inputOptions.raw.premultiplied;
176
-
177
186
  switch (input.constructor) {
178
187
  case Uint8Array:
179
188
  case Uint8ClampedArray:
@@ -207,6 +216,25 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
207
216
  } else {
208
217
  throw new Error('Expected width, height and channels for raw pixel input');
209
218
  }
219
+ inputDescriptor.rawPremultiplied = false;
220
+ if (is.defined(inputOptions.raw.premultiplied)) {
221
+ if (is.bool(inputOptions.raw.premultiplied)) {
222
+ inputDescriptor.rawPremultiplied = inputOptions.raw.premultiplied;
223
+ } else {
224
+ throw is.invalidParameterError('raw.premultiplied', 'boolean', inputOptions.raw.premultiplied);
225
+ }
226
+ }
227
+ inputDescriptor.rawPageHeight = 0;
228
+ if (is.defined(inputOptions.raw.pageHeight)) {
229
+ if (is.integer(inputOptions.raw.pageHeight) && inputOptions.raw.pageHeight > 0 && inputOptions.raw.pageHeight <= inputOptions.raw.height) {
230
+ if (inputOptions.raw.height % inputOptions.raw.pageHeight !== 0) {
231
+ throw new Error(`Expected raw.height ${inputOptions.raw.height} to be a multiple of raw.pageHeight ${inputOptions.raw.pageHeight}`);
232
+ }
233
+ inputDescriptor.rawPageHeight = inputOptions.raw.pageHeight;
234
+ } else {
235
+ throw is.invalidParameterError('raw.pageHeight', 'positive integer', inputOptions.raw.pageHeight);
236
+ }
237
+ }
210
238
  }
211
239
  // Multi-page input (GIF, TIFF, PDF)
212
240
  if (is.defined(inputOptions.animated)) {
@@ -230,26 +258,68 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
230
258
  throw is.invalidParameterError('page', 'integer between 0 and 100000', inputOptions.page);
231
259
  }
232
260
  }
233
- // Multi-level input (OpenSlide)
234
- if (is.defined(inputOptions.level)) {
261
+ // OpenSlide specific options
262
+ if (is.object(inputOptions.openSlide) && is.defined(inputOptions.openSlide.level)) {
263
+ if (is.integer(inputOptions.openSlide.level) && is.inRange(inputOptions.openSlide.level, 0, 256)) {
264
+ inputDescriptor.openSlideLevel = inputOptions.openSlide.level;
265
+ } else {
266
+ throw is.invalidParameterError('openSlide.level', 'integer between 0 and 256', inputOptions.openSlide.level);
267
+ }
268
+ } else if (is.defined(inputOptions.level)) {
269
+ // Deprecated
235
270
  if (is.integer(inputOptions.level) && is.inRange(inputOptions.level, 0, 256)) {
236
- inputDescriptor.level = inputOptions.level;
271
+ inputDescriptor.openSlideLevel = inputOptions.level;
237
272
  } else {
238
273
  throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level);
239
274
  }
240
275
  }
241
- // Sub Image File Directory (TIFF)
242
- if (is.defined(inputOptions.subifd)) {
276
+ // TIFF specific options
277
+ if (is.object(inputOptions.tiff) && is.defined(inputOptions.tiff.subifd)) {
278
+ if (is.integer(inputOptions.tiff.subifd) && is.inRange(inputOptions.tiff.subifd, -1, 100000)) {
279
+ inputDescriptor.tiffSubifd = inputOptions.tiff.subifd;
280
+ } else {
281
+ throw is.invalidParameterError('tiff.subifd', 'integer between -1 and 100000', inputOptions.tiff.subifd);
282
+ }
283
+ } else if (is.defined(inputOptions.subifd)) {
284
+ // Deprecated
243
285
  if (is.integer(inputOptions.subifd) && is.inRange(inputOptions.subifd, -1, 100000)) {
244
- inputDescriptor.subifd = inputOptions.subifd;
286
+ inputDescriptor.tiffSubifd = inputOptions.subifd;
245
287
  } else {
246
288
  throw is.invalidParameterError('subifd', 'integer between -1 and 100000', inputOptions.subifd);
247
289
  }
248
290
  }
249
- // PDF background colour
250
- if (is.defined(inputOptions.pdfBackground)) {
291
+ // SVG specific options
292
+ if (is.object(inputOptions.svg)) {
293
+ if (is.defined(inputOptions.svg.stylesheet)) {
294
+ if (is.string(inputOptions.svg.stylesheet)) {
295
+ inputDescriptor.svgStylesheet = inputOptions.svg.stylesheet;
296
+ } else {
297
+ throw is.invalidParameterError('svg.stylesheet', 'string', inputOptions.svg.stylesheet);
298
+ }
299
+ }
300
+ if (is.defined(inputOptions.svg.highBitdepth)) {
301
+ if (is.bool(inputOptions.svg.highBitdepth)) {
302
+ inputDescriptor.svgHighBitdepth = inputOptions.svg.highBitdepth;
303
+ } else {
304
+ throw is.invalidParameterError('svg.highBitdepth', 'boolean', inputOptions.svg.highBitdepth);
305
+ }
306
+ }
307
+ }
308
+ // PDF specific options
309
+ if (is.object(inputOptions.pdf) && is.defined(inputOptions.pdf.background)) {
310
+ inputDescriptor.pdfBackground = this._getBackgroundColourOption(inputOptions.pdf.background);
311
+ } else if (is.defined(inputOptions.pdfBackground)) {
312
+ // Deprecated
251
313
  inputDescriptor.pdfBackground = this._getBackgroundColourOption(inputOptions.pdfBackground);
252
314
  }
315
+ // JPEG 2000 specific options
316
+ if (is.object(inputOptions.jp2) && is.defined(inputOptions.jp2.oneshot)) {
317
+ if (is.bool(inputOptions.jp2.oneshot)) {
318
+ inputDescriptor.jp2Oneshot = inputOptions.jp2.oneshot;
319
+ } else {
320
+ throw is.invalidParameterError('jp2.oneshot', 'boolean', inputOptions.jp2.oneshot);
321
+ }
322
+ }
253
323
  // Create new image
254
324
  if (is.defined(inputOptions.create)) {
255
325
  if (
@@ -261,27 +331,44 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
261
331
  inputDescriptor.createWidth = inputOptions.create.width;
262
332
  inputDescriptor.createHeight = inputOptions.create.height;
263
333
  inputDescriptor.createChannels = inputOptions.create.channels;
334
+ inputDescriptor.createPageHeight = 0;
335
+ if (is.defined(inputOptions.create.pageHeight)) {
336
+ if (is.integer(inputOptions.create.pageHeight) && inputOptions.create.pageHeight > 0 && inputOptions.create.pageHeight <= inputOptions.create.height) {
337
+ if (inputOptions.create.height % inputOptions.create.pageHeight !== 0) {
338
+ throw new Error(`Expected create.height ${inputOptions.create.height} to be a multiple of create.pageHeight ${inputOptions.create.pageHeight}`);
339
+ }
340
+ inputDescriptor.createPageHeight = inputOptions.create.pageHeight;
341
+ } else {
342
+ throw is.invalidParameterError('create.pageHeight', 'positive integer', inputOptions.create.pageHeight);
343
+ }
344
+ }
264
345
  // Noise
265
346
  if (is.defined(inputOptions.create.noise)) {
266
347
  if (!is.object(inputOptions.create.noise)) {
267
348
  throw new Error('Expected noise to be an object');
268
349
  }
269
- if (!is.inArray(inputOptions.create.noise.type, ['gaussian'])) {
350
+ if (inputOptions.create.noise.type !== 'gaussian') {
270
351
  throw new Error('Only gaussian noise is supported at the moment');
271
352
  }
353
+ inputDescriptor.createNoiseType = inputOptions.create.noise.type;
272
354
  if (!is.inRange(inputOptions.create.channels, 1, 4)) {
273
355
  throw is.invalidParameterError('create.channels', 'number between 1 and 4', inputOptions.create.channels);
274
356
  }
275
- inputDescriptor.createNoiseType = inputOptions.create.noise.type;
276
- if (is.number(inputOptions.create.noise.mean) && is.inRange(inputOptions.create.noise.mean, 0, 10000)) {
277
- inputDescriptor.createNoiseMean = inputOptions.create.noise.mean;
278
- } else {
279
- throw is.invalidParameterError('create.noise.mean', 'number between 0 and 10000', inputOptions.create.noise.mean);
357
+ inputDescriptor.createNoiseMean = 128;
358
+ if (is.defined(inputOptions.create.noise.mean)) {
359
+ if (is.number(inputOptions.create.noise.mean) && is.inRange(inputOptions.create.noise.mean, 0, 10000)) {
360
+ inputDescriptor.createNoiseMean = inputOptions.create.noise.mean;
361
+ } else {
362
+ throw is.invalidParameterError('create.noise.mean', 'number between 0 and 10000', inputOptions.create.noise.mean);
363
+ }
280
364
  }
281
- if (is.number(inputOptions.create.noise.sigma) && is.inRange(inputOptions.create.noise.sigma, 0, 10000)) {
282
- inputDescriptor.createNoiseSigma = inputOptions.create.noise.sigma;
283
- } else {
284
- throw is.invalidParameterError('create.noise.sigma', 'number between 0 and 10000', inputOptions.create.noise.sigma);
365
+ inputDescriptor.createNoiseSigma = 30;
366
+ if (is.defined(inputOptions.create.noise.sigma)) {
367
+ if (is.number(inputOptions.create.noise.sigma) && is.inRange(inputOptions.create.noise.sigma, 0, 10000)) {
368
+ inputDescriptor.createNoiseSigma = inputOptions.create.noise.sigma;
369
+ } else {
370
+ throw is.invalidParameterError('create.noise.sigma', 'number between 0 and 10000', inputOptions.create.noise.sigma);
371
+ }
285
372
  }
286
373
  } else if (is.defined(inputOptions.create.background)) {
287
374
  if (!is.inRange(inputOptions.create.channels, 3, 4)) {
@@ -424,7 +511,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
424
511
  }
425
512
  }
426
513
  } else if (is.defined(inputOptions)) {
427
- throw new Error('Invalid input options ' + inputOptions);
514
+ throw new Error(`Invalid input options ${inputOptions}`);
428
515
  }
429
516
  return inputDescriptor;
430
517
  }
@@ -436,10 +523,8 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
436
523
  * @param {string} encoding - unused
437
524
  * @param {Function} callback
438
525
  */
439
- function _write (chunk, encoding, callback) {
440
- /* istanbul ignore else */
526
+ function _write (chunk, _encoding, callback) {
441
527
  if (Array.isArray(this.options.input.buffer)) {
442
- /* istanbul ignore else */
443
528
  if (is.buffer(chunk)) {
444
529
  if (this.options.input.buffer.length === 0) {
445
530
  this.on('finish', () => {
@@ -516,6 +601,7 @@ function _isStreamInput () {
516
601
  * - `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
517
602
  * - `iptc`: Buffer containing raw IPTC data, if present
518
603
  * - `xmp`: Buffer containing raw XMP data, if present
604
+ * - `xmpAsString`: String containing XMP data, if valid UTF-8.
519
605
  * - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
520
606
  * - `formatMagick`: String containing format for images loaded via *magick
521
607
  * - `comments`: Array of keyword/text pairs representing PNG text blocks, if present.
package/lib/is.js CHANGED
@@ -1,8 +1,6 @@
1
1
  // Copyright 2013 Lovell Fuller and others.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- 'use strict';
5
-
6
4
  /**
7
5
  * Is this value defined and not null?
8
6
  * @private
package/lib/libvips.js CHANGED
@@ -1,8 +1,6 @@
1
1
  // Copyright 2013 Lovell Fuller and others.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- 'use strict';
5
-
6
4
  const { spawnSync } = require('node:child_process');
7
5
  const { createHash } = require('node:crypto');
8
6
  const semverCoerce = require('semver/functions/coerce');
@@ -12,15 +10,15 @@ const detectLibc = require('detect-libc');
12
10
 
13
11
  const { config, engines, optionalDependencies } = require('../package.json');
14
12
 
15
- const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || /* istanbul ignore next */
16
- config.libvips;
13
+ /* node:coverage ignore next */
14
+ const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || config.libvips;
17
15
  const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
18
16
 
19
17
  const prebuiltPlatforms = [
20
18
  'darwin-arm64', 'darwin-x64',
21
- 'linux-arm', 'linux-arm64', 'linux-s390x', 'linux-x64',
19
+ 'linux-arm', 'linux-arm64', 'linux-ppc64', 'linux-s390x', 'linux-x64',
22
20
  'linuxmusl-arm64', 'linuxmusl-x64',
23
- 'win32-ia32', 'win32-x64'
21
+ 'win32-arm64', 'win32-ia32', 'win32-x64'
24
22
  ];
25
23
 
26
24
  const spawnSyncOptions = {
@@ -36,17 +34,16 @@ const log = (item) => {
36
34
  }
37
35
  };
38
36
 
39
- /* istanbul ignore next */
37
+ /* node:coverage ignore next */
40
38
  const runtimeLibc = () => detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '';
41
39
 
42
40
  const runtimePlatformArch = () => `${process.platform}${runtimeLibc()}-${process.arch}`;
43
41
 
44
- /* istanbul ignore next */
45
42
  const buildPlatformArch = () => {
43
+ /* node:coverage ignore next 3 */
46
44
  if (isEmscripten()) {
47
45
  return 'wasm32';
48
46
  }
49
- /* eslint camelcase: ["error", { allow: ["^npm_config_"] }] */
50
47
  const { npm_config_arch, npm_config_platform, npm_config_libc } = process.env;
51
48
  const libc = typeof npm_config_libc === 'string' ? npm_config_libc : runtimeLibc();
52
49
  return `${npm_config_platform || process.platform}${libc}-${npm_config_arch || process.arch}`;
@@ -56,19 +53,19 @@ const buildSharpLibvipsIncludeDir = () => {
56
53
  try {
57
54
  return require(`@revizly/sharp-libvips-dev-${buildPlatformArch()}/include`);
58
55
  } catch {
56
+ /* node:coverage ignore next 5 */
59
57
  try {
60
58
  return require('@revizly/sharp-libvips-dev/include');
61
59
  } catch {}
62
60
  }
63
- /* istanbul ignore next */
64
61
  return '';
65
62
  };
66
63
 
67
64
  const buildSharpLibvipsCPlusPlusDir = () => {
65
+ /* node:coverage ignore next 4 */
68
66
  try {
69
67
  return require('@revizly/sharp-libvips-dev/cplusplus');
70
68
  } catch {}
71
- /* istanbul ignore next */
72
69
  return '';
73
70
  };
74
71
 
@@ -76,16 +73,17 @@ const buildSharpLibvipsLibDir = () => {
76
73
  try {
77
74
  return require(`@revizly/sharp-libvips-dev-${buildPlatformArch()}/lib`);
78
75
  } catch {
76
+ /* node:coverage ignore next 5 */
79
77
  try {
80
78
  return require(`@revizly/sharp-libvips-${buildPlatformArch()}/lib`);
81
79
  } catch {}
82
80
  }
83
- /* istanbul ignore next */
84
81
  return '';
85
82
  };
86
83
 
84
+ /* node:coverage disable */
85
+
87
86
  const isUnsupportedNodeRuntime = () => {
88
- /* istanbul ignore next */
89
87
  if (process.release?.name === 'node' && process.versions) {
90
88
  if (!semverSatisfies(process.versions.node, engines.node)) {
91
89
  return { found: process.versions.node, expected: engines.node };
@@ -93,14 +91,12 @@ const isUnsupportedNodeRuntime = () => {
93
91
  }
94
92
  };
95
93
 
96
- /* istanbul ignore next */
97
94
  const isEmscripten = () => {
98
95
  const { CC } = process.env;
99
- return Boolean(CC && CC.endsWith('/emcc'));
96
+ return Boolean(CC?.endsWith('/emcc'));
100
97
  };
101
98
 
102
99
  const isRosetta = () => {
103
- /* istanbul ignore next */
104
100
  if (process.platform === 'darwin' && process.arch === 'x64') {
105
101
  const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout;
106
102
  return (translated || '').trim() === 'sysctl.proc_translated: 1';
@@ -108,6 +104,8 @@ const isRosetta = () => {
108
104
  return false;
109
105
  };
110
106
 
107
+ /* node:coverage enable */
108
+
111
109
  const sha512 = (s) => createHash('sha512').update(s).digest('hex');
112
110
 
113
111
  const yarnLocator = () => {
@@ -121,7 +119,8 @@ const yarnLocator = () => {
121
119
  return '';
122
120
  };
123
121
 
124
- /* istanbul ignore next */
122
+ /* node:coverage disable */
123
+
125
124
  const spawnRebuild = () =>
126
125
  spawnSync(`node-gyp rebuild --directory=src ${isEmscripten() ? '--nodedir=emscripten' : ''}`, {
127
126
  ...spawnSyncOptions,
@@ -137,16 +136,17 @@ const globalLibvipsVersion = () => {
137
136
  PKG_CONFIG_PATH: pkgConfigPath()
138
137
  }
139
138
  }).stdout;
140
- /* istanbul ignore next */
141
139
  return (globalLibvipsVersion || '').trim();
142
140
  } else {
143
141
  return '';
144
142
  }
145
143
  };
146
144
 
147
- /* istanbul ignore next */
145
+ /* node:coverage enable */
146
+
148
147
  const pkgConfigPath = () => {
149
148
  if (process.platform !== 'win32') {
149
+ /* node:coverage ignore next 4 */
150
150
  const brewPkgConfigPath = spawnSync(
151
151
  'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
152
152
  spawnSyncOptions
@@ -178,13 +178,13 @@ const useGlobalLibvips = (logger) => {
178
178
  if (Boolean(process.env.SHARP_FORCE_GLOBAL_LIBVIPS) === true) {
179
179
  return skipSearch(true, 'SHARP_FORCE_GLOBAL_LIBVIPS', logger);
180
180
  }
181
- /* istanbul ignore next */
181
+ /* node:coverage ignore next 3 */
182
182
  if (isRosetta()) {
183
183
  return skipSearch(false, 'Rosetta', logger);
184
184
  }
185
185
  const globalVipsVersion = globalLibvipsVersion();
186
- return !!globalVipsVersion && /* istanbul ignore next */
187
- semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion);
186
+ /* node:coverage ignore next */
187
+ return !!globalVipsVersion && semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion);
188
188
  };
189
189
 
190
190
  module.exports = {
package/lib/operation.js CHANGED
@@ -1,8 +1,6 @@
1
1
  // Copyright 2013 Lovell Fuller and others.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- 'use strict';
5
-
6
4
  const is = require('./is');
7
5
 
8
6
  /**
package/lib/output.js CHANGED
@@ -1,8 +1,6 @@
1
1
  // Copyright 2013 Lovell Fuller and others.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- 'use strict';
5
-
6
4
  const path = require('node:path');
7
5
  const is = require('./is');
8
6
  const sharp = require('./sharp');
@@ -312,6 +310,59 @@ function withIccProfile (icc, options) {
312
310
  return this;
313
311
  }
314
312
 
313
+ /**
314
+ * Keep XMP metadata from the input image in the output image.
315
+ *
316
+ * @since 0.34.3
317
+ *
318
+ * @example
319
+ * const outputWithXmp = await sharp(inputWithXmp)
320
+ * .keepXmp()
321
+ * .toBuffer();
322
+ *
323
+ * @returns {Sharp}
324
+ */
325
+ function keepXmp () {
326
+ this.options.keepMetadata |= 0b00010;
327
+ return this;
328
+ }
329
+
330
+ /**
331
+ * Set XMP metadata in the output image.
332
+ *
333
+ * Supported by PNG, JPEG, WebP, and TIFF output.
334
+ *
335
+ * @since 0.34.3
336
+ *
337
+ * @example
338
+ * const xmpString = `
339
+ * <?xml version="1.0"?>
340
+ * <x:xmpmeta xmlns:x="adobe:ns:meta/">
341
+ * <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
342
+ * <rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">
343
+ * <dc:creator><rdf:Seq><rdf:li>John Doe</rdf:li></rdf:Seq></dc:creator>
344
+ * </rdf:Description>
345
+ * </rdf:RDF>
346
+ * </x:xmpmeta>`;
347
+ *
348
+ * const data = await sharp(input)
349
+ * .withXmp(xmpString)
350
+ * .toBuffer();
351
+ *
352
+ * @param {string} xmp String containing XMP metadata to be embedded in the output image.
353
+ * @returns {Sharp}
354
+ * @throws {Error} Invalid parameters
355
+ */
356
+ function withXmp (xmp) {
357
+ if (is.string(xmp) && xmp.length > 0) {
358
+ this.options.withXmp = xmp;
359
+ this.options.keepMetadata |= 0b00010;
360
+ } else {
361
+ throw is.invalidParameterError('xmp', 'non-empty string', xmp);
362
+ }
363
+ return this;
364
+ }
365
+
315
366
  /**
316
367
  * Keep all metadata (EXIF, ICC, XMP, IPTC) from the input image in the output image.
317
368
  *
@@ -729,6 +780,7 @@ function webp (options) {
729
780
  * @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most)
730
781
  * @param {number} [options.interFrameMaxError=0] - maximum inter-frame error for transparency, between 0 (lossless) and 32
731
782
  * @param {number} [options.interPaletteMaxError=3] - maximum inter-palette error for palette reuse, between 0 and 256
783
+ * @param {boolean} [options.keepDuplicateFrames=false] - keep duplicate frames in the output instead of combining them
732
784
  * @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
733
785
  * @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
734
786
  * @param {boolean} [options.force=true] - force GIF output, otherwise attempt to use input format
@@ -779,12 +831,18 @@ function gif (options) {
779
831
  throw is.invalidParameterError('interPaletteMaxError', 'number between 0.0 and 256.0', options.interPaletteMaxError);
780
832
  }
781
833
  }
834
+ if (is.defined(options.keepDuplicateFrames)) {
835
+ if (is.bool(options.keepDuplicateFrames)) {
836
+ this._setBooleanOption('gifKeepDuplicateFrames', options.keepDuplicateFrames);
837
+ } else {
838
+ throw is.invalidParameterError('keepDuplicateFrames', 'boolean', options.keepDuplicateFrames);
839
+ }
840
+ }
782
841
  }
783
842
  trySetAnimationOptions(options, this.options);
784
843
  return this._updateFormatOut('gif', options);
785
844
  }
786
845
 
787
- /* istanbul ignore next */
788
846
  /**
789
847
  * Use these JP2 options for output image.
790
848
  *
@@ -819,6 +877,7 @@ function gif (options) {
819
877
  * @throws {Error} Invalid options
820
878
  */
821
879
  function jp2 (options) {
880
+ /* node:coverage ignore next 41 */
822
881
  if (!this.constructor.format.jp2k.output.buffer) {
823
882
  throw errJp2Save();
824
883
  }
@@ -1019,6 +1078,9 @@ function tiff (options) {
1019
1078
  * AVIF image sequences are not supported.
1020
1079
  * Prebuilt binaries support a bitdepth of 8 only.
1021
1080
  *
1081
+ * This feature is experimental on the Windows ARM64 platform
1082
+ * and requires a CPU with ARM64v8.4 or later.
1083
+ *
1022
1084
  * @example
1023
1085
  * const data = await sharp(input)
1024
1086
  * .avif({ effort: 2 })
@@ -1438,7 +1500,6 @@ function _setBooleanOption (key, val) {
1438
1500
  * @private
1439
1501
  */
1440
1502
  function _read () {
1441
- /* istanbul ignore else */
1442
1503
  if (!this.options.streamOut) {
1443
1504
  this.options.streamOut = true;
1444
1505
  const stack = Error();
@@ -1565,6 +1626,8 @@ module.exports = function (Sharp) {
1565
1626
  withExifMerge,
1566
1627
  keepIccProfile,
1567
1628
  withIccProfile,
1629
+ keepXmp,
1630
+ withXmp,
1568
1631
  keepMetadata,
1569
1632
  withMetadata,
1570
1633
  toFormat,
package/lib/resize.js CHANGED
@@ -1,8 +1,6 @@
1
1
  // Copyright 2013 Lovell Fuller and others.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- 'use strict';
5
-
6
4
  const is = require('./is');
7
5
 
8
6
  /**
@@ -107,7 +105,7 @@ const mapFitToCanvas = {
107
105
  * @private
108
106
  */
109
107
  function isRotationExpected (options) {
110
- return (options.angle % 360) !== 0 || options.input.autoOrient === true || options.rotationAngle !== 0;
108
+ return (options.angle % 360) !== 0 || options.rotationAngle !== 0;
111
109
  }
112
110
 
113
111
  /**
@@ -129,7 +127,7 @@ function isResizeExpected (options) {
129
127
  *
130
128
  * Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
131
129
  *
132
- * <img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/public/api-resize-fit.svg">
130
+ * <img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="/api-resize-fit.svg">
133
131
  *
134
132
  * When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
135
133
  * - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
@@ -150,6 +148,8 @@ function isResizeExpected (options) {
150
148
  * - `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
151
149
  * - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
152
150
  * - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
151
+ * - `mks2013`: Use a [Magic Kernel Sharp](https://johncostella.com/magic/mks.pdf) 2013 kernel, as adopted by Facebook.
152
+ * - `mks2021`: Use a Magic Kernel Sharp 2021 kernel, with more accurate (reduced) sharpening than the 2013 version.
153
153
  *
154
154
  * When upsampling, these kernels map to `nearest`, `linear` and `cubic` interpolators.
155
155
  * Downsampling kernels without a matching upsampling interpolator map to `cubic`.
@@ -341,7 +341,7 @@ function resize (widthOrOptions, height, options) {
341
341
  }
342
342
  }
343
343
  if (isRotationExpected(this.options) && isResizeExpected(this.options)) {
344
- this.options.rotateBeforePreExtract = true;
344
+ this.options.rotateBefore = true;
345
345
  }
346
346
  return this;
347
347
  }
@@ -488,9 +488,12 @@ function extract (options) {
488
488
  // Ensure existing rotation occurs before pre-resize extraction
489
489
  if (isRotationExpected(this.options) && !isResizeExpected(this.options)) {
490
490
  if (this.options.widthPre === -1 || this.options.widthPost === -1) {
491
- this.options.rotateBeforePreExtract = true;
491
+ this.options.rotateBefore = true;
492
492
  }
493
493
  }
494
+ if (this.options.input.autoOrient) {
495
+ this.options.orientBefore = true;
496
+ }
494
497
  return this;
495
498
  }
496
499
 
@@ -564,7 +567,7 @@ function trim (options) {
564
567
  }
565
568
  }
566
569
  if (isRotationExpected(this.options)) {
567
- this.options.rotateBeforePreExtract = true;
570
+ this.options.rotateBefore = true;
568
571
  }
569
572
  return this;
570
573
  }
package/lib/sharp.js CHANGED
@@ -1,8 +1,6 @@
1
1
  // Copyright 2013 Lovell Fuller and others.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- 'use strict';
5
-
6
4
  // Inspects the runtime environment and exports the relevant sharp.node binary
7
5
 
8
6
  const { familySync, versionSync } = require('detect-libc');
@@ -17,6 +15,8 @@ const paths = [
17
15
  '@revizly/sharp-wasm32/sharp.node'
18
16
  ];
19
17
 
18
+ /* node:coverage disable */
19
+
20
20
  let path, sharp;
21
21
  const errors = [];
22
22
  for (path of paths) {
@@ -24,12 +24,10 @@ for (path of paths) {
24
24
  sharp = require(path);
25
25
  break;
26
26
  } catch (err) {
27
- /* istanbul ignore next */
28
27
  errors.push(err);
29
28
  }
30
29
  }
31
30
 
32
- /* istanbul ignore next */
33
31
  if (sharp && path.startsWith('@img/sharp-linux-x64') && !sharp._isUsingX64V2()) {
34
32
  const err = new Error('Prebuilt binaries for linux-x64 require v2 microarchitecture');
35
33
  err.code = 'Unsupported CPU';
@@ -37,7 +35,6 @@ if (sharp && path.startsWith('@img/sharp-linux-x64') && !sharp._isUsingX64V2())
37
35
  sharp = null;
38
36
  }
39
37
 
40
- /* istanbul ignore next */
41
38
  if (sharp) {
42
39
  module.exports = sharp;
43
40
  } else {
@@ -88,7 +85,7 @@ if (sharp) {
88
85
  ` Found ${libcFound}`,
89
86
  ` Requires ${libcRequires}`
90
87
  );
91
- } catch (errEngines) {}
88
+ } catch (_errEngines) {}
92
89
  }
93
90
  if (isLinux && /\/snap\/core[0-9]{2}/.test(messages)) {
94
91
  help.push(