sharp 0.31.0 → 0.31.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
@@ -98,7 +98,6 @@ readableStream
98
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=main)](https://coveralls.io/r/lovell/sharp?branch=main)
102
101
  [![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
102
 
104
103
  ## Licensing
package/lib/libvips.js CHANGED
@@ -89,6 +89,7 @@ const removeVendoredLibvips = function () {
89
89
  rm(vendorPath, { recursive: true, maxRetries: 3, force: true });
90
90
  };
91
91
 
92
+ /* istanbul ignore next */
92
93
  const pkgConfigPath = function () {
93
94
  if (process.platform !== 'win32') {
94
95
  const brewPkgConfigPath = spawnSync(
package/lib/output.js CHANGED
@@ -1205,7 +1205,7 @@ function _pipeline (callback) {
1205
1205
  this.push(data);
1206
1206
  }
1207
1207
  this.push(null);
1208
- this.emit('close');
1208
+ this.on('end', () => this.emit('close'));
1209
1209
  });
1210
1210
  });
1211
1211
  if (this.streamInFinished) {
@@ -1221,7 +1221,7 @@ function _pipeline (callback) {
1221
1221
  this.push(data);
1222
1222
  }
1223
1223
  this.push(null);
1224
- this.emit('close');
1224
+ this.on('end', () => this.emit('close'));
1225
1225
  });
1226
1226
  }
1227
1227
  return this;
package/lib/resize.js CHANGED
@@ -111,7 +111,7 @@ function isResizeExpected (options) {
111
111
  *
112
112
  * Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
113
113
  *
114
- * When using a `fit` of `cover` or `contain`, the default **position** is `centre`. Other options are:
114
+ * When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
115
115
  * - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
116
116
  * - `sharp.gravity`: `north`, `northeast`, `east`, `southeast`, `south`, `southwest`, `west`, `northwest`, `center` or `centre`.
117
117
  * - `sharp.strategy`: `cover` only, dynamically crop using either the `entropy` or `attention` strategy.
@@ -428,7 +428,7 @@ function extend (extend) {
428
428
  * @throws {Error} Invalid parameters
429
429
  */
430
430
  function extract (options) {
431
- const suffix = isResizeExpected(this.options) || isRotationExpected(this.options) ? 'Post' : 'Pre';
431
+ const suffix = isResizeExpected(this.options) || this.options.widthPre !== -1 ? 'Post' : 'Pre';
432
432
  if (this.options[`width${suffix}`] !== -1) {
433
433
  this.options.debuglog('ignoring previous extract options');
434
434
  }
@@ -511,15 +511,12 @@ function trim (trim) {
511
511
  }
512
512
  } else if (is.object(trim)) {
513
513
  this._setBackgroundColourOption('trimBackground', trim.background);
514
-
515
514
  if (!is.defined(trim.threshold)) {
516
515
  this.options.trimThreshold = 10;
517
- } else if (is.number(trim.threshold)) {
518
- if (trim.threshold >= 0) {
519
- this.options.trimThreshold = trim.threshold;
520
- } else {
521
- throw is.invalidParameterError('threshold', 'positive number', trim);
522
- }
516
+ } else if (is.number(trim.threshold) && trim.threshold >= 0) {
517
+ this.options.trimThreshold = trim.threshold;
518
+ } else {
519
+ throw is.invalidParameterError('threshold', 'positive number', trim);
523
520
  }
524
521
  } else {
525
522
  throw is.invalidParameterError('trim', 'string, number or object', trim);
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.31.0",
4
+ "version": "0.31.2",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://github.com/lovell/sharp",
7
7
  "contributors": [
@@ -92,9 +92,8 @@
92
92
  "clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
93
93
  "test": "npm run test-lint && npm run test-unit && npm run test-licensing",
94
94
  "test-lint": "semistandard && cpplint",
95
- "test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=1000 --timeout=60000 ./test/unit/*.js",
95
+ "test-unit": "nyc --reporter=lcov --reporter=text --check-coverage --branches=100 mocha --slow=1000 --timeout=30000 ./test/unit/*.js",
96
96
  "test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
97
- "test-coverage": "./test/coverage/report.sh",
98
97
  "test-leak": "./test/leak/leak.sh",
99
98
  "docs-build": "documentation lint lib && node docs/build && node docs/search-index/build",
100
99
  "docs-serve": "cd docs && npx serve",
@@ -134,7 +133,7 @@
134
133
  "detect-libc": "^2.0.1",
135
134
  "node-addon-api": "^5.0.0",
136
135
  "prebuild-install": "^7.1.1",
137
- "semver": "^7.3.7",
136
+ "semver": "^7.3.8",
138
137
  "simple-get": "^4.0.1",
139
138
  "tar-fs": "^2.1.1",
140
139
  "tunnel-agent": "^0.6.0"
@@ -147,8 +146,8 @@
147
146
  "extract-zip": "^2.0.1",
148
147
  "icc": "^2.0.0",
149
148
  "license-checker": "^25.0.1",
150
- "mocha": "^10.0.0",
151
- "mock-fs": "^5.1.4",
149
+ "mocha": "^10.1.0",
150
+ "mock-fs": "^5.2.0",
152
151
  "nyc": "^15.1.0",
153
152
  "prebuild": "^11.0.4",
154
153
  "rimraf": "^3.0.2",
@@ -156,19 +155,19 @@
156
155
  },
157
156
  "license": "Apache-2.0",
158
157
  "config": {
159
- "libvips": "8.13.1",
158
+ "libvips": "8.13.3",
160
159
  "integrity": {
161
- "darwin-arm64v8": "sha512-JdpGTx67RDbvRkg3ljFvTzqoq+oBXmMdDFEp0expDYXmP5HLH+GCkikmsROlGltgfKE2KqL/qwpxTEhIwMK/3A==",
162
- "darwin-x64": "sha512-0Oh4/hEDnzV+X8MiiyUQ4G/Zh/MHw9rKstfuX0P1czgaxS2hX8Pxdbzdk1oqwTOEYVEGO/hMm9ItCVZ3RVPPaA==",
163
- "linux-arm64v8": "sha512-9pSlPzEojt6ue5vXfASNMhQO1YS1p4i4Wydu+bzOfMtIPSBRXbu/+y8WELbbo03Ts7pftm9KtrMHitCVdy5EXw==",
164
- "linux-armv6": "sha512-sv2FqS/ggpQly7h5/+nh8txQDulolE5ptaE90PO7iwfTont8N42pudeqootWKsuf0fRmkW4M92184VfVVYCvGw==",
165
- "linux-armv7": "sha512-LmQIB8FDfasK6BsFhnE7ZI3LMlxh/rF5tZRNQ/uoTbF2xrtWQqqgiZgCifJByiEM+1tR7RxwNdnjxZhWvM9WmQ==",
166
- "linux-x64": "sha512-JBRf8WBnlVw/K1jpSvmeZpnGZGjeqhG2NDEiQV/hUze3zgDGwDza4oiworaQExQmKcDrc2LJKF14Nsz1qQSNJw==",
167
- "linuxmusl-arm64v8": "sha512-yzUQO5isDwsRpEUxbMXBeWp0sKhWghebrSK46SUF5mvB/kq6hZ7JbRuJ2aZjE84K/HUTyuCc0kE+M3m8naOs+g==",
168
- "linuxmusl-x64": "sha512-H3Vz1QaaZ6X5iEbfPST7TPFwDO01tI8dk1osLm6l4a17BWCaOMaBQlqxgTgYrtd09JJ9CvGoq5fo5j5TPxUc4Q==",
169
- "win32-arm64v8": "sha512-b5Ver+uwOJhdOGqvZVM+qF2KLKcowcac/wKK5Fg0czqlSMqP/KxDF2kxw2eKXUJNgfqe4eDH1QG/yTg2pQSetQ==",
170
- "win32-ia32": "sha512-h/SJ/Yfn0ce9H70vt1wS8rZ4PfHnguCCTsOGik7e6O/e2AlBQOM0mKsPIB9jSOquoCP8rP0qF6AOPOjXKnCk+w==",
171
- "win32-x64": "sha512-p9qpdWdhZooPteib92Kk+qF1vvzcScxvOwdIP8muhgo/A8uDI4/mqXCpEbMBw6vjETKlS3qo2JUbVF6+0/lyWQ=="
160
+ "darwin-arm64v8": "sha512-xFgYt7CtQSZcWoyUdzPTDNHbioZIrZSEU+gkMxzH4Cgjhi4/N49UsonnIZhKQoTBGloAqEexHeMx4rYTQ2Kgvw==",
161
+ "darwin-x64": "sha512-6SivWKzu15aUMMohe0wg7sNYMPETVnOe40BuWsnKOgzl3o5FpQqNSgs+68Mi8Za3Qti9/DaR+H/fyD0x48Af2w==",
162
+ "linux-arm64v8": "sha512-b+iI9V/ehgDabXYRQcvqa5CEysh+1FQsgFmYc358StCrJCDahwNmsQdsiH1GOVd5WaWh5wHUGByPwMmFOO16Aw==",
163
+ "linux-armv6": "sha512-zRP2F+EiustLE4bXSH8AHCxwfemh9d+QuvmPjira/HL6uJOUuA7SyQgVV1TPwTQle2ioCNnKPm7FEB/MAiT+ug==",
164
+ "linux-armv7": "sha512-6OCChowE5lBXXXAZrnGdA9dVktg7UdODEBpE5qTroiAJYZv4yXRMgyDFYajok7du2NTgoklhxGk8d9+4vGv5hg==",
165
+ "linux-x64": "sha512-OTmlmP2r8ozGKdB96X+K5oQE1ojVZanqLqqKlwDpEnfixyIaDGYbVzcjWBNGU3ai/26bvkaCkjynnc2ecYcsuA==",
166
+ "linuxmusl-arm64v8": "sha512-Qh5Wi+bkKTohFYHzSPssfjMhIkD6z6EHbVmnwmWSsgY9zsUBStFp6+mKcNTQfP5YM5Mz06vJOkLHX2OzEr5TzA==",
167
+ "linuxmusl-x64": "sha512-DwB4Fs3+ISw9etaLCANkueZDdk758iOS+wNp4TKZkHdq0al6B/3Pk7OHLR8a9E3H6wYDD328u++dcJzip5tacA==",
168
+ "win32-arm64v8": "sha512-96r3W+O4BtX602B1MtxU5Ru4lKzRRTZqM4OQEBJ//TNL3fiCZdd9agD+RQBjaeR4KFIyBSt3F7IE425ZWmxz+w==",
169
+ "win32-ia32": "sha512-qfN1MsfQGek1QQd1UNW7JT+5K5Ne1suFQ2GpgpYm3JLSpIve/tz2vOGEGzvTVssOBADJvAkTDFt+yIi3PgU9pA==",
170
+ "win32-x64": "sha512-eb3aAmjbVVBVRbiYgebQwoxkAt69WI8nwmKlilSQ3kWqoc0pXfIe322rF2UR8ebbISCGvYRUfzD2r1k92RXISQ=="
172
171
  },
173
172
  "runtime": "napi",
174
173
  "target": 7
package/src/common.cc CHANGED
@@ -507,9 +507,10 @@ namespace sharp {
507
507
  }
508
508
  }
509
509
  }
510
+
510
511
  // Limit input images to a given number of pixels, where pixels = width * height
511
512
  if (descriptor->limitInputPixels > 0 &&
512
- static_cast<uint64_t>(image.width() * image.height()) > descriptor->limitInputPixels) {
513
+ static_cast<uint64_t>(image.width()) * image.height() > descriptor->limitInputPixels) {
513
514
  throw vips::VError("Input image exceeds pixel limit");
514
515
  }
515
516
  return std::make_tuple(image, imageType);
@@ -556,6 +557,7 @@ namespace sharp {
556
557
  VImage RemoveExifOrientation(VImage image) {
557
558
  VImage copy = image.copy();
558
559
  copy.remove(VIPS_META_ORIENTATION);
560
+ copy.remove("exif-ifd0-Orientation");
559
561
  return copy;
560
562
  }
561
563
 
package/src/common.h CHANGED
@@ -26,8 +26,8 @@
26
26
 
27
27
  #if (VIPS_MAJOR_VERSION < 8) || \
28
28
  (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 13) || \
29
- (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 13 && VIPS_MICRO_VERSION < 1)
30
- #error "libvips version 8.13.1+ is required - please see https://sharp.pixelplumbing.com/install"
29
+ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 13 && VIPS_MICRO_VERSION < 3)
30
+ #error "libvips version 8.13.3+ is required - please see https://sharp.pixelplumbing.com/install"
31
31
  #endif
32
32
 
33
33
  #if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
package/src/operations.cc CHANGED
@@ -289,17 +289,20 @@ namespace sharp {
289
289
  background = image.extract_area(0, 0, 1, 1)(0, 0);
290
290
  multiplier = 1.0;
291
291
  }
292
- if (background.size() == 4) {
292
+ if (HasAlpha(image) && background.size() == 4) {
293
293
  // Just discard the alpha because flattening the background colour with
294
294
  // itself (effectively what find_trim() does) gives the same result
295
295
  backgroundAlpha[0] = background[3] * multiplier;
296
296
  }
297
- background = {
298
- background[0] * multiplier,
299
- background[1] * multiplier,
300
- background[2] * multiplier
301
- };
302
-
297
+ if (image.bands() > 2) {
298
+ background = {
299
+ background[0] * multiplier,
300
+ background[1] * multiplier,
301
+ background[2] * multiplier
302
+ };
303
+ } else {
304
+ background[0] = background[0] * multiplier;
305
+ }
303
306
  int left, top, width, height;
304
307
  left = image.find_trim(&top, &width, &height, VImage::option()
305
308
  ->set("background", background)
package/src/pipeline.cc CHANGED
@@ -81,32 +81,47 @@ class PipelineWorker : public Napi::AsyncWorker {
81
81
  int pageHeight = sharp::GetPageHeight(image);
82
82
 
83
83
  // Calculate angle of rotation
84
- VipsAngle rotation;
85
- bool flip = FALSE;
86
- bool flop = FALSE;
84
+ VipsAngle rotation = VIPS_ANGLE_D0;
85
+ VipsAngle autoRotation = VIPS_ANGLE_D0;
86
+ bool autoFlip = FALSE;
87
+ bool autoFlop = FALSE;
87
88
  if (baton->useExifOrientation) {
88
89
  // Rotate and flip image according to Exif orientation
89
- std::tie(rotation, flip, flop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
90
+ std::tie(autoRotation, autoFlip, autoFlop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
91
+ image = sharp::RemoveExifOrientation(image);
90
92
  } else {
91
93
  rotation = CalculateAngleRotation(baton->angle);
92
94
  }
93
95
 
94
96
  // Rotate pre-extract
95
- if (baton->rotateBeforePreExtract) {
96
- if (rotation != VIPS_ANGLE_D0) {
97
- image = image.rot(rotation);
97
+ bool const shouldRotateBefore = baton->rotateBeforePreExtract &&
98
+ (rotation != VIPS_ANGLE_D0 || autoRotation != VIPS_ANGLE_D0 ||
99
+ autoFlip || baton->flip || autoFlop || baton->flop ||
100
+ baton->rotationAngle != 0.0);
101
+
102
+ if (shouldRotateBefore) {
103
+ if (autoRotation != VIPS_ANGLE_D0) {
104
+ image = image.rot(autoRotation);
105
+ autoRotation = VIPS_ANGLE_D0;
98
106
  }
99
- if (flip) {
107
+ if (autoFlip) {
100
108
  image = image.flip(VIPS_DIRECTION_VERTICAL);
109
+ autoFlip = FALSE;
110
+ } else if (baton->flip) {
111
+ image = image.flip(VIPS_DIRECTION_VERTICAL);
112
+ baton->flip = FALSE;
101
113
  }
102
- if (flop) {
114
+ if (autoFlop) {
115
+ image = image.flip(VIPS_DIRECTION_HORIZONTAL);
116
+ autoFlop = FALSE;
117
+ } else if (baton->flop) {
103
118
  image = image.flip(VIPS_DIRECTION_HORIZONTAL);
119
+ baton->flop = FALSE;
104
120
  }
105
- if (rotation != VIPS_ANGLE_D0 || flip || flop) {
106
- image = sharp::RemoveExifOrientation(image);
121
+ if (rotation != VIPS_ANGLE_D0) {
122
+ image = image.rot(rotation);
123
+ rotation = VIPS_ANGLE_D0;
107
124
  }
108
- flop = FALSE;
109
- flip = FALSE;
110
125
  if (baton->rotationAngle != 0.0) {
111
126
  MultiPageUnsupported(nPages, "Rotate");
112
127
  std::vector<double> background;
@@ -147,7 +162,9 @@ class PipelineWorker : public Napi::AsyncWorker {
147
162
  int targetResizeHeight = baton->height;
148
163
 
149
164
  // Swap input output width and height when rotating by 90 or 270 degrees
150
- bool swap = !baton->rotateBeforePreExtract && (rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270);
165
+ bool swap = !baton->rotateBeforePreExtract &&
166
+ (rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270 ||
167
+ autoRotation == VIPS_ANGLE_D90 || autoRotation == VIPS_ANGLE_D270);
151
168
 
152
169
  // Shrink to pageHeight, so we work for multi-page images
153
170
  std::tie(hshrink, vshrink) = sharp::ResolveShrink(
@@ -167,7 +184,7 @@ class PipelineWorker : public Napi::AsyncWorker {
167
184
  // - input colourspace is not specified;
168
185
  bool const shouldPreShrink = (targetResizeWidth > 0 || targetResizeHeight > 0) &&
169
186
  baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold == 0.0 &&
170
- baton->colourspaceInput == VIPS_INTERPRETATION_LAST;
187
+ baton->colourspaceInput == VIPS_INTERPRETATION_LAST && !shouldRotateBefore;
171
188
 
172
189
  if (shouldPreShrink) {
173
190
  // The common part of the shrink: the bit by which both axes must be shrunk
@@ -364,30 +381,21 @@ class PipelineWorker : public Napi::AsyncWorker {
364
381
  ->set("kernel", baton->kernel));
365
382
  }
366
383
 
384
+ // Auto-rotate post-extract
385
+ if (autoRotation != VIPS_ANGLE_D0) {
386
+ image = image.rot(autoRotation);
387
+ }
367
388
  // Flip (mirror about Y axis)
368
- if (baton->flip || flip) {
389
+ if (baton->flip || autoFlip) {
369
390
  image = image.flip(VIPS_DIRECTION_VERTICAL);
370
- image = sharp::RemoveExifOrientation(image);
371
391
  }
372
-
373
392
  // Flop (mirror about X axis)
374
- if (baton->flop || flop) {
393
+ if (baton->flop || autoFlop) {
375
394
  image = image.flip(VIPS_DIRECTION_HORIZONTAL);
376
- image = sharp::RemoveExifOrientation(image);
377
395
  }
378
-
379
396
  // Rotate post-extract 90-angle
380
- if (!baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) {
397
+ if (rotation != VIPS_ANGLE_D0) {
381
398
  image = image.rot(rotation);
382
- if (flip) {
383
- image = image.flip(VIPS_DIRECTION_VERTICAL);
384
- flip = FALSE;
385
- }
386
- if (flop) {
387
- image = image.flip(VIPS_DIRECTION_HORIZONTAL);
388
- flop = FALSE;
389
- }
390
- image = sharp::RemoveExifOrientation(image);
391
399
  }
392
400
 
393
401
  // Join additional color channels to the image
@@ -899,12 +907,13 @@ class PipelineWorker : public Napi::AsyncWorker {
899
907
  } else if (baton->formatOut == "heif" ||
900
908
  (baton->formatOut == "input" && inputImageType == sharp::ImageType::HEIF)) {
901
909
  // Write HEIF to buffer
902
- image = sharp::RemoveAnimationProperties(image);
910
+ image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
903
911
  VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
904
912
  ->set("strip", !baton->withMetadata)
905
913
  ->set("Q", baton->heifQuality)
906
914
  ->set("compression", baton->heifCompression)
907
915
  ->set("effort", baton->heifEffort)
916
+ ->set("bitdepth", 8)
908
917
  ->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
909
918
  ? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
910
919
  ->set("lossless", baton->heifLossless)));
@@ -1070,12 +1079,13 @@ class PipelineWorker : public Napi::AsyncWorker {
1070
1079
  } else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
1071
1080
  (willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
1072
1081
  // Write HEIF to file
1073
- image = sharp::RemoveAnimationProperties(image);
1082
+ image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
1074
1083
  image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
1075
1084
  ->set("strip", !baton->withMetadata)
1076
1085
  ->set("Q", baton->heifQuality)
1077
1086
  ->set("compression", baton->heifCompression)
1078
1087
  ->set("effort", baton->heifEffort)
1088
+ ->set("bitdepth", 8)
1079
1089
  ->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
1080
1090
  ? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
1081
1091
  ->set("lossless", baton->heifLossless));