sharp 0.23.0 → 0.23.4

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.
@@ -19,6 +19,9 @@ const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SH
19
19
 
20
20
  const fail = function (err) {
21
21
  npmLog.error('sharp', err.message);
22
+ if (err.code === 'EACCES') {
23
+ npmLog.info('sharp', 'Are you trying to install as a root or sudo user? Try again with the --unsafe-perm flag');
24
+ }
22
25
  npmLog.info('sharp', 'Attempting to build from source via node-gyp but this may fail due to the above error');
23
26
  npmLog.info('sharp', 'Please see https://sharp.pixelplumbing.com/page/install for required dependencies');
24
27
  process.exit(1);
@@ -79,14 +82,16 @@ try {
79
82
  npmLog.info('sharp', `Downloading ${url}`);
80
83
  simpleGet({ url: url, agent: agent() }, function (err, response) {
81
84
  if (err) {
82
- throw err;
83
- }
84
- if (response.statusCode !== 200) {
85
- throw new Error(`Status ${response.statusCode}`);
85
+ fail(err);
86
+ } else if (response.statusCode === 404) {
87
+ fail(new Error(`Prebuilt libvips binaries are not yet available for ${platformAndArch}`));
88
+ } else if (response.statusCode !== 200) {
89
+ fail(new Error(`Status ${response.statusCode} ${response.statusMessage}`));
90
+ } else {
91
+ response
92
+ .on('error', fail)
93
+ .pipe(tmpFile);
86
94
  }
87
- response
88
- .on('error', fail)
89
- .pipe(tmpFile);
90
95
  });
91
96
  tmpFile
92
97
  .on('error', fail)
package/lib/agent.js CHANGED
@@ -20,15 +20,18 @@ function env (key) {
20
20
 
21
21
  module.exports = function () {
22
22
  try {
23
- const proxy = url.parse(proxies.map(env).find(is.string));
23
+ const proxy = new url.URL(proxies.map(env).find(is.string));
24
24
  const tunnel = proxy.protocol === 'https:'
25
25
  ? tunnelAgent.httpsOverHttps
26
26
  : tunnelAgent.httpsOverHttp;
27
+ const proxyAuth = proxy.username && proxy.password
28
+ ? `${proxy.username}:${proxy.password}`
29
+ : null;
27
30
  return tunnel({
28
31
  proxy: {
29
32
  port: Number(proxy.port),
30
33
  host: proxy.hostname,
31
- proxyAuth: proxy.auth
34
+ proxyAuth
32
35
  }
33
36
  });
34
37
  } catch (err) {
package/lib/colour.js CHANGED
@@ -83,18 +83,22 @@ function toColorspace (colorspace) {
83
83
  * Update a colour attribute of the this.options Object.
84
84
  * @private
85
85
  * @param {String} key
86
- * @param {String|Object} val
87
- * @throws {Error} Invalid key
86
+ * @param {String|Object} value
87
+ * @throws {Error} Invalid value
88
88
  */
89
- function _setColourOption (key, val) {
90
- if (is.object(val) || is.string(val)) {
91
- const colour = color(val);
92
- this.options[key] = [
93
- colour.red(),
94
- colour.green(),
95
- colour.blue(),
96
- Math.round(colour.alpha() * 255)
97
- ];
89
+ function _setBackgroundColourOption (key, value) {
90
+ if (is.defined(value)) {
91
+ if (is.object(value) || is.string(value)) {
92
+ const colour = color(value);
93
+ this.options[key] = [
94
+ colour.red(),
95
+ colour.green(),
96
+ colour.blue(),
97
+ Math.round(colour.alpha() * 255)
98
+ ];
99
+ } else {
100
+ throw is.invalidParameterError('background', 'object or string', value);
101
+ }
98
102
  }
99
103
  }
100
104
 
@@ -111,7 +115,7 @@ module.exports = function (Sharp) {
111
115
  toColourspace,
112
116
  toColorspace,
113
117
  // Private
114
- _setColourOption
118
+ _setBackgroundColourOption
115
119
  });
116
120
  // Class attributes
117
121
  Sharp.colourspace = colourspace;
package/lib/composite.js CHANGED
@@ -81,6 +81,7 @@ const blend = {
81
81
  * @param {Number} [images[].top] - the pixel offset from the top edge.
82
82
  * @param {Number} [images[].left] - the pixel offset from the left edge.
83
83
  * @param {Boolean} [images[].tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
84
+ * @param {Boolean} [images[].premultiplied=false] - set to true to avoid premultipling the image below. Equivalent to the `--premultiplied` vips option.
84
85
  * @param {Number} [images[].density=72] - number representing the DPI for vector overlay image.
85
86
  * @param {Object} [images[].raw] - describes overlay when using raw pixel data.
86
87
  * @param {Number} [images[].raw.width]
@@ -105,7 +106,8 @@ function composite (images) {
105
106
  tile: false,
106
107
  left: -1,
107
108
  top: -1,
108
- gravity: 0
109
+ gravity: 0,
110
+ premultiplied: false
109
111
  };
110
112
  if (is.defined(image.blend)) {
111
113
  if (is.string(blend[image.blend])) {
@@ -147,6 +149,14 @@ function composite (images) {
147
149
  throw is.invalidParameterError('gravity', 'valid gravity', image.gravity);
148
150
  }
149
151
  }
152
+ if (is.defined(image.premultiplied)) {
153
+ if (is.bool(image.premultiplied)) {
154
+ composite.premultiplied = image.premultiplied;
155
+ } else {
156
+ throw is.invalidParameterError('premultiplied', 'boolean', image.premultiplied);
157
+ }
158
+ }
159
+
150
160
  return composite;
151
161
  });
152
162
  return this;
@@ -78,7 +78,7 @@ const debuglog = util.debuglog('sharp');
78
78
  *
79
79
  * @param {(Buffer|String)} [input] - if present, can be
80
80
  * a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
81
- * a String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
81
+ * a String containing the filesystem path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
82
82
  * JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
83
83
  * @param {Object} [options] - if present, is an Object with optional attributes.
84
84
  * @param {Boolean} [options.failOnError=true] - by default halt processing and raise an error when loading invalid images.
@@ -213,6 +213,7 @@ const Sharp = function (input, options) {
213
213
  tileSize: 256,
214
214
  tileOverlap: 0,
215
215
  tileSkipBlanks: -1,
216
+ tileBackground: [255, 255, 255, 255],
216
217
  linearA: 1,
217
218
  linearB: 0,
218
219
  // Function to notify of libvips warnings
package/lib/input.js CHANGED
@@ -65,11 +65,15 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
65
65
  if (is.defined(inputOptions.pages)) {
66
66
  if (is.integer(inputOptions.pages) && is.inRange(inputOptions.pages, -1, 100000)) {
67
67
  inputDescriptor.pages = inputOptions.pages;
68
+ } else {
69
+ throw is.invalidParameterError('pages', 'integer between -1 and 100000', inputOptions.pages);
68
70
  }
69
71
  }
70
72
  if (is.defined(inputOptions.page)) {
71
73
  if (is.integer(inputOptions.page) && is.inRange(inputOptions.page, 0, 100000)) {
72
74
  inputDescriptor.page = inputOptions.page;
75
+ } else {
76
+ throw is.invalidParameterError('page', 'integer between 0 and 100000', inputOptions.page);
73
77
  }
74
78
  }
75
79
  // Create new image
@@ -203,6 +207,7 @@ function clone () {
203
207
  * - `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
204
208
  * - `iptc`: Buffer containing raw IPTC data, if present
205
209
  * - `xmp`: Buffer containing raw XMP data, if present
210
+ * - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
206
211
  *
207
212
  * @example
208
213
  * const image = sharp(inputJpg);
package/lib/operation.js CHANGED
@@ -179,7 +179,7 @@ function blur (sigma) {
179
179
  function flatten (options) {
180
180
  this.options.flatten = is.bool(options) ? options : true;
181
181
  if (is.object(options)) {
182
- this._setColourOption('flattenBackground', options.background);
182
+ this._setBackgroundColourOption('flattenBackground', options.background);
183
183
  }
184
184
  return this;
185
185
  }
package/lib/output.js CHANGED
@@ -10,6 +10,9 @@ const sharp = require('../build/Release/sharp.node');
10
10
  * with JPEG, PNG, WebP, TIFF, DZI, and libvips' V format supported.
11
11
  * Note that raw pixel data is only supported for buffer output.
12
12
  *
13
+ * By default all metadata will be removed, which includes EXIF-based orientation.
14
+ * See {@link withMetadata} for control over this.
15
+ *
13
16
  * A `Promise` is returned when `callback` is not provided.
14
17
  *
15
18
  * @example
@@ -57,7 +60,11 @@ function toFile (fileOut, callback) {
57
60
  /**
58
61
  * Write output to a Buffer.
59
62
  * JPEG, PNG, WebP, TIFF and RAW output are supported.
60
- * By default, the format will match the input image, except GIF and SVG input which become PNG output.
63
+ *
64
+ * If no explicit format is set, the output format will match the input image, except GIF and SVG input which become PNG output.
65
+ *
66
+ * By default all metadata will be removed, which includes EXIF-based orientation.
67
+ * See {@link withMetadata} for control over this.
61
68
  *
62
69
  * `callback`, if present, gets three arguments `(err, data, info)` where:
63
70
  * - `err` is an error, if any.
@@ -92,6 +99,8 @@ function toFile (fileOut, callback) {
92
99
  function toBuffer (options, callback) {
93
100
  if (is.object(options)) {
94
101
  this._setBooleanOption('resolveWithObject', options.resolveWithObject);
102
+ } else if (this.options.resolveWithObject) {
103
+ this.options.resolveWithObject = false;
95
104
  }
96
105
  return this._pipeline(is.fn(options) ? options : callback);
97
106
  }
@@ -487,6 +496,17 @@ function raw () {
487
496
  return this._updateFormatOut('raw');
488
497
  }
489
498
 
499
+ const formats = new Map([
500
+ ['heic', 'heif'],
501
+ ['heif', 'heif'],
502
+ ['jpeg', 'jpeg'],
503
+ ['jpg', 'jpeg'],
504
+ ['png', 'png'],
505
+ ['raw', 'raw'],
506
+ ['tiff', 'tiff'],
507
+ ['webp', 'webp']
508
+ ]);
509
+
490
510
  /**
491
511
  * Force output to a given format.
492
512
  *
@@ -502,14 +522,11 @@ function raw () {
502
522
  * @throws {Error} unsupported format or options
503
523
  */
504
524
  function toFormat (format, options) {
505
- if (is.object(format) && is.string(format.id)) {
506
- format = format.id;
507
- }
508
- if (format === 'jpg') format = 'jpeg';
509
- if (!is.inArray(format, ['jpeg', 'png', 'webp', 'tiff', 'raw'])) {
510
- throw is.invalidParameterError('format', 'one of: jpeg, png, webp, tiff, raw', format);
525
+ const actualFormat = formats.get(is.object(format) && is.string(format.id) ? format.id : format);
526
+ if (!actualFormat) {
527
+ throw is.invalidParameterError('format', `one of: ${[...formats.keys()].join(', ')}`, format);
511
528
  }
512
- return this[format](options);
529
+ return this[actualFormat](options);
513
530
  }
514
531
 
515
532
  /**
@@ -534,6 +551,7 @@ function toFormat (format, options) {
534
551
  * @param {Number} [options.size=256] tile size in pixels, a value between 1 and 8192.
535
552
  * @param {Number} [options.overlap=0] tile overlap in pixels, a value between 0 and 8192.
536
553
  * @param {Number} [options.angle=0] tile angle of rotation, must be a multiple of 90.
554
+ * @param {String|Object} [options.background={r: 255, g: 255, b: 255, alpha: 1}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to white without transparency.
537
555
  * @param {String} [options.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
538
556
  * @param {Number} [options.skipBlanks=-1] threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images
539
557
  * @param {String} [options.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
@@ -557,7 +575,7 @@ function tile (options) {
557
575
  if (options.overlap > this.options.tileSize) {
558
576
  throw is.invalidParameterError('overlap', `<= size (${this.options.tileSize})`, options.overlap);
559
577
  }
560
- this.options.tileOverlap = tile.overlap;
578
+ this.options.tileOverlap = options.overlap;
561
579
  } else {
562
580
  throw is.invalidParameterError('overlap', 'integer between 0 and 8192', options.overlap);
563
581
  }
@@ -586,6 +604,8 @@ function tile (options) {
586
604
  throw is.invalidParameterError('angle', 'positive/negative multiple of 90', options.angle);
587
605
  }
588
606
  }
607
+ // Background colour
608
+ this._setBackgroundColourOption('tileBackground', options.background);
589
609
  // Depth of tiles
590
610
  if (is.defined(options.depth)) {
591
611
  if (is.string(options.depth) && is.inArray(options.depth, ['onepixel', 'onetile', 'one'])) {
package/lib/platform.js CHANGED
@@ -7,6 +7,7 @@ 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 */
10
11
  const libc = (platform === 'linux' && detectLibc.isNonGlibcLinux) ? detectLibc.family : '';
11
12
 
12
13
  const platformId = [`${platform}${libc}`];
package/lib/resize.js CHANGED
@@ -210,12 +210,20 @@ function resize (width, height, options) {
210
210
  }
211
211
  if (is.object(options)) {
212
212
  // Width
213
- if (is.integer(options.width) && options.width > 0) {
214
- this.options.width = options.width;
213
+ if (is.defined(options.width)) {
214
+ if (is.integer(options.width) && options.width > 0) {
215
+ this.options.width = options.width;
216
+ } else {
217
+ throw is.invalidParameterError('width', 'positive integer', options.width);
218
+ }
215
219
  }
216
220
  // Height
217
- if (is.integer(options.height) && options.height > 0) {
218
- this.options.height = options.height;
221
+ if (is.defined(options.height)) {
222
+ if (is.integer(options.height) && options.height > 0) {
223
+ this.options.height = options.height;
224
+ } else {
225
+ throw is.invalidParameterError('height', 'positive integer', options.height);
226
+ }
219
227
  }
220
228
  // Fit
221
229
  if (is.defined(options.fit)) {
@@ -238,9 +246,7 @@ function resize (width, height, options) {
238
246
  }
239
247
  }
240
248
  // Background
241
- if (is.defined(options.background)) {
242
- this._setColourOption('resizeBackground', options.background);
243
- }
249
+ this._setBackgroundColourOption('resizeBackground', options.background);
244
250
  // Kernel
245
251
  if (is.defined(options.kernel)) {
246
252
  if (is.string(kernel[options.kernel])) {
@@ -305,7 +311,7 @@ function extend (extend) {
305
311
  this.options.extendBottom = extend.bottom;
306
312
  this.options.extendLeft = extend.left;
307
313
  this.options.extendRight = extend.right;
308
- this._setColourOption('extendBackground', extend.background);
314
+ this._setBackgroundColourOption('extendBackground', extend.background);
309
315
  } else {
310
316
  throw is.invalidParameterError('extend', 'integer or object', extend);
311
317
  }
@@ -361,7 +367,10 @@ function extract (options) {
361
367
 
362
368
  /**
363
369
  * Trim "boring" pixels from all edges that contain values similar to the top-left pixel.
370
+ * Images consisting entirely of a single colour will calculate "boring" using the alpha channel, if any.
371
+ *
364
372
  * The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
373
+ *
365
374
  * @param {Number} [threshold=10] the allowed difference from the top-left pixel, a number greater than zero.
366
375
  * @returns {Sharp}
367
376
  * @throws {Error} Invalid parameters
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 and TIFF images",
4
- "version": "0.23.0",
4
+ "version": "0.23.4",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://github.com/lovell/sharp",
7
7
  "contributors": [
@@ -61,12 +61,15 @@
61
61
  "Keith Belovay <keith@picthrive.com>",
62
62
  "Michael B. Klein <mbklein@gmail.com>",
63
63
  "Jordan Prudhomme <jordan@raboland.fr>",
64
- "Ilya Ovdin <iovdin@gmail.com>"
64
+ "Ilya Ovdin <iovdin@gmail.com>",
65
+ "Andargor <andargor@yahoo.com>",
66
+ "Paul Neave <paul.neave@gmail.com>",
67
+ "Brendan Kennedy <brenwken@gmail.com>"
65
68
  ],
66
69
  "scripts": {
67
70
  "install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
68
71
  "clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
69
- "test": "semistandard && cc && npm run test-unit && npm run test-licensing && prebuild-ci",
72
+ "test": "semistandard && cpplint && npm run test-unit && npm run test-licensing && prebuild-ci",
70
73
  "test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
71
74
  "test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
72
75
  "test-coverage": "./test/coverage/report.sh",
@@ -74,6 +77,14 @@
74
77
  "docs": "for m in constructor input resize composite operation colour channel output utility; do documentation build --shallow --format=md --markdown-toc=false lib/$m.js >docs/api-$m.md; done"
75
78
  },
76
79
  "main": "lib/index.js",
80
+ "files": [
81
+ "binding.gyp",
82
+ "docs/**",
83
+ "!docs/css/**",
84
+ "install/**",
85
+ "lib/**",
86
+ "src/**"
87
+ ],
77
88
  "repository": {
78
89
  "type": "git",
79
90
  "url": "git://github.com/lovell/sharp"
@@ -99,27 +110,27 @@
99
110
  "detect-libc": "^1.0.3",
100
111
  "nan": "^2.14.0",
101
112
  "npmlog": "^4.1.2",
102
- "prebuild-install": "^5.3.0",
113
+ "prebuild-install": "^5.3.3",
103
114
  "semver": "^6.3.0",
104
- "simple-get": "^3.0.3",
105
- "tar": "^4.4.10",
115
+ "simple-get": "^3.1.0",
116
+ "tar": "^5.0.5",
106
117
  "tunnel-agent": "^0.6.0"
107
118
  },
108
119
  "devDependencies": {
109
120
  "async": "^3.1.0",
110
- "cc": "^1.0.2",
121
+ "cc": "^2.0.1",
111
122
  "decompress-zip": "^0.3.2",
112
- "documentation": "^12.0.3",
113
- "exif-reader": "^1.0.2",
123
+ "documentation": "^12.1.4",
124
+ "exif-reader": "^1.0.3",
114
125
  "icc": "^1.0.0",
115
126
  "license-checker": "^25.0.1",
116
- "mocha": "^6.2.0",
117
- "mock-fs": "^4.10.1",
127
+ "mocha": "^6.2.2",
128
+ "mock-fs": "^4.10.4",
118
129
  "nyc": "^14.1.1",
119
- "prebuild": "^9.0.1",
130
+ "prebuild": "^9.1.1",
120
131
  "prebuild-ci": "^3.1.0",
121
- "rimraf": "^2.6.3",
122
- "semistandard": "^13.0.1"
132
+ "rimraf": "^3.0.0",
133
+ "semistandard": "^14.2.0"
123
134
  },
124
135
  "license": "Apache-2.0",
125
136
  "config": {
@@ -128,6 +139,9 @@
128
139
  "engines": {
129
140
  "node": ">=8.5.0"
130
141
  },
142
+ "funding": {
143
+ "url": "https://opencollective.com/libvips"
144
+ },
131
145
  "semistandard": {
132
146
  "env": [
133
147
  "mocha"
@@ -136,10 +150,7 @@
136
150
  "cc": {
137
151
  "linelength": "120",
138
152
  "filter": [
139
- "build/c++11",
140
- "build/include",
141
- "runtime/indentation_namespace",
142
- "runtime/references"
153
+ "build/include"
143
154
  ]
144
155
  }
145
156
  }
package/src/common.cc CHANGED
@@ -17,7 +17,7 @@
17
17
  #include <string.h>
18
18
  #include <vector>
19
19
  #include <queue>
20
- #include <mutex>
20
+ #include <mutex> // NOLINT(build/c++11)
21
21
 
22
22
  #include <node.h>
23
23
  #include <node_buffer.h>
@@ -58,6 +58,7 @@ namespace sharp {
58
58
  v8::Local<v8::Object> buffer = AttrAs<v8::Object>(input, "buffer");
59
59
  descriptor->bufferLength = node::Buffer::Length(buffer);
60
60
  descriptor->buffer = node::Buffer::Data(buffer);
61
+ descriptor->isBuffer = TRUE;
61
62
  buffersToPersist.push_back(buffer);
62
63
  }
63
64
  descriptor->failOnError = AttrTo<bool>(input, "failOnError");
@@ -147,7 +148,7 @@ namespace sharp {
147
148
  case ImageType::OPENSLIDE: id = "openslide"; break;
148
149
  case ImageType::PPM: id = "ppm"; break;
149
150
  case ImageType::FITS: id = "fits"; break;
150
- case ImageType::VIPS: id = "v"; break;
151
+ case ImageType::VIPS: id = "vips"; break;
151
152
  case ImageType::RAW: id = "raw"; break;
152
153
  case ImageType::UNKNOWN: id = "unknown"; break;
153
154
  case ImageType::MISSING: id = "missing"; break;
@@ -246,7 +247,7 @@ namespace sharp {
246
247
  std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor, VipsAccess accessMethod) {
247
248
  VImage image;
248
249
  ImageType imageType;
249
- if (descriptor->buffer != nullptr) {
250
+ if (descriptor->isBuffer) {
250
251
  if (descriptor->rawChannels > 0) {
251
252
  // Raw, uncompressed pixel data
252
253
  image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
@@ -277,7 +278,7 @@ namespace sharp {
277
278
  }
278
279
  image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
279
280
  if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
280
- SetDensity(image, descriptor->density);
281
+ image = SetDensity(image, descriptor->density);
281
282
  }
282
283
  } catch (vips::VError const &err) {
283
284
  throw vips::VError(std::string("Input buffer has corrupt header: ") + err.what());
@@ -323,7 +324,7 @@ namespace sharp {
323
324
  }
324
325
  image = VImage::new_from_file(descriptor->file.data(), option);
325
326
  if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
326
- SetDensity(image, descriptor->density);
327
+ image = SetDensity(image, descriptor->density);
327
328
  }
328
329
  } catch (vips::VError const &err) {
329
330
  throw vips::VError(std::string("Input file has corrupt header: ") + err.what());
@@ -370,15 +371,19 @@ namespace sharp {
370
371
  /*
371
372
  Set EXIF Orientation of image.
372
373
  */
373
- void SetExifOrientation(VImage image, int const orientation) {
374
- image.set(VIPS_META_ORIENTATION, orientation);
374
+ VImage SetExifOrientation(VImage image, int const orientation) {
375
+ VImage copy = image.copy();
376
+ copy.set(VIPS_META_ORIENTATION, orientation);
377
+ return copy;
375
378
  }
376
379
 
377
380
  /*
378
381
  Remove EXIF Orientation from image.
379
382
  */
380
- void RemoveExifOrientation(VImage image) {
381
- vips_image_remove(image.get_image(), VIPS_META_ORIENTATION);
383
+ VImage RemoveExifOrientation(VImage image) {
384
+ VImage copy = image.copy();
385
+ copy.remove(VIPS_META_ORIENTATION);
386
+ return copy;
382
387
  }
383
388
 
384
389
  /*
@@ -398,11 +403,13 @@ namespace sharp {
398
403
  /*
399
404
  Set pixels/mm resolution based on a pixels/inch density.
400
405
  */
401
- void SetDensity(VImage image, const double density) {
406
+ VImage SetDensity(VImage image, const double density) {
402
407
  const double pixelsPerMm = density / 25.4;
403
- image.set("Xres", pixelsPerMm);
404
- image.set("Yres", pixelsPerMm);
405
- image.set(VIPS_META_RESOLUTION_UNIT, "in");
408
+ VImage copy = image.copy();
409
+ copy.set("Xres", pixelsPerMm);
410
+ copy.set("Yres", pixelsPerMm);
411
+ copy.set(VIPS_META_RESOLUTION_UNIT, "in");
412
+ return copy;
406
413
  }
407
414
 
408
415
  /*
package/src/common.h CHANGED
@@ -43,12 +43,13 @@ using vips::VImage;
43
43
 
44
44
  namespace sharp {
45
45
 
46
- struct InputDescriptor {
46
+ struct InputDescriptor { // NOLINT(runtime/indentation_namespace)
47
47
  std::string name;
48
48
  std::string file;
49
49
  char *buffer;
50
50
  bool failOnError;
51
51
  size_t bufferLength;
52
+ bool isBuffer;
52
53
  double density;
53
54
  int rawChannels;
54
55
  int rawWidth;
@@ -64,6 +65,7 @@ namespace sharp {
64
65
  buffer(nullptr),
65
66
  failOnError(TRUE),
66
67
  bufferLength(0),
68
+ isBuffer(FALSE),
67
69
  density(72.0),
68
70
  rawChannels(0),
69
71
  rawWidth(0),
@@ -92,7 +94,7 @@ namespace sharp {
92
94
 
93
95
  // Create an InputDescriptor instance from a v8::Object describing an input image
94
96
  InputDescriptor* CreateInputDescriptor(
95
- v8::Local<v8::Object> input, std::vector<v8::Local<v8::Object>> &buffersToPersist);
97
+ v8::Local<v8::Object> input, std::vector<v8::Local<v8::Object>> &buffersToPersist); // NOLINT(runtime/references)
96
98
 
97
99
  enum class ImageType {
98
100
  JPEG,
@@ -175,12 +177,12 @@ namespace sharp {
175
177
  /*
176
178
  Set EXIF Orientation of image.
177
179
  */
178
- void SetExifOrientation(VImage image, int const orientation);
180
+ VImage SetExifOrientation(VImage image, int const orientation);
179
181
 
180
182
  /*
181
183
  Remove EXIF Orientation from image.
182
184
  */
183
- void RemoveExifOrientation(VImage image);
185
+ VImage RemoveExifOrientation(VImage image);
184
186
 
185
187
  /*
186
188
  Does this image have a non-default density?
@@ -195,7 +197,7 @@ namespace sharp {
195
197
  /*
196
198
  Set pixels/mm resolution based on a pixels/inch density.
197
199
  */
198
- void SetDensity(VImage image, const double density);
200
+ VImage SetDensity(VImage image, const double density);
199
201
 
200
202
  /*
201
203
  Check the proposed format supports the current dimensions.
package/src/metadata.cc CHANGED
@@ -116,6 +116,14 @@ class MetadataWorker : public Nan::AsyncWorker {
116
116
  memcpy(baton->xmp, xmp, xmpLength);
117
117
  baton->xmpLength = xmpLength;
118
118
  }
119
+ // TIFFTAG_PHOTOSHOP
120
+ if (image.get_typeof(VIPS_META_PHOTOSHOP_NAME) == VIPS_TYPE_BLOB) {
121
+ size_t tifftagPhotoshopLength;
122
+ void const *tifftagPhotoshop = image.get_blob(VIPS_META_PHOTOSHOP_NAME, &tifftagPhotoshopLength);
123
+ baton->tifftagPhotoshop = static_cast<char *>(g_malloc(tifftagPhotoshopLength));
124
+ memcpy(baton->tifftagPhotoshop, tifftagPhotoshop, tifftagPhotoshopLength);
125
+ baton->tifftagPhotoshopLength = tifftagPhotoshopLength;
126
+ }
119
127
  }
120
128
 
121
129
  // Clean up
@@ -189,6 +197,12 @@ class MetadataWorker : public Nan::AsyncWorker {
189
197
  New("xmp").ToLocalChecked(),
190
198
  Nan::NewBuffer(baton->xmp, baton->xmpLength, sharp::FreeCallback, nullptr).ToLocalChecked());
191
199
  }
200
+ if (baton->tifftagPhotoshopLength > 0) {
201
+ Set(info,
202
+ New("tifftagPhotoshop").ToLocalChecked(),
203
+ Nan::NewBuffer(baton->tifftagPhotoshop, baton->tifftagPhotoshopLength, sharp::FreeCallback, nullptr)
204
+ .ToLocalChecked());
205
+ }
192
206
  argv[1] = info;
193
207
  }
194
208
 
package/src/metadata.h CHANGED
@@ -48,6 +48,8 @@ struct MetadataBaton {
48
48
  size_t iptcLength;
49
49
  char *xmp;
50
50
  size_t xmpLength;
51
+ char *tifftagPhotoshop;
52
+ size_t tifftagPhotoshopLength;
51
53
  std::string err;
52
54
 
53
55
  MetadataBaton():
@@ -71,7 +73,9 @@ struct MetadataBaton {
71
73
  iptc(nullptr),
72
74
  iptcLength(0),
73
75
  xmp(nullptr),
74
- xmpLength(0) {}
76
+ xmpLength(0),
77
+ tifftagPhotoshop(nullptr),
78
+ tifftagPhotoshopLength(0) {}
75
79
  };
76
80
 
77
81
  NAN_METHOD(metadata);