sharp 0.30.4 → 0.30.7

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.
@@ -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? Try again with the --unsafe-perm flag');
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.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode
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
package/lib/input.js CHANGED
@@ -86,10 +86,10 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
86
86
  inputDescriptor.limitInputPixels = inputOptions.limitInputPixels
87
87
  ? Math.pow(0x3FFF, 2)
88
88
  : 0;
89
- } else if (is.integer(inputOptions.limitInputPixels) && inputOptions.limitInputPixels >= 0) {
89
+ } else if (is.integer(inputOptions.limitInputPixels) && is.inRange(inputOptions.limitInputPixels, 0, Number.MAX_SAFE_INTEGER)) {
90
90
  inputDescriptor.limitInputPixels = inputOptions.limitInputPixels;
91
91
  } else {
92
- throw is.invalidParameterError('limitInputPixels', 'integer >= 0', inputOptions.limitInputPixels);
92
+ throw is.invalidParameterError('limitInputPixels', 'positive integer', inputOptions.limitInputPixels);
93
93
  }
94
94
  }
95
95
  // unlimited
@@ -309,9 +309,9 @@ function _isStreamInput () {
309
309
  * - `size`: Total size of image in bytes, for Stream and Buffer input only
310
310
  * - `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
311
311
  * - `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)
312
- * - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation)
312
+ * - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/VipsImage.html#VipsInterpretation)
313
313
  * - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
314
- * - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsBandFormat)
314
+ * - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://www.libvips.org/API/current/VipsImage.html#VipsBandFormat)
315
315
  * - `density`: Number of pixels per inch (DPI), if present
316
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
317
317
  * - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
@@ -357,7 +357,7 @@ function _isStreamInput () {
357
357
  * const size = getNormalSize(await sharp(input).metadata());
358
358
  *
359
359
  * function getNormalSize({ width, height, orientation }) {
360
- * return orientation || 0 >= 5
360
+ * return (orientation || 0) >= 5
361
361
  * ? { width: height, height: width }
362
362
  * : { width, height };
363
363
  * }
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(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout;
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('which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR', spawnSyncOptions).stdout || '';
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/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 => { ... });
@@ -492,7 +495,7 @@ function webp (options) {
492
495
  if (is.defined(options.smartSubsample)) {
493
496
  this._setBooleanOption('webpSmartSubsample', options.smartSubsample);
494
497
  }
495
- const effort = options.effort || options.reductionEffort;
498
+ const effort = is.defined(options.effort) ? options.effort : options.reductionEffort;
496
499
  if (is.defined(effort)) {
497
500
  if (is.integer(effort) && is.inRange(effort, 0, 6)) {
498
501
  this.options.webpEffort = effort;
@@ -516,7 +519,7 @@ function webp (options) {
516
519
  * // Convert PNG to GIF
517
520
  * await sharp(pngBuffer)
518
521
  * .gif()
519
- * .toBuffer());
522
+ * .toBuffer();
520
523
  *
521
524
  * @example
522
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
- /* istanbul ignore next */
11
- const libc = (platform === 'linux' && detectLibc.isNonGlibcLinuxSync()) ? detectLibc.familySync() : '';
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}${libc}`];
15
+ const platformId = [`${platform}${libcId}`];
14
16
 
15
17
  if (arch === 'arm') {
16
18
  const fallback = process.versions.electron ? '7' : '6';
package/lib/resize.js CHANGED
@@ -211,7 +211,7 @@ function isRotationExpected (options) {
211
211
  * @param {String} [options.height] - alternative means of specifying `height`. If both are present this take priority.
212
212
  * @param {String} [options.fit='cover'] - how the image should be resized to fit both provided dimensions, one of `cover`, `contain`, `fill`, `inside` or `outside`.
213
213
  * @param {String} [options.position='centre'] - position, gravity or strategy to use when `fit` is `cover` or `contain`.
214
- * @param {String|Object} [options.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour when using a `fit` of `contain`, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
214
+ * @param {String|Object} [options.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour when `fit` is `contain`, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
215
215
  * @param {String} [options.kernel='lanczos3'] - the kernel to use for image reduction.
216
216
  * @param {Boolean} [options.withoutEnlargement=false] - do not enlarge if the width *or* height are already less than the specified dimensions, equivalent to GraphicsMagick's `>` geometry option.
217
217
  * @param {Boolean} [options.withoutReduction=false] - do not reduce if the width *or* height are already greater than the specified dimensions, equivalent to GraphicsMagick's `<` geometry option.
package/lib/sharp.js CHANGED
@@ -12,8 +12,11 @@ try {
12
12
  help.push('- Update Homebrew: "brew update && brew upgrade vips"');
13
13
  } else {
14
14
  const [platform, arch] = platformAndArch.split('-');
15
+ if (platform === 'linux' && /Module did not self-register/.test(err.message)) {
16
+ help.push('- Using worker threads? See https://sharp.pixelplumbing.com/install#worker-threads');
17
+ }
15
18
  help.push(
16
- '- Install with the --verbose flag and look for errors: "npm install --ignore-scripts=false --verbose sharp"',
19
+ '- Install with verbose logging and look for errors: "npm install --ignore-scripts=false --foreground-scripts --verbose sharp"',
17
20
  `- Install for the current ${platformAndArch} runtime: "npm install --platform=${platform} --arch=${arch} sharp"`
18
21
  );
19
22
  }
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 _libvips'_ should create to process each image.
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
- * The maximum number of images that can be processed in parallel
115
- * is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
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
- * This method always returns the current concurrency.
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",
4
+ "version": "0.30.7",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://github.com/lovell/sharp",
7
7
  "contributors": [
@@ -82,7 +82,8 @@
82
82
  "Joris Dugué <zaruike10@gmail.com>",
83
83
  "Chris Banks <christopher.bradley.banks@gmail.com>",
84
84
  "Ompal Singh <ompal.hitm09@gmail.com>",
85
- "Brodan <christopher.hranj@gmail.com"
85
+ "Brodan <christopher.hranj@gmail.com",
86
+ "Ankur Parihar <ankur.github@gmail.com>"
86
87
  ],
87
88
  "scripts": {
88
89
  "install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)",
@@ -129,22 +130,22 @@
129
130
  "dependencies": {
130
131
  "color": "^4.2.3",
131
132
  "detect-libc": "^2.0.1",
132
- "node-addon-api": "^4.3.0",
133
- "prebuild-install": "^7.0.1",
133
+ "node-addon-api": "^5.0.0",
134
+ "prebuild-install": "^7.1.1",
134
135
  "semver": "^7.3.7",
135
136
  "simple-get": "^4.0.1",
136
137
  "tar-fs": "^2.1.1",
137
138
  "tunnel-agent": "^0.6.0"
138
139
  },
139
140
  "devDependencies": {
140
- "async": "^3.2.3",
141
+ "async": "^3.2.4",
141
142
  "cc": "^3.0.1",
142
143
  "decompress-zip": "^0.3.3",
143
144
  "documentation": "^13.2.5",
144
145
  "exif-reader": "^1.0.3",
145
146
  "icc": "^2.0.0",
146
147
  "license-checker": "^25.0.1",
147
- "mocha": "^9.2.2",
148
+ "mocha": "^10.0.0",
148
149
  "mock-fs": "^5.1.2",
149
150
  "nyc": "^15.1.0",
150
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
  }
@@ -131,7 +134,7 @@ namespace sharp {
131
134
  }
132
135
  }
133
136
  // Limit input images to a given number of pixels, where pixels = width * height
134
- descriptor->limitInputPixels = AttrAsUint32(input, "limitInputPixels");
137
+ descriptor->limitInputPixels = static_cast<uint64_t>(AttrAsInt64(input, "limitInputPixels"));
135
138
  // Allow switch from random to sequential access
136
139
  descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
137
140
  // Remove safety features and allow unlimited SVG/PNG input
@@ -439,7 +442,7 @@ namespace sharp {
439
442
  }
440
443
  // Limit input images to a given number of pixels, where pixels = width * height
441
444
  if (descriptor->limitInputPixels > 0 &&
442
- static_cast<uint64_t>(image.width() * image.height()) > static_cast<uint64_t>(descriptor->limitInputPixels)) {
445
+ static_cast<uint64_t>(image.width() * image.height()) > descriptor->limitInputPixels) {
443
446
  throw vips::VError("Input image exceeds pixel limit");
444
447
  }
445
448
  return std::make_tuple(image, imageType);
package/src/common.h CHANGED
@@ -49,7 +49,7 @@ namespace sharp {
49
49
  std::string file;
50
50
  char *buffer;
51
51
  VipsFailOn failOn;
52
- int limitInputPixels;
52
+ uint64_t limitInputPixels;
53
53
  bool unlimited;
54
54
  VipsAccess access;
55
55
  size_t bufferLength;
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
- if (flip) {
99
- image = image.flip(VIPS_DIRECTION_VERTICAL);
100
- flip = FALSE;
101
- }
102
- if (flop) {
103
- image = image.flip(VIPS_DIRECTION_HORIZONTAL);
104
- flop = FALSE;
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;
@@ -186,8 +188,10 @@ class PipelineWorker : public Napi::AsyncWorker {
186
188
  if (jpegShrinkOnLoad > 1 && static_cast<int>(shrink) == jpegShrinkOnLoad) {
187
189
  jpegShrinkOnLoad /= 2;
188
190
  }
189
- } else if (inputImageType == sharp::ImageType::WEBP ||
190
- inputImageType == sharp::ImageType::SVG ||
191
+ } else if (inputImageType == sharp::ImageType::WEBP && shrink > 1.0) {
192
+ // Avoid upscaling via webp
193
+ scale = 1.0 / shrink;
194
+ } else if (inputImageType == sharp::ImageType::SVG ||
191
195
  inputImageType == sharp::ImageType::PDF) {
192
196
  scale = 1.0 / shrink;
193
197
  }
@@ -605,14 +609,14 @@ class PipelineWorker : public Napi::AsyncWorker {
605
609
  int across = 0;
606
610
  int down = 0;
607
611
  // Use gravity in overlay
608
- if (compositeImage.width() <= baton->width) {
612
+ if (compositeImage.width() <= image.width()) {
609
613
  across = static_cast<int>(ceil(static_cast<double>(image.width()) / compositeImage.width()));
610
614
  // Ensure odd number of tiles across when gravity is centre, north or south
611
615
  if (composite->gravity == 0 || composite->gravity == 1 || composite->gravity == 3) {
612
616
  across |= 1;
613
617
  }
614
618
  }
615
- if (compositeImage.height() <= baton->height) {
619
+ if (compositeImage.height() <= image.height()) {
616
620
  down = static_cast<int>(ceil(static_cast<double>(image.height()) / compositeImage.height()));
617
621
  // Ensure odd number of tiles down when gravity is centre, east or west
618
622
  if (composite->gravity == 0 || composite->gravity == 2 || composite->gravity == 4) {