@revizly/sharp 0.34.1-revizly9 → 0.34.4-revizly3

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/output.js CHANGED
@@ -312,6 +312,59 @@ function withIccProfile (icc, options) {
312
312
  return this;
313
313
  }
314
314
 
315
+ /**
316
+ * Keep XMP metadata from the input image in the output image.
317
+ *
318
+ * @since 0.34.3
319
+ *
320
+ * @example
321
+ * const outputWithXmp = await sharp(inputWithXmp)
322
+ * .keepXmp()
323
+ * .toBuffer();
324
+ *
325
+ * @returns {Sharp}
326
+ */
327
+ function keepXmp () {
328
+ this.options.keepMetadata |= 0b00010;
329
+ return this;
330
+ }
331
+
332
+ /**
333
+ * Set XMP metadata in the output image.
334
+ *
335
+ * Supported by PNG, JPEG, WebP, and TIFF output.
336
+ *
337
+ * @since 0.34.3
338
+ *
339
+ * @example
340
+ * const xmpString = `
341
+ * <?xml version="1.0"?>
342
+ * <x:xmpmeta xmlns:x="adobe:ns:meta/">
343
+ * <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
344
+ * <rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">
345
+ * <dc:creator><rdf:Seq><rdf:li>John Doe</rdf:li></rdf:Seq></dc:creator>
346
+ * </rdf:Description>
347
+ * </rdf:RDF>
348
+ * </x:xmpmeta>`;
349
+ *
350
+ * const data = await sharp(input)
351
+ * .withXmp(xmpString)
352
+ * .toBuffer();
353
+ *
354
+ * @param {string} xmp String containing XMP metadata to be embedded in the output image.
355
+ * @returns {Sharp}
356
+ * @throws {Error} Invalid parameters
357
+ */
358
+ function withXmp (xmp) {
359
+ if (is.string(xmp) && xmp.length > 0) {
360
+ this.options.withXmp = xmp;
361
+ this.options.keepMetadata |= 0b00010;
362
+ } else {
363
+ throw is.invalidParameterError('xmp', 'non-empty string', xmp);
364
+ }
365
+ return this;
366
+ }
367
+
315
368
  /**
316
369
  * Keep all metadata (EXIF, ICC, XMP, IPTC) from the input image in the output image.
317
370
  *
@@ -729,6 +782,7 @@ function webp (options) {
729
782
  * @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most)
730
783
  * @param {number} [options.interFrameMaxError=0] - maximum inter-frame error for transparency, between 0 (lossless) and 32
731
784
  * @param {number} [options.interPaletteMaxError=3] - maximum inter-palette error for palette reuse, between 0 and 256
785
+ * @param {boolean} [options.keepDuplicateFrames=false] - keep duplicate frames in the output instead of combining them
732
786
  * @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
733
787
  * @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
734
788
  * @param {boolean} [options.force=true] - force GIF output, otherwise attempt to use input format
@@ -779,6 +833,13 @@ function gif (options) {
779
833
  throw is.invalidParameterError('interPaletteMaxError', 'number between 0.0 and 256.0', options.interPaletteMaxError);
780
834
  }
781
835
  }
836
+ if (is.defined(options.keepDuplicateFrames)) {
837
+ if (is.bool(options.keepDuplicateFrames)) {
838
+ this._setBooleanOption('gifKeepDuplicateFrames', options.keepDuplicateFrames);
839
+ } else {
840
+ throw is.invalidParameterError('keepDuplicateFrames', 'boolean', options.keepDuplicateFrames);
841
+ }
842
+ }
782
843
  }
783
844
  trySetAnimationOptions(options, this.options);
784
845
  return this._updateFormatOut('gif', options);
@@ -1019,6 +1080,9 @@ function tiff (options) {
1019
1080
  * AVIF image sequences are not supported.
1020
1081
  * Prebuilt binaries support a bitdepth of 8 only.
1021
1082
  *
1083
+ * This feature is experimental on the Windows ARM64 platform
1084
+ * and requires a CPU with ARM64v8.4 or later.
1085
+ *
1022
1086
  * @example
1023
1087
  * const data = await sharp(input)
1024
1088
  * .avif({ effort: 2 })
@@ -1565,6 +1629,8 @@ module.exports = function (Sharp) {
1565
1629
  withExifMerge,
1566
1630
  keepIccProfile,
1567
1631
  withIccProfile,
1632
+ keepXmp,
1633
+ withXmp,
1568
1634
  keepMetadata,
1569
1635
  withMetadata,
1570
1636
  toFormat,
package/lib/resize.js CHANGED
@@ -107,7 +107,7 @@ const mapFitToCanvas = {
107
107
  * @private
108
108
  */
109
109
  function isRotationExpected (options) {
110
- return (options.angle % 360) !== 0 || options.input.autoOrient === true || options.rotationAngle !== 0;
110
+ return (options.angle % 360) !== 0 || options.rotationAngle !== 0;
111
111
  }
112
112
 
113
113
  /**
@@ -129,7 +129,7 @@ function isResizeExpected (options) {
129
129
  *
130
130
  * Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
131
131
  *
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">
132
+ * <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
133
  *
134
134
  * When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
135
135
  * - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
@@ -150,6 +150,8 @@ function isResizeExpected (options) {
150
150
  * - `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
151
151
  * - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
152
152
  * - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
153
+ * - `mks2013`: Use a [Magic Kernel Sharp](https://johncostella.com/magic/mks.pdf) 2013 kernel, as adopted by Facebook.
154
+ * - `mks2021`: Use a Magic Kernel Sharp 2021 kernel, with more accurate (reduced) sharpening than the 2013 version.
153
155
  *
154
156
  * When upsampling, these kernels map to `nearest`, `linear` and `cubic` interpolators.
155
157
  * Downsampling kernels without a matching upsampling interpolator map to `cubic`.
@@ -341,7 +343,7 @@ function resize (widthOrOptions, height, options) {
341
343
  }
342
344
  }
343
345
  if (isRotationExpected(this.options) && isResizeExpected(this.options)) {
344
- this.options.rotateBeforePreExtract = true;
346
+ this.options.rotateBefore = true;
345
347
  }
346
348
  return this;
347
349
  }
@@ -488,9 +490,12 @@ function extract (options) {
488
490
  // Ensure existing rotation occurs before pre-resize extraction
489
491
  if (isRotationExpected(this.options) && !isResizeExpected(this.options)) {
490
492
  if (this.options.widthPre === -1 || this.options.widthPost === -1) {
491
- this.options.rotateBeforePreExtract = true;
493
+ this.options.rotateBefore = true;
492
494
  }
493
495
  }
496
+ if (this.options.input.autoOrient) {
497
+ this.options.orientBefore = true;
498
+ }
494
499
  return this;
495
500
  }
496
501
 
@@ -564,7 +569,7 @@ function trim (options) {
564
569
  }
565
570
  }
566
571
  if (isRotationExpected(this.options)) {
567
- this.options.rotateBeforePreExtract = true;
572
+ this.options.rotateBefore = true;
568
573
  }
569
574
  return this;
570
575
  }
package/lib/utility.js CHANGED
@@ -135,15 +135,9 @@ cache(true);
135
135
  * e.g. libaom manages its own 4 threads when encoding AVIF images,
136
136
  * and these are independent of the value set here.
137
137
  *
138
- * The maximum number of images that sharp can process in parallel
139
- * is controlled by libuv's `UV_THREADPOOL_SIZE` environment variable,
140
- * which defaults to 4.
141
- *
142
- * https://nodejs.org/api/cli.html#uv_threadpool_sizesize
143
- *
144
- * For example, by default, a machine with 8 CPU cores will process
145
- * 4 images in parallel and use up to 8 threads per image,
146
- * so there will be up to 32 concurrent threads.
138
+ * :::note
139
+ * Further {@link /performance|control over performance} is available.
140
+ * :::
147
141
  *
148
142
  * @example
149
143
  * const threads = sharp.concurrency(); // 4
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@revizly/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.34.1-revizly9",
4
+ "version": "0.34.4-revizly3",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://sharp.pixelplumbing.com",
7
7
  "contributors": [
@@ -92,7 +92,7 @@
92
92
  "Don Denton <don@happycollision.com>"
93
93
  ],
94
94
  "scripts": {
95
- "install": "node install/check",
95
+ "install": "node install/check.js",
96
96
  "clean": "rm -rf src/build/ .nyc_output/ coverage/ test/fixtures/output.*",
97
97
  "test": "npm run test-lint && npm run test-unit && npm run test-licensing && npm run test-types",
98
98
  "test-lint": "semistandard && cpplint",
@@ -100,8 +100,8 @@
100
100
  "test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;LGPL-3.0-or-later;MIT\"",
101
101
  "test-leak": "./test/leak/leak.sh",
102
102
  "test-types": "tsd",
103
- "package-from-local-build": "node npm/from-local-build",
104
- "package-from-github-release": "node npm/from-github-release",
103
+ "package-from-local-build": "node npm/from-local-build.js",
104
+ "package-release-notes": "node npm/release-notes.js",
105
105
  "docs-build": "node docs/build.mjs",
106
106
  "docs-serve": "cd docs && npm start",
107
107
  "docs-publish": "cd docs && npm run build && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
@@ -138,49 +138,44 @@
138
138
  ],
139
139
  "dependencies": {
140
140
  "color": "^4.2.3",
141
- "detect-libc": "^2.0.3",
142
- "semver": "^7.7.1"
141
+ "detect-libc": "^2.0.4",
142
+ "semver": "^7.7.2"
143
143
  },
144
144
  "optionalDependencies": {
145
- "@revizly/sharp-libvips-linux-arm64": "1.0.18",
146
- "@revizly/sharp-libvips-linux-x64": "1.0.18",
147
- "@revizly/sharp-linux-arm64": "0.34.1-revizly7",
148
- "@revizly/sharp-linux-x64": "0.34.1-revizly7"
145
+ "@revizly/sharp-libvips-linux-arm64": "1.0.21",
146
+ "@revizly/sharp-libvips-linux-x64": "1.0.21",
147
+ "@revizly/sharp-linux-arm64": "0.34.1-revizly12",
148
+ "@revizly/sharp-linux-x64": "0.34.1-revizly12"
149
149
  },
150
150
  "devDependencies": {
151
- "@emnapi/runtime": "^1.4.0",
152
- "@revizly/sharp-libvips-dev": "1.0.18",
151
+ "@emnapi/runtime": "^1.4.5",
152
+ "@revizly/sharp-libvips-dev": "1.0.21",
153
153
  "@types/node": "*",
154
154
  "cc": "^3.0.1",
155
- "emnapi": "^1.4.0",
155
+ "emnapi": "^1.4.5",
156
156
  "exif-reader": "^2.0.2",
157
157
  "extract-zip": "^2.0.1",
158
158
  "icc": "^3.0.0",
159
- "jsdoc-to-markdown": "^9.1.1",
159
+ "jsdoc-to-markdown": "^9.1.2",
160
160
  "license-checker": "^25.0.1",
161
- "mocha": "^11.1.0",
162
- "node-addon-api": "^8.3.1",
161
+ "mocha": "^11.7.1",
162
+ "node-addon-api": "^8.5.0",
163
+ "node-gyp": "^11.4.1",
163
164
  "nyc": "^17.1.0",
164
- "prebuild": "^13.0.1",
165
165
  "semistandard": "^17.0.0",
166
- "tar-fs": "^3.0.8",
167
- "tsd": "^0.31.2"
166
+ "tar-fs": "^3.1.0",
167
+ "tsd": "^0.33.0"
168
168
  },
169
169
  "license": "Apache-2.0",
170
170
  "engines": {
171
171
  "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
172
172
  },
173
173
  "config": {
174
- "libvips": ">=8.16.1"
174
+ "libvips": ">=8.17.1"
175
175
  },
176
176
  "funding": {
177
177
  "url": "https://opencollective.com/libvips"
178
178
  },
179
- "binary": {
180
- "napi_versions": [
181
- 9
182
- ]
183
- },
184
179
  "semistandard": {
185
180
  "env": [
186
181
  "mocha"
package/src/binding.gyp CHANGED
@@ -163,6 +163,8 @@
163
163
  },
164
164
  'xcode_settings': {
165
165
  'OTHER_LDFLAGS': [
166
+ '-Wl,-s',
167
+ '-Wl,-dead_strip',
166
168
  # Ensure runtime linking is relative to sharp.node
167
169
  '-Wl,-rpath,\'@loader_path/../../sharp-libvips-<(platform_and_arch)/lib\'',
168
170
  '-Wl,-rpath,\'@loader_path/../../../sharp-libvips-<(platform_and_arch)/<(sharp_libvips_version)/lib\'',
@@ -176,6 +178,9 @@
176
178
  'defines': [
177
179
  '_GLIBCXX_USE_CXX11_ABI=1'
178
180
  ],
181
+ 'cflags_cc': [
182
+ '<!(node -p "require(\'detect-libc\').isNonGlibcLinuxSync() ? \'\' : \'-flto=auto\'")'
183
+ ],
179
184
  'link_settings': {
180
185
  'libraries': [
181
186
  '-l:libvips-cpp.so.<(vips_version)'
@@ -203,11 +208,9 @@
203
208
  '-Oz',
204
209
  '-sALLOW_MEMORY_GROWTH',
205
210
  '-sENVIRONMENT=node',
206
- '-sEXPORTED_FUNCTIONS=["emnapiInit", "_vips_shutdown", "_uv_library_shutdown"]',
211
+ '-sEXPORTED_FUNCTIONS=emnapiInit,_vips_shutdown,_uv_library_shutdown',
207
212
  '-sNODERAWFS',
208
- '-sTEXTDECODER=0',
209
- '-sWASM_ASYNC_COMPILATION=0',
210
- '-sWASM_BIGINT'
213
+ '-sWASM_ASYNC_COMPILATION=0'
211
214
  ],
212
215
  'libraries': [
213
216
  '<!@(PKG_CONFIG_PATH="<!(node -p "require(\'@revizly/sharp-libvips-dev-wasm32/lib\')")/pkgconfig" pkg-config --static --libs vips-cpp)'
package/src/common.cc CHANGED
@@ -93,6 +93,7 @@ namespace sharp {
93
93
  descriptor->rawWidth = AttrAsUint32(input, "rawWidth");
94
94
  descriptor->rawHeight = AttrAsUint32(input, "rawHeight");
95
95
  descriptor->rawPremultiplied = AttrAsBool(input, "rawPremultiplied");
96
+ descriptor->rawPageHeight = AttrAsUint32(input, "rawPageHeight");
96
97
  }
97
98
  // Multi-page input (GIF, TIFF, PDF)
98
99
  if (HasAttr(input, "pages")) {
@@ -101,23 +102,35 @@ namespace sharp {
101
102
  if (HasAttr(input, "page")) {
102
103
  descriptor->page = AttrAsUint32(input, "page");
103
104
  }
105
+ // SVG
106
+ if (HasAttr(input, "svgStylesheet")) {
107
+ descriptor->svgStylesheet = AttrAsStr(input, "svgStylesheet");
108
+ }
109
+ if (HasAttr(input, "svgHighBitdepth")) {
110
+ descriptor->svgHighBitdepth = AttrAsBool(input, "svgHighBitdepth");
111
+ }
104
112
  // Multi-level input (OpenSlide)
105
- if (HasAttr(input, "level")) {
106
- descriptor->level = AttrAsUint32(input, "level");
113
+ if (HasAttr(input, "openSlideLevel")) {
114
+ descriptor->openSlideLevel = AttrAsUint32(input, "openSlideLevel");
107
115
  }
108
116
  // subIFD (OME-TIFF)
109
117
  if (HasAttr(input, "subifd")) {
110
- descriptor->subifd = AttrAsInt32(input, "subifd");
118
+ descriptor->tiffSubifd = AttrAsInt32(input, "tiffSubifd");
111
119
  }
112
120
  // // PDF background color
113
121
  if (HasAttr(input, "pdfBackground")) {
114
122
  descriptor->pdfBackground = AttrAsVectorOfDouble(input, "pdfBackground");
115
123
  }
124
+ // Use JPEG 2000 oneshot mode?
125
+ if (HasAttr(input, "jp2Oneshot")) {
126
+ descriptor->jp2Oneshot = AttrAsBool(input, "jp2Oneshot");
127
+ }
116
128
  // Create new image
117
129
  if (HasAttr(input, "createChannels")) {
118
130
  descriptor->createChannels = AttrAsUint32(input, "createChannels");
119
131
  descriptor->createWidth = AttrAsUint32(input, "createWidth");
120
132
  descriptor->createHeight = AttrAsUint32(input, "createHeight");
133
+ descriptor->createPageHeight = AttrAsUint32(input, "createPageHeight");
121
134
  if (HasAttr(input, "createNoiseType")) {
122
135
  descriptor->createNoiseType = AttrAsStr(input, "createNoiseType");
123
136
  descriptor->createNoiseMean = AttrAsDouble(input, "createNoiseMean");
@@ -271,6 +284,7 @@ namespace sharp {
271
284
  case ImageType::EXR: id = "exr"; break;
272
285
  case ImageType::JXL: id = "jxl"; break;
273
286
  case ImageType::RAD: id = "rad"; break;
287
+ case ImageType::DCRAW: id = "dcraw"; break;
274
288
  case ImageType::VIPS: id = "vips"; break;
275
289
  case ImageType::RAW: id = "raw"; break;
276
290
  case ImageType::UNKNOWN: id = "unknown"; break;
@@ -319,6 +333,8 @@ namespace sharp {
319
333
  { "VipsForeignLoadJxlBuffer", ImageType::JXL },
320
334
  { "VipsForeignLoadRadFile", ImageType::RAD },
321
335
  { "VipsForeignLoadRadBuffer", ImageType::RAD },
336
+ { "VipsForeignLoadDcRawFile", ImageType::DCRAW },
337
+ { "VipsForeignLoadDcRawBuffer", ImageType::DCRAW },
322
338
  { "VipsForeignLoadVips", ImageType::VIPS },
323
339
  { "VipsForeignLoadVipsFile", ImageType::VIPS },
324
340
  { "VipsForeignLoadRaw", ImageType::RAW }
@@ -383,6 +399,48 @@ namespace sharp {
383
399
  imageType == ImageType::HEIF;
384
400
  }
385
401
 
402
+ /*
403
+ Format-specific options builder
404
+ */
405
+ vips::VOption* GetOptionsForImageType(ImageType imageType, InputDescriptor *descriptor) {
406
+ vips::VOption *option = VImage::option()
407
+ ->set("access", descriptor->access)
408
+ ->set("fail_on", descriptor->failOn);
409
+ if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
410
+ option->set("unlimited", true);
411
+ }
412
+ if (ImageTypeSupportsPage(imageType)) {
413
+ option->set("n", descriptor->pages);
414
+ option->set("page", descriptor->page);
415
+ }
416
+ switch (imageType) {
417
+ case ImageType::SVG:
418
+ option->set("dpi", descriptor->density)
419
+ ->set("stylesheet", descriptor->svgStylesheet.data())
420
+ ->set("high_bitdepth", descriptor->svgHighBitdepth);
421
+ break;
422
+ case ImageType::TIFF:
423
+ option->set("tiffSubifd", descriptor->tiffSubifd);
424
+ break;
425
+ case ImageType::PDF:
426
+ option->set("dpi", descriptor->density)
427
+ ->set("background", descriptor->pdfBackground);
428
+ break;
429
+ case ImageType::OPENSLIDE:
430
+ option->set("openSlideLevel", descriptor->openSlideLevel);
431
+ break;
432
+ case ImageType::JP2:
433
+ option->set("oneshot", descriptor->jp2Oneshot);
434
+ break;
435
+ case ImageType::MAGICK:
436
+ option->set("density", std::to_string(descriptor->density).data());
437
+ break;
438
+ default:
439
+ break;
440
+ }
441
+ return option;
442
+ }
443
+
386
444
  /*
387
445
  Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
388
446
  */
@@ -400,6 +458,10 @@ namespace sharp {
400
458
  } else {
401
459
  image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_RGB16;
402
460
  }
461
+ if (descriptor->rawPageHeight > 0) {
462
+ image.set(VIPS_META_PAGE_HEIGHT, descriptor->rawPageHeight);
463
+ image.set(VIPS_META_N_PAGES, static_cast<int>(descriptor->rawHeight / descriptor->rawPageHeight));
464
+ }
403
465
  if (descriptor->rawPremultiplied) {
404
466
  image = image.unpremultiply();
405
467
  }
@@ -409,31 +471,7 @@ namespace sharp {
409
471
  imageType = DetermineImageType(descriptor->buffer, descriptor->bufferLength);
410
472
  if (imageType != ImageType::UNKNOWN) {
411
473
  try {
412
- vips::VOption *option = VImage::option()
413
- ->set("access", descriptor->access)
414
- ->set("fail_on", descriptor->failOn);
415
- if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
416
- option->set("unlimited", true);
417
- }
418
- if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
419
- option->set("dpi", descriptor->density);
420
- }
421
- if (imageType == ImageType::MAGICK) {
422
- option->set("density", std::to_string(descriptor->density).data());
423
- }
424
- if (ImageTypeSupportsPage(imageType)) {
425
- option->set("n", descriptor->pages);
426
- option->set("page", descriptor->page);
427
- }
428
- if (imageType == ImageType::OPENSLIDE) {
429
- option->set("level", descriptor->level);
430
- }
431
- if (imageType == ImageType::TIFF) {
432
- option->set("subifd", descriptor->subifd);
433
- }
434
- if (imageType == ImageType::PDF) {
435
- option->set("background", descriptor->pdfBackground);
436
- }
474
+ vips::VOption *option = GetOptionsForImageType(imageType, descriptor);
437
475
  image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
438
476
  if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
439
477
  image = SetDensity(image, descriptor->density);
@@ -473,6 +511,10 @@ namespace sharp {
473
511
  channels < 3 ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_sRGB))
474
512
  .new_from_image(background);
475
513
  }
514
+ if (descriptor->createPageHeight > 0) {
515
+ image.set(VIPS_META_PAGE_HEIGHT, descriptor->createPageHeight);
516
+ image.set(VIPS_META_N_PAGES, static_cast<int>(descriptor->createHeight / descriptor->createPageHeight));
517
+ }
476
518
  image = image.cast(VIPS_FORMAT_UCHAR);
477
519
  imageType = ImageType::RAW;
478
520
  } else if (descriptor->textValue.length() > 0) {
@@ -516,31 +558,7 @@ namespace sharp {
516
558
  }
517
559
  if (imageType != ImageType::UNKNOWN) {
518
560
  try {
519
- vips::VOption *option = VImage::option()
520
- ->set("access", descriptor->access)
521
- ->set("fail_on", descriptor->failOn);
522
- if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
523
- option->set("unlimited", true);
524
- }
525
- if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
526
- option->set("dpi", descriptor->density);
527
- }
528
- if (imageType == ImageType::MAGICK) {
529
- option->set("density", std::to_string(descriptor->density).data());
530
- }
531
- if (ImageTypeSupportsPage(imageType)) {
532
- option->set("n", descriptor->pages);
533
- option->set("page", descriptor->page);
534
- }
535
- if (imageType == ImageType::OPENSLIDE) {
536
- option->set("level", descriptor->level);
537
- }
538
- if (imageType == ImageType::TIFF) {
539
- option->set("subifd", descriptor->subifd);
540
- }
541
- if (imageType == ImageType::PDF) {
542
- option->set("background", descriptor->pdfBackground);
543
- }
561
+ vips::VOption *option = GetOptionsForImageType(imageType, descriptor);
544
562
  image = VImage::new_from_file(descriptor->file.data(), option);
545
563
  if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
546
564
  image = SetDensity(image, descriptor->density);
@@ -951,14 +969,6 @@ namespace sharp {
951
969
  return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16;
952
970
  }
953
971
 
954
- /*
955
- Return the image alpha maximum. Useful for combining alpha bands. scRGB
956
- images are 0 - 1 for image data, but the alpha is 0 - 255.
957
- */
958
- double MaximumImageAlpha(VipsInterpretation const interpretation) {
959
- return Is16Bit(interpretation) ? 65535.0 : 255.0;
960
- }
961
-
962
972
  /*
963
973
  Convert RGBA value to another colourspace
964
974
  */
@@ -1001,16 +1011,16 @@ namespace sharp {
1001
1011
  0.0722 * colour[2])
1002
1012
  };
1003
1013
  }
1004
- // Add alpha channel to alphaColour colour
1014
+ // Add alpha channel(s) to alphaColour colour
1005
1015
  if (colour[3] < 255.0 || image.has_alpha()) {
1006
- alphaColour.push_back(colour[3] * multiplier);
1016
+ int extraBands = image.bands() > 4 ? image.bands() - 3 : 1;
1017
+ alphaColour.insert(alphaColour.end(), extraBands, colour[3] * multiplier);
1007
1018
  }
1008
1019
  // Ensure alphaColour colour uses correct colourspace
1009
1020
  alphaColour = sharp::GetRgbaAsColourspace(alphaColour, image.interpretation(), premultiply);
1010
1021
  // Add non-transparent alpha channel, if required
1011
1022
  if (colour[3] < 255.0 && !image.has_alpha()) {
1012
- image = image.bandjoin(
1013
- VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier).cast(image.format()));
1023
+ image = image.bandjoin_const({ 255 * multiplier });
1014
1024
  }
1015
1025
  return std::make_tuple(image, alphaColour);
1016
1026
  }
@@ -1030,9 +1040,7 @@ namespace sharp {
1030
1040
  */
1031
1041
  VImage EnsureAlpha(VImage image, double const value) {
1032
1042
  if (!image.has_alpha()) {
1033
- std::vector<double> alpha;
1034
- alpha.push_back(value * sharp::MaximumImageAlpha(image.interpretation()));
1035
- image = image.bandjoin_const(alpha);
1043
+ image = image.bandjoin_const({ value * vips_interpretation_max_alpha(image.interpretation()) });
1036
1044
  }
1037
1045
  return image;
1038
1046
  }
package/src/common.h CHANGED
@@ -15,9 +15,9 @@
15
15
  // Verify platform and compiler compatibility
16
16
 
17
17
  #if (VIPS_MAJOR_VERSION < 8) || \
18
- (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 16) || \
19
- (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 16 && VIPS_MICRO_VERSION < 1)
20
- #error "libvips version 8.16.1+ is required - please see https://sharp.pixelplumbing.com/install"
18
+ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 17) || \
19
+ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 17 && VIPS_MICRO_VERSION < 1)
20
+ #error "libvips version 8.17.1+ is required - please see https://sharp.pixelplumbing.com/install"
21
21
  #endif
22
22
 
23
23
  #if defined(__has_include)
@@ -48,13 +48,13 @@ namespace sharp {
48
48
  int rawWidth;
49
49
  int rawHeight;
50
50
  bool rawPremultiplied;
51
+ int rawPageHeight;
51
52
  int pages;
52
53
  int page;
53
- int level;
54
- int subifd;
55
54
  int createChannels;
56
55
  int createWidth;
57
56
  int createHeight;
57
+ int createPageHeight;
58
58
  std::vector<double> createBackground;
59
59
  std::string createNoiseType;
60
60
  double createNoiseMean;
@@ -77,7 +77,12 @@ namespace sharp {
77
77
  std::vector<double> joinBackground;
78
78
  VipsAlign joinHalign;
79
79
  VipsAlign joinValign;
80
+ std::string svgStylesheet;
81
+ bool svgHighBitdepth;
82
+ int tiffSubifd;
83
+ int openSlideLevel;
80
84
  std::vector<double> pdfBackground;
85
+ bool jp2Oneshot;
81
86
 
82
87
  InputDescriptor():
83
88
  autoOrient(false),
@@ -95,13 +100,13 @@ namespace sharp {
95
100
  rawWidth(0),
96
101
  rawHeight(0),
97
102
  rawPremultiplied(false),
103
+ rawPageHeight(0),
98
104
  pages(1),
99
105
  page(0),
100
- level(0),
101
- subifd(-1),
102
106
  createChannels(0),
103
107
  createWidth(0),
104
108
  createHeight(0),
109
+ createPageHeight(0),
105
110
  createBackground{ 0.0, 0.0, 0.0, 255.0 },
106
111
  createNoiseMean(0.0),
107
112
  createNoiseSigma(0.0),
@@ -120,7 +125,11 @@ namespace sharp {
120
125
  joinBackground{ 0.0, 0.0, 0.0, 255.0 },
121
126
  joinHalign(VIPS_ALIGN_LOW),
122
127
  joinValign(VIPS_ALIGN_LOW),
123
- pdfBackground{ 255.0, 255.0, 255.0, 255.0 } {}
128
+ svgHighBitdepth(false),
129
+ tiffSubifd(-1),
130
+ openSlideLevel(0),
131
+ pdfBackground{ 255.0, 255.0, 255.0, 255.0 },
132
+ jp2Oneshot(false) {}
124
133
  };
125
134
 
126
135
  // Convenience methods to access the attributes of a Napi::Object
@@ -160,6 +169,7 @@ namespace sharp {
160
169
  EXR,
161
170
  JXL,
162
171
  RAD,
172
+ DCRAW,
163
173
  VIPS,
164
174
  RAW,
165
175
  UNKNOWN,
@@ -216,14 +226,9 @@ namespace sharp {
216
226
  ImageType DetermineImageType(char const *file);
217
227
 
218
228
  /*
219
- Does this image type support multiple pages?
229
+ Format-specific options builder
220
230
  */
221
- bool ImageTypeSupportsPage(ImageType imageType);
222
-
223
- /*
224
- Does this image type support removal of safety limits?
225
- */
226
- bool ImageTypeSupportsUnlimited(ImageType imageType);
231
+ vips::VOption* GetOptionsForImageType(ImageType imageType, InputDescriptor *descriptor);
227
232
 
228
233
  /*
229
234
  Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
@@ -357,12 +362,6 @@ namespace sharp {
357
362
  */
358
363
  bool Is16Bit(VipsInterpretation const interpretation);
359
364
 
360
- /*
361
- Return the image alpha maximum. Useful for combining alpha bands. scRGB
362
- images are 0 - 1 for image data, but the alpha is 0 - 255.
363
- */
364
- double MaximumImageAlpha(VipsInterpretation const interpretation);
365
-
366
365
  /*
367
366
  Convert RGBA value to another colourspace
368
367
  */
package/src/metadata.cc CHANGED
@@ -262,6 +262,10 @@ class MetadataWorker : public Napi::AsyncWorker {
262
262
  }
263
263
  if (baton->xmpLength > 0) {
264
264
  info.Set("xmp", Napi::Buffer<char>::NewOrCopy(env, baton->xmp, baton->xmpLength, sharp::FreeCallback));
265
+ if (g_utf8_validate(static_cast<char const *>(baton->xmp), baton->xmpLength, nullptr)) {
266
+ info.Set("xmpAsString",
267
+ Napi::String::New(env, static_cast<char const *>(baton->xmp), baton->xmpLength));
268
+ }
265
269
  }
266
270
  if (baton->tifftagPhotoshopLength > 0) {
267
271
  info.Set("tifftagPhotoshop",