sharp 0.28.0 → 0.28.1

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.
@@ -145,7 +145,9 @@ try {
145
145
  tmpFileStream
146
146
  .on('error', function (err) {
147
147
  // Clean up temporary file
148
- fs.unlinkSync(tarPathTemp);
148
+ try {
149
+ fs.unlinkSync(tarPathTemp);
150
+ } catch (e) {}
149
151
  fail(err);
150
152
  })
151
153
  .on('close', function () {
@@ -157,7 +159,7 @@ try {
157
159
  fs.copyFileSync(tarPathTemp, tarPathCache);
158
160
  fs.unlinkSync(tarPathTemp);
159
161
  }
160
- extractTarball(tarPathCache);
162
+ extractTarball(tarPathCache, platformAndArch);
161
163
  });
162
164
  }
163
165
  });
package/lib/channel.js CHANGED
@@ -30,21 +30,39 @@ function removeAlpha () {
30
30
  }
31
31
 
32
32
  /**
33
- * Ensure alpha channel, if missing. The added alpha channel will be fully opaque. This is a no-op if the image already has an alpha channel.
33
+ * Ensure the output image has an alpha transparency channel.
34
+ * If missing, the added alpha channel will have the specified
35
+ * transparency level, defaulting to fully-opaque (1).
36
+ * This is a no-op if the image already has an alpha channel.
34
37
  *
35
38
  * @since 0.21.2
36
39
  *
37
40
  * @example
38
- * sharp('rgb.jpg')
41
+ * // rgba.png will be a 4 channel image with a fully-opaque alpha channel
42
+ * await sharp('rgb.jpg')
39
43
  * .ensureAlpha()
40
- * .toFile('rgba.png', function(err, info) {
41
- * // rgba.png is a 4 channel image with a fully opaque alpha channel
42
- * });
44
+ * .toFile('rgba.png')
45
+ *
46
+ * @example
47
+ * // rgba is a 4 channel image with a fully-transparent alpha channel
48
+ * const rgba = await sharp(rgb)
49
+ * .ensureAlpha(0)
50
+ * .toBuffer();
43
51
  *
52
+ * @param {number} [alpha=1] - alpha transparency level (0=fully-transparent, 1=fully-opaque)
44
53
  * @returns {Sharp}
54
+ * @throws {Error} Invalid alpha transparency level
45
55
  */
46
- function ensureAlpha () {
47
- this.options.ensureAlpha = true;
56
+ function ensureAlpha (alpha) {
57
+ if (is.defined(alpha)) {
58
+ if (is.number(alpha) && is.inRange(alpha, 0, 1)) {
59
+ this.options.ensureAlpha = alpha;
60
+ } else {
61
+ throw is.invalidParameterError('alpha', 'number between 0 and 1', alpha);
62
+ }
63
+ } else {
64
+ this.options.ensureAlpha = 1;
65
+ }
48
66
  return this;
49
67
  }
50
68
 
@@ -132,6 +132,7 @@ const debuglog = util.debuglog('sharp');
132
132
  * @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
133
133
  * @param {number} [options.pages=1] - number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages.
134
134
  * @param {number} [options.page=0] - page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based.
135
+ * @param {number} [options.subifd=-1] - subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image.
135
136
  * @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
136
137
  * @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`).
137
138
  * @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
@@ -221,7 +222,7 @@ const Sharp = function (input, options) {
221
222
  joinChannelIn: [],
222
223
  extractChannel: -1,
223
224
  removeAlpha: false,
224
- ensureAlpha: false,
225
+ ensureAlpha: -1,
225
226
  colourspace: 'srgb',
226
227
  composite: [],
227
228
  // output
@@ -231,6 +232,7 @@ const Sharp = function (input, options) {
231
232
  withMetadata: false,
232
233
  withMetadataOrientation: -1,
233
234
  withMetadataIcc: '',
235
+ withMetadataStrs: {},
234
236
  resolveWithObject: false,
235
237
  // output format
236
238
  jpegQuality: 80,
package/lib/input.js CHANGED
@@ -9,9 +9,9 @@ const sharp = require('../build/Release/sharp.node');
9
9
  * @private
10
10
  */
11
11
  function _inputOptionsFromObject (obj) {
12
- const { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages } = obj;
13
- return [raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages].some(is.defined)
14
- ? { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages }
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 }
15
15
  : undefined;
16
16
  }
17
17
 
@@ -131,6 +131,14 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
131
131
  throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level);
132
132
  }
133
133
  }
134
+ // Sub Image File Directory (TIFF)
135
+ if (is.defined(inputOptions.subifd)) {
136
+ if (is.integer(inputOptions.subifd) && is.inRange(inputOptions.subifd, -1, 100000)) {
137
+ inputDescriptor.subifd = inputOptions.subifd;
138
+ } else {
139
+ throw is.invalidParameterError('subifd', 'integer between -1 and 100000', inputOptions.subifd);
140
+ }
141
+ }
134
142
  // Create new image
135
143
  if (is.defined(inputOptions.create)) {
136
144
  if (
@@ -255,6 +263,7 @@ function _isStreamInput () {
255
263
  * - `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
256
264
  * - `pagePrimary`: Number of the primary page in a HEIF image
257
265
  * - `levels`: Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide
266
+ * - `subifds`: Number of Sub Image File Directories in an OME-TIFF image
258
267
  * - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
259
268
  * - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
260
269
  * - `orientation`: Number value of the EXIF Orientation header, if present
package/lib/libvips.js CHANGED
@@ -39,7 +39,7 @@ const cachePath = function () {
39
39
 
40
40
  const log = function (item) {
41
41
  if (item instanceof Error) {
42
- console.error(`sharp: ${item.message}`);
42
+ console.error(`sharp: Installation error: ${item.message}`);
43
43
  } else {
44
44
  console.log(`sharp: ${item}`);
45
45
  }
package/lib/output.js CHANGED
@@ -148,9 +148,22 @@ function toBuffer (options, callback) {
148
148
  * .toFile('output-with-metadata.jpg')
149
149
  * .then(info => { ... });
150
150
  *
151
+ * @example
152
+ * // Set "IFD0-Copyright" in output EXIF metadata
153
+ * await sharp(input)
154
+ * .withMetadata({
155
+ * exif: {
156
+ * IFD0: {
157
+ * Copyright: 'Wernham Hogg'
158
+ * }
159
+ * }
160
+ * })
161
+ * .toBuffer();
162
+ *
151
163
  * @param {Object} [options]
152
164
  * @param {number} [options.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
153
165
  * @param {string} [options.icc] filesystem path to output ICC profile, defaults to sRGB.
166
+ * @param {Object<Object>} [options.exif={}] Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
154
167
  * @returns {Sharp}
155
168
  * @throws {Error} Invalid parameters
156
169
  */
@@ -171,6 +184,25 @@ function withMetadata (options) {
171
184
  throw is.invalidParameterError('icc', 'string filesystem path to ICC profile', options.icc);
172
185
  }
173
186
  }
187
+ if (is.defined(options.exif)) {
188
+ if (is.object(options.exif)) {
189
+ for (const [ifd, entries] of Object.entries(options.exif)) {
190
+ if (is.object(entries)) {
191
+ for (const [k, v] of Object.entries(entries)) {
192
+ if (is.string(v)) {
193
+ this.options.withMetadataStrs[`exif-${ifd.toLowerCase()}-${k}`] = v;
194
+ } else {
195
+ throw is.invalidParameterError(`exif.${ifd}.${k}`, 'string', v);
196
+ }
197
+ }
198
+ } else {
199
+ throw is.invalidParameterError(`exif.${ifd}`, 'object', entries);
200
+ }
201
+ }
202
+ } else {
203
+ throw is.invalidParameterError('exif', 'object', options.exif);
204
+ }
205
+ }
174
206
  }
175
207
  return this;
176
208
  }
@@ -383,6 +415,12 @@ function png (options) {
383
415
  * .webp({ lossless: true })
384
416
  * .toBuffer();
385
417
  *
418
+ * @example
419
+ * // Optimise the file size of an animated WebP
420
+ * const outputWebp = await sharp(inputWebp, { animated: true })
421
+ * .webp({ reductionEffort: 6 })
422
+ * .toBuffer();
423
+ *
386
424
  * @param {Object} [options] - output options
387
425
  * @param {number} [options.quality=80] - quality, integer 1-100
388
426
  * @param {number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100
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, AVIF and TIFF images",
4
- "version": "0.28.0",
4
+ "version": "0.28.1",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://github.com/lovell/sharp",
7
7
  "contributors": [
@@ -120,7 +120,7 @@
120
120
  "color": "^3.1.3",
121
121
  "detect-libc": "^1.0.3",
122
122
  "node-addon-api": "^3.1.0",
123
- "prebuild-install": "^6.0.1",
123
+ "prebuild-install": "^6.1.1",
124
124
  "semver": "^7.3.5",
125
125
  "simple-get": "^3.1.0",
126
126
  "tar-fs": "^2.1.1",
package/src/common.cc CHANGED
@@ -36,6 +36,9 @@ namespace sharp {
36
36
  std::string AttrAsStr(Napi::Object obj, std::string attr) {
37
37
  return obj.Get(attr).As<Napi::String>();
38
38
  }
39
+ std::string AttrAsStr(Napi::Object obj, unsigned int const attr) {
40
+ return obj.Get(attr).As<Napi::String>();
41
+ }
39
42
  uint32_t AttrAsUint32(Napi::Object obj, std::string attr) {
40
43
  return obj.Get(attr).As<Napi::Number>().Uint32Value();
41
44
  }
@@ -104,6 +107,10 @@ namespace sharp {
104
107
  if (HasAttr(input, "level")) {
105
108
  descriptor->level = AttrAsUint32(input, "level");
106
109
  }
110
+ // subIFD (OME-TIFF)
111
+ if (HasAttr(input, "subifd")) {
112
+ descriptor->subifd = AttrAsInt32(input, "subifd");
113
+ }
107
114
  // Create new image
108
115
  if (HasAttr(input, "createChannels")) {
109
116
  descriptor->createChannels = AttrAsUint32(input, "createChannels");
@@ -319,6 +326,9 @@ namespace sharp {
319
326
  if (imageType == ImageType::OPENSLIDE) {
320
327
  option->set("level", descriptor->level);
321
328
  }
329
+ if (imageType == ImageType::TIFF) {
330
+ option->set("subifd", descriptor->subifd);
331
+ }
322
332
  image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
323
333
  if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
324
334
  image = SetDensity(image, descriptor->density);
@@ -391,6 +401,9 @@ namespace sharp {
391
401
  if (imageType == ImageType::OPENSLIDE) {
392
402
  option->set("level", descriptor->level);
393
403
  }
404
+ if (imageType == ImageType::TIFF) {
405
+ option->set("subifd", descriptor->subifd);
406
+ }
394
407
  image = VImage::new_from_file(descriptor->file.data(), option);
395
408
  if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
396
409
  image = SetDensity(image, descriptor->density);
@@ -799,10 +812,10 @@ namespace sharp {
799
812
  /*
800
813
  Ensures alpha channel, if missing.
801
814
  */
802
- VImage EnsureAlpha(VImage image) {
815
+ VImage EnsureAlpha(VImage image, double const value) {
803
816
  if (!HasAlpha(image)) {
804
817
  std::vector<double> alpha;
805
- alpha.push_back(sharp::MaximumImageAlpha(image.interpretation()));
818
+ alpha.push_back(value * sharp::MaximumImageAlpha(image.interpretation()));
806
819
  image = image.bandjoin_const(alpha);
807
820
  }
808
821
  return image;
package/src/common.h CHANGED
@@ -60,6 +60,7 @@ namespace sharp {
60
60
  int pages;
61
61
  int page;
62
62
  int level;
63
+ int subifd;
63
64
  int createChannels;
64
65
  int createWidth;
65
66
  int createHeight;
@@ -82,6 +83,7 @@ namespace sharp {
82
83
  pages(1),
83
84
  page(0),
84
85
  level(0),
86
+ subifd(-1),
85
87
  createChannels(0),
86
88
  createWidth(0),
87
89
  createHeight(0),
@@ -93,6 +95,7 @@ namespace sharp {
93
95
  // Convenience methods to access the attributes of a Napi::Object
94
96
  bool HasAttr(Napi::Object obj, std::string attr);
95
97
  std::string AttrAsStr(Napi::Object obj, std::string attr);
98
+ std::string AttrAsStr(Napi::Object obj, unsigned int const attr);
96
99
  uint32_t AttrAsUint32(Napi::Object obj, std::string attr);
97
100
  int32_t AttrAsInt32(Napi::Object obj, std::string attr);
98
101
  int32_t AttrAsInt32(Napi::Object obj, unsigned int const attr);
@@ -296,7 +299,7 @@ namespace sharp {
296
299
  /*
297
300
  Ensures alpha channel, if missing.
298
301
  */
299
- VImage EnsureAlpha(VImage image);
302
+ VImage EnsureAlpha(VImage image, double const value);
300
303
 
301
304
  } // namespace sharp
302
305
 
package/src/metadata.cc CHANGED
@@ -86,6 +86,9 @@ class MetadataWorker : public Napi::AsyncWorker {
86
86
  baton->levels.push_back(std::pair<int, int>(width, height));
87
87
  }
88
88
  }
89
+ if (image.get_typeof(VIPS_META_N_SUBIFDS) == G_TYPE_INT) {
90
+ baton->subifds = image.get_int(VIPS_META_N_SUBIFDS);
91
+ }
89
92
  baton->hasProfile = sharp::HasProfile(image);
90
93
  // Derived attributes
91
94
  baton->hasAlpha = sharp::HasAlpha(image);
@@ -203,6 +206,9 @@ class MetadataWorker : public Napi::AsyncWorker {
203
206
  }
204
207
  info.Set("levels", levels);
205
208
  }
209
+ if (baton->subifds > 0) {
210
+ info.Set("subifds", baton->subifds);
211
+ }
206
212
  info.Set("hasProfile", baton->hasProfile);
207
213
  info.Set("hasAlpha", baton->hasAlpha);
208
214
  if (baton->orientation > 0) {
package/src/metadata.h CHANGED
@@ -41,6 +41,7 @@ struct MetadataBaton {
41
41
  int pagePrimary;
42
42
  std::string compression;
43
43
  std::vector<std::pair<int, int>> levels;
44
+ int subifds;
44
45
  bool hasProfile;
45
46
  bool hasAlpha;
46
47
  int orientation;
@@ -68,6 +69,7 @@ struct MetadataBaton {
68
69
  pageHeight(0),
69
70
  loop(-1),
70
71
  pagePrimary(-1),
72
+ subifds(0),
71
73
  hasProfile(false),
72
74
  hasAlpha(false),
73
75
  orientation(0),
package/src/pipeline.cc CHANGED
@@ -346,7 +346,7 @@ class PipelineWorker : public Napi::AsyncWorker {
346
346
  bool const shouldModulate = baton->brightness != 1.0 || baton->saturation != 1.0 || baton->hue != 0.0;
347
347
 
348
348
  if (shouldComposite && !sharp::HasAlpha(image)) {
349
- image = sharp::EnsureAlpha(image);
349
+ image = sharp::EnsureAlpha(image, 1);
350
350
  }
351
351
 
352
352
  bool const shouldPremultiplyAlpha = sharp::HasAlpha(image) &&
@@ -594,7 +594,7 @@ class PipelineWorker : public Napi::AsyncWorker {
594
594
  // Ensure image to composite is sRGB with premultiplied alpha
595
595
  compositeImage = compositeImage.colourspace(VIPS_INTERPRETATION_sRGB);
596
596
  if (!sharp::HasAlpha(compositeImage)) {
597
- compositeImage = sharp::EnsureAlpha(compositeImage);
597
+ compositeImage = sharp::EnsureAlpha(compositeImage, 1);
598
598
  }
599
599
  if (!composite->premultiplied) compositeImage = compositeImage.premultiply();
600
600
  // Calculate position
@@ -691,8 +691,8 @@ class PipelineWorker : public Napi::AsyncWorker {
691
691
  }
692
692
 
693
693
  // Ensure alpha channel, if missing
694
- if (baton->ensureAlpha) {
695
- image = sharp::EnsureAlpha(image);
694
+ if (baton->ensureAlpha != -1) {
695
+ image = sharp::EnsureAlpha(image, baton->ensureAlpha);
696
696
  }
697
697
 
698
698
  // Convert image to sRGB, if not already
@@ -717,11 +717,17 @@ class PipelineWorker : public Napi::AsyncWorker {
717
717
  ->set("input_profile", "srgb")
718
718
  ->set("intent", VIPS_INTENT_PERCEPTUAL));
719
719
  }
720
-
721
720
  // Override EXIF Orientation tag
722
721
  if (baton->withMetadata && baton->withMetadataOrientation != -1) {
723
722
  image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
724
723
  }
724
+ // Metadata key/value pairs, e.g. EXIF
725
+ if (!baton->withMetadataStrs.empty()) {
726
+ image = image.copy();
727
+ for (const auto& s : baton->withMetadataStrs) {
728
+ image.set(s.first.data(), s.second.data());
729
+ }
730
+ }
725
731
 
726
732
  // Number of channels used in output image
727
733
  baton->channels = image.bands();
@@ -1341,7 +1347,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1341
1347
  baton->affineInterpolator = vips::VInterpolate::new_from_name(sharp::AttrAsStr(options, "affineInterpolator").data());
1342
1348
 
1343
1349
  baton->removeAlpha = sharp::AttrAsBool(options, "removeAlpha");
1344
- baton->ensureAlpha = sharp::AttrAsBool(options, "ensureAlpha");
1350
+ baton->ensureAlpha = sharp::AttrAsDouble(options, "ensureAlpha");
1345
1351
  if (options.Has("boolean")) {
1346
1352
  baton->boolean = sharp::CreateInputDescriptor(options.Get("boolean").As<Napi::Object>());
1347
1353
  baton->booleanOp = sharp::GetBooleanOperation(sharp::AttrAsStr(options, "booleanOp"));
@@ -1379,6 +1385,12 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1379
1385
  baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
1380
1386
  baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
1381
1387
  baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
1388
+ Napi::Object mdStrs = options.Get("withMetadataStrs").As<Napi::Object>();
1389
+ Napi::Array mdStrKeys = mdStrs.GetPropertyNames();
1390
+ for (unsigned int i = 0; i < mdStrKeys.Length(); i++) {
1391
+ std::string k = sharp::AttrAsStr(mdStrKeys, i);
1392
+ baton->withMetadataStrs.insert(std::make_pair(k, sharp::AttrAsStr(mdStrs, k)));
1393
+ }
1382
1394
  // Format-specific
1383
1395
  baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
1384
1396
  baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
package/src/pipeline.h CHANGED
@@ -18,6 +18,7 @@
18
18
  #include <memory>
19
19
  #include <string>
20
20
  #include <vector>
21
+ #include <unordered_map>
21
22
 
22
23
  #include <napi.h>
23
24
  #include <vips/vips8>
@@ -168,6 +169,7 @@ struct PipelineBaton {
168
169
  bool withMetadata;
169
170
  int withMetadataOrientation;
170
171
  std::string withMetadataIcc;
172
+ std::unordered_map<std::string, std::string> withMetadataStrs;
171
173
  std::unique_ptr<double[]> convKernel;
172
174
  int convKernelWidth;
173
175
  int convKernelHeight;
@@ -178,7 +180,7 @@ struct PipelineBaton {
178
180
  VipsOperationBoolean bandBoolOp;
179
181
  int extractChannel;
180
182
  bool removeAlpha;
181
- bool ensureAlpha;
183
+ double ensureAlpha;
182
184
  VipsInterpretation colourspace;
183
185
  int pageHeight;
184
186
  std::vector<int> delay;
@@ -297,7 +299,7 @@ struct PipelineBaton {
297
299
  bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
298
300
  extractChannel(-1),
299
301
  removeAlpha(false),
300
- ensureAlpha(false),
302
+ ensureAlpha(-1.0),
301
303
  colourspace(VIPS_INTERPRETATION_LAST),
302
304
  pageHeight(0),
303
305
  delay{-1},