mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Compare commits
No commits in common. "main" and "v0.34.3-rc.0" have entirely different histories.
main
...
v0.34.3-rc
@ -320,6 +320,3 @@ GitHub: https://github.com/qpincon
|
||||
|
||||
Name: Hans Chen
|
||||
GitHub: https://github.com/hans00
|
||||
|
||||
Name: Thibaut Patel
|
||||
GitHub: https://github.com/tpatel
|
||||
|
@ -44,23 +44,25 @@ where the overall height is the `pageHeight` multiplied by the number of `pages`
|
||||
| [options.ignoreIcc] | <code>number</code> | <code>false</code> | should the embedded ICC profile, if any, be ignored. |
|
||||
| [options.pages] | <code>number</code> | <code>1</code> | Number of pages to extract for multi-page input (GIF, WebP, TIFF), use -1 for all pages. |
|
||||
| [options.page] | <code>number</code> | <code>0</code> | Page number to start extracting from for multi-page input (GIF, WebP, TIFF), zero based. |
|
||||
| [options.subifd] | <code>number</code> | <code>-1</code> | subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. |
|
||||
| [options.level] | <code>number</code> | <code>0</code> | level to extract from a multi-level input (OpenSlide), zero based. |
|
||||
| [options.pdfBackground] | <code>string</code> \| <code>Object</code> | | Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. |
|
||||
| [options.jp2Oneshot] | <code>boolean</code> | <code>false</code> | Set to `true` to decode tiled JPEG 2000 images in a single operation, improving compatibility. |
|
||||
| [options.animated] | <code>boolean</code> | <code>false</code> | Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`. |
|
||||
| [options.raw] | <code>Object</code> | | describes raw pixel input image data. See `raw()` for pixel ordering. |
|
||||
| [options.raw.width] | <code>number</code> | | integral number of pixels wide. |
|
||||
| [options.raw.height] | <code>number</code> | | integral number of pixels high. |
|
||||
| [options.raw.channels] | <code>number</code> | | integral number of channels, between 1 and 4. |
|
||||
| [options.raw.premultiplied] | <code>boolean</code> | | specifies that the raw input has already been premultiplied, set to `true` to avoid sharp premultiplying the image. (optional, default `false`) |
|
||||
| [options.raw.pageHeight] | <code>number</code> | | The pixel height of each page/frame for animated images, must be an integral factor of `raw.height`. |
|
||||
| [options.create] | <code>Object</code> | | describes a new image to be created. |
|
||||
| [options.create.width] | <code>number</code> | | integral number of pixels wide. |
|
||||
| [options.create.height] | <code>number</code> | | integral number of pixels high. |
|
||||
| [options.create.channels] | <code>number</code> | | integral number of channels, either 3 (RGB) or 4 (RGBA). |
|
||||
| [options.create.background] | <code>string</code> \| <code>Object</code> | | parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
|
||||
| [options.create.pageHeight] | <code>number</code> | | The pixel height of each page/frame for animated images, must be an integral factor of `create.height`. |
|
||||
| [options.create.noise] | <code>Object</code> | | describes a noise to be created. |
|
||||
| [options.create.noise.type] | <code>string</code> | | type of generated noise, currently only `gaussian` is supported. |
|
||||
| [options.create.noise.mean] | <code>number</code> | <code>128</code> | Mean value of pixels in the generated noise. |
|
||||
| [options.create.noise.sigma] | <code>number</code> | <code>30</code> | Standard deviation of pixel values in the generated noise. |
|
||||
| [options.create.noise.mean] | <code>number</code> | | mean of pixels in generated noise. |
|
||||
| [options.create.noise.sigma] | <code>number</code> | | standard deviation of pixels in generated noise. |
|
||||
| [options.text] | <code>Object</code> | | describes a new text image to be created. |
|
||||
| [options.text.text] | <code>string</code> | | text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`. |
|
||||
| [options.text.font] | <code>string</code> | | font name to render with. |
|
||||
@ -80,17 +82,6 @@ where the overall height is the `pageHeight` multiplied by the number of `pages`
|
||||
| [options.join.background] | <code>string</code> \| <code>Object</code> | | parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
|
||||
| [options.join.halign] | <code>string</code> | <code>"'left'"</code> | horizontal alignment style for images joined horizontally (`'left'`, `'centre'`, `'center'`, `'right'`). |
|
||||
| [options.join.valign] | <code>string</code> | <code>"'top'"</code> | vertical alignment style for images joined vertically (`'top'`, `'centre'`, `'center'`, `'bottom'`). |
|
||||
| [options.tiff] | <code>Object</code> | | Describes TIFF specific options. |
|
||||
| [options.tiff.subifd] | <code>number</code> | <code>-1</code> | Sub Image File Directory to extract for OME-TIFF, defaults to main image. |
|
||||
| [options.svg] | <code>Object</code> | | Describes SVG specific options. |
|
||||
| [options.svg.stylesheet] | <code>string</code> | | Custom CSS for SVG input, applied with a User Origin during the CSS cascade. |
|
||||
| [options.svg.highBitdepth] | <code>boolean</code> | <code>false</code> | Set to `true` to render SVG input at 32-bits per channel (128-bit) instead of 8-bits per channel (32-bit) RGBA. |
|
||||
| [options.pdf] | <code>Object</code> | | Describes PDF specific options. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. |
|
||||
| [options.pdf.background] | <code>string</code> \| <code>Object</code> | | Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
|
||||
| [options.openSlide] | <code>Object</code> | | Describes OpenSlide specific options. Requires the use of a globally-installed libvips compiled with support for OpenSlide. |
|
||||
| [options.openSlide.level] | <code>number</code> | <code>0</code> | Level to extract from a multi-level input, zero based. |
|
||||
| [options.jp2] | <code>Object</code> | | Describes JPEG 2000 specific options. Requires the use of a globally-installed libvips compiled with support for OpenJPEG. |
|
||||
| [options.jp2.oneshot] | <code>boolean</code> | <code>false</code> | Set to `true` to decode tiled JPEG 2000 images in a single operation, improving compatibility. |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
|
@ -242,57 +242,6 @@ const outputWithP3 = await sharp(input)
|
||||
```
|
||||
|
||||
|
||||
## keepXmp
|
||||
> keepXmp() ⇒ <code>Sharp</code>
|
||||
|
||||
Keep XMP metadata from the input image in the output image.
|
||||
|
||||
|
||||
**Since**: 0.34.3
|
||||
**Example**
|
||||
```js
|
||||
const outputWithXmp = await sharp(inputWithXmp)
|
||||
.keepXmp()
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
|
||||
## withXmp
|
||||
> withXmp(xmp) ⇒ <code>Sharp</code>
|
||||
|
||||
Set XMP metadata in the output image.
|
||||
|
||||
Supported by PNG, JPEG, WebP, and TIFF output.
|
||||
|
||||
|
||||
**Throws**:
|
||||
|
||||
- <code>Error</code> Invalid parameters
|
||||
|
||||
**Since**: 0.34.3
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| xmp | <code>string</code> | String containing XMP metadata to be embedded in the output image. |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
const xmpString = `
|
||||
<?xml version="1.0"?>
|
||||
<x:xmpmeta xmlns:x="adobe:ns:meta/">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<dc:creator><rdf:Seq><rdf:li>John Doe</rdf:li></rdf:Seq></dc:creator>
|
||||
</rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>`;
|
||||
|
||||
const data = await sharp(input)
|
||||
.withXmp(xmpString)
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
|
||||
## keepMetadata
|
||||
> keepMetadata() ⇒ <code>Sharp</code>
|
||||
|
||||
@ -547,7 +496,6 @@ The palette of the input image will be re-used if possible.
|
||||
| [options.dither] | <code>number</code> | <code>1.0</code> | level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most) |
|
||||
| [options.interFrameMaxError] | <code>number</code> | <code>0</code> | maximum inter-frame error for transparency, between 0 (lossless) and 32 |
|
||||
| [options.interPaletteMaxError] | <code>number</code> | <code>3</code> | maximum inter-palette error for palette reuse, between 0 and 256 |
|
||||
| [options.keepDuplicateFrames] | <code>boolean</code> | <code>false</code> | keep duplicate frames in the output instead of combining them |
|
||||
| [options.loop] | <code>number</code> | <code>0</code> | number of animation iterations, use 0 for infinite animation |
|
||||
| [options.delay] | <code>number</code> \| <code>Array.<number></code> | | delay(s) between animation frames (in milliseconds) |
|
||||
| [options.force] | <code>boolean</code> | <code>true</code> | force GIF output, otherwise attempt to use input format |
|
||||
|
@ -38,8 +38,6 @@ Possible downsizing kernels are:
|
||||
- `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
|
||||
- `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
|
||||
- `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
|
||||
- `mks2013`: Use a [Magic Kernel Sharp](https://johncostella.com/magic/mks.pdf) 2013 kernel, as adopted by Facebook.
|
||||
- `mks2021`: Use a Magic Kernel Sharp 2021 kernel, with more accurate (reduced) sharpening than the 2013 version.
|
||||
|
||||
When upsampling, these kernels map to `nearest`, `linear` and `cubic` interpolators.
|
||||
Downsampling kernels without a matching upsampling interpolator map to `cubic`.
|
||||
|
@ -113,9 +113,15 @@ Some image format libraries spawn additional threads,
|
||||
e.g. libaom manages its own 4 threads when encoding AVIF images,
|
||||
and these are independent of the value set here.
|
||||
|
||||
:::note
|
||||
Further [control over performance](/performance) is available.
|
||||
:::
|
||||
The maximum number of images that sharp can process in parallel
|
||||
is controlled by libuv's `UV_THREADPOOL_SIZE` environment variable,
|
||||
which defaults to 4.
|
||||
|
||||
https://nodejs.org/api/cli.html#uv_threadpool_sizesize
|
||||
|
||||
For example, by default, a machine with 8 CPU cores will process
|
||||
4 images in parallel and use up to 8 threads per image,
|
||||
so there will be up to 32 concurrent threads.
|
||||
|
||||
|
||||
**Returns**: <code>number</code> - concurrency
|
||||
|
@ -4,26 +4,11 @@ title: Changelog
|
||||
|
||||
## v0.34 - *hat*
|
||||
|
||||
Requires libvips v8.17.1
|
||||
Requires libvips v8.17.0
|
||||
|
||||
### v0.34.3 - TBD
|
||||
|
||||
* Upgrade to libvips v8.17.1 for upstream bug fixes.
|
||||
|
||||
* Add "Magic Kernel Sharp" (no relation) to resizing kernels.
|
||||
|
||||
* Deprecate top-level, format-specific constructor parameters, e.g. `subifd` becomes `tiff.subifd`.
|
||||
|
||||
* Expose `stylesheet` and `highBitdepth` SVG input parameters.
|
||||
|
||||
* Expose `keepDuplicateFrames` GIF output parameter.
|
||||
|
||||
* Add support for RAW digital camera image input. Requires libvips compiled with libraw support.
|
||||
|
||||
* Provide XMP metadata as a string, as well as a Buffer, where possible.
|
||||
|
||||
* Add `pageHeight` option to `create` and `raw` input for animated images.
|
||||
[#3236](https://github.com/lovell/sharp/issues/3236)
|
||||
* Upgrade to libvips v8.17.0 for upstream bug fixes.
|
||||
|
||||
* Expose JPEG 2000 `oneshot` decoder option.
|
||||
[#4262](https://github.com/lovell/sharp/pull/4262)
|
||||
@ -33,10 +18,6 @@ Requires libvips v8.17.1
|
||||
[#4412](https://github.com/lovell/sharp/pull/4412)
|
||||
[@kleisauke](https://github.com/kleisauke)
|
||||
|
||||
* Add `keepXmp` and `withXmp` for control over output XMP metadata.
|
||||
[#4416](https://github.com/lovell/sharp/pull/4416)
|
||||
[@tpatel](https://github.com/tpatel)
|
||||
|
||||
### v0.34.2 - 20th May 2025
|
||||
|
||||
* Ensure animated GIF to WebP conversion retains loop (regression in 0.34.0).
|
||||
|
@ -2,38 +2,6 @@
|
||||
title: Performance
|
||||
---
|
||||
|
||||
## Parallelism and concurrency
|
||||
|
||||
Node.js uses a libuv-managed thread pool when processing asynchronous calls to native modules such as sharp.
|
||||
|
||||
The maximum number of images that sharp can process in parallel is controlled by libuv's
|
||||
[`UV_THREADPOOL_SIZE`](https://nodejs.org/api/cli.html#uv_threadpool_sizesize)
|
||||
environment variable, which defaults to 4.
|
||||
|
||||
When using more than 4 physical CPU cores, set this environment variable
|
||||
before the Node.js process starts to increase the thread pool size.
|
||||
|
||||
```sh
|
||||
export UV_THREADPOOL_SIZE="$(lscpu -p | egrep -v "^#" | sort -u -t, -k 2,4 | wc -l)"
|
||||
```
|
||||
|
||||
libvips uses a glib-managed thread pool to avoid the overhead of spawning new threads.
|
||||
|
||||
The default number of threads used to concurrently process each image is the same as the number of CPU cores,
|
||||
except when using glibc-based Linux without jemalloc, where the default is `1` to help reduce memory fragmentation.
|
||||
|
||||
Use [`sharp.concurrency()`](/api-utility/#concurrency) to manage the number of threads per image.
|
||||
|
||||
To reduce memory fragmentation when using the default Linux glibc memory allocator, set the
|
||||
[`MALLOC_ARENA_MAX`](https://www.gnu.org/software/libc/manual/html_node/Memory-Allocation-Tunables.html)
|
||||
environment variable before the Node.js process starts to reduce the number of memory pools.
|
||||
|
||||
```sh
|
||||
export MALLOC_ARENA_MAX="2"
|
||||
```
|
||||
|
||||
## Benchmark
|
||||
|
||||
A test to benchmark the performance of this module relative to alternatives.
|
||||
|
||||
Greater libvips performance can be expected with caching enabled (default)
|
||||
@ -41,28 +9,28 @@ and using 8+ core machines, especially those with larger L1/L2 CPU caches.
|
||||
|
||||
The I/O limits of the relevant (de)compression library will generally determine maximum throughput.
|
||||
|
||||
### Contenders
|
||||
## Contenders
|
||||
|
||||
- [jimp](https://www.npmjs.com/package/jimp) v1.6.0 - Image processing in pure JavaScript.
|
||||
- [imagemagick](https://www.npmjs.com/package/imagemagick) v0.1.3 - Supports filesystem only and "_has been unmaintained for a long time_".
|
||||
- [gm](https://www.npmjs.com/package/gm) v1.25.1 - Fully featured wrapper around GraphicsMagick's `gm` command line utility, but "_has been sunset_".
|
||||
- sharp v0.34.3 / libvips v8.17.0 - Caching within libvips disabled to ensure a fair comparison.
|
||||
* [jimp](https://www.npmjs.com/package/jimp) v1.6.0 - Image processing in pure JavaScript.
|
||||
* [imagemagick](https://www.npmjs.com/package/imagemagick) v0.1.3 - Supports filesystem only and "*has been unmaintained for a long time*".
|
||||
* [gm](https://www.npmjs.com/package/gm) v1.25.1 - Fully featured wrapper around GraphicsMagick's `gm` command line utility, but "*has been sunset*".
|
||||
* sharp v0.34.0 / libvips v8.16.1 - Caching within libvips disabled to ensure a fair comparison.
|
||||
|
||||
### Environment
|
||||
## Environment
|
||||
|
||||
#### AMD64
|
||||
### AMD64
|
||||
|
||||
- AWS EC2 us-west-2 [c7a.xlarge](https://aws.amazon.com/ec2/instance-types/c7a/) (4x AMD EPYC 9R14)
|
||||
- Ubuntu 25.04
|
||||
- Node.js 24.3.0
|
||||
* AWS EC2 us-west-2 [c7a.xlarge](https://aws.amazon.com/ec2/instance-types/c7a/) (4x AMD EPYC 9R14)
|
||||
* Ubuntu 24.10 [fad5ba7223f8](https://hub.docker.com/layers/library/ubuntu/24.10/images/sha256-fad5ba7223f8d87179dfa23211d31845d47e07a474ac31ad5258afb606523c0d)
|
||||
* Node.js 22.14.0
|
||||
|
||||
#### ARM64
|
||||
### ARM64
|
||||
|
||||
- AWS EC2 us-west-2 [c8g.xlarge](https://aws.amazon.com/ec2/instance-types/c8g/) (4x ARM Graviton4)
|
||||
- Ubuntu 25.04
|
||||
- Node.js 24.3.0
|
||||
* AWS EC2 us-west-2 [c8g.xlarge](https://aws.amazon.com/ec2/instance-types/c8g/) (4x ARM Graviton4)
|
||||
* Ubuntu 24.10 [133f2e05cb69](https://hub.docker.com/layers/library/ubuntu/24.10/images/sha256-133f2e05cb6958c3ce7ec870fd5a864558ba780fb7062315b51a23670bff7e76)
|
||||
* Node.js 22.14.0
|
||||
|
||||
### Task: JPEG
|
||||
## Task: JPEG
|
||||
|
||||
Decompress a 2725x2225 JPEG image,
|
||||
resize to 720x588 using Lanczos 3 resampling (where available),
|
||||
@ -72,31 +40,29 @@ Note: jimp does not support Lanczos 3, bicubic resampling used instead.
|
||||
|
||||
#### Results: JPEG (AMD64)
|
||||
|
||||
| Package | I/O | Ops/sec | Speed-up |
|
||||
| :---------- | :----- | ------: | -------: |
|
||||
| jimp | buffer | 2.40 | 1.0 |
|
||||
| jimp | file | 2.60 | 1.1 |
|
||||
| imagemagick | file | 9.70 | 4.0 |
|
||||
| gm | buffer | 11.60 | 4.8 |
|
||||
| gm | file | 11.72 | 4.9 |
|
||||
| sharp | stream | 59.40 | 24.8 |
|
||||
| sharp | file | 62.67 | 26.1 |
|
||||
| sharp | buffer | 64.42 | 26.8 |
|
||||
| Module | Input | Output | Ops/sec | Speed-up |
|
||||
| :----------------- | :----- | :----- | ------: | -------: |
|
||||
| jimp | buffer | buffer | 2.35 | 1.0 |
|
||||
| imagemagick | file | file | 10.51 | 4.5 |
|
||||
| gm | buffer | buffer | 11.67 | 5.0 |
|
||||
| gm | file | file | 11.75 | 5.1 |
|
||||
| sharp | stream | stream | 60.72 | 25.8 |
|
||||
| sharp | file | file | 62.37 | 26.5 |
|
||||
| sharp | buffer | buffer | 65.15 | 27.7 |
|
||||
|
||||
#### Results: JPEG (ARM64)
|
||||
|
||||
| Package | I/O | Ops/sec | Speed-up |
|
||||
| :---------- | :----- | ------: | -------: |
|
||||
| jimp | buffer | 2.24 | 1.0 |
|
||||
| jimp | file | 2.47 | 1.1 |
|
||||
| imagemagick | file | 10.42 | 4.7 |
|
||||
| gm | buffer | 12.80 | 5.7 |
|
||||
| gm | file | 12.88 | 5.7 |
|
||||
| sharp | stream | 45.58 | 20.3 |
|
||||
| sharp | file | 47.99 | 21.4 |
|
||||
| sharp | buffer | 49.20 | 22.0 |
|
||||
| Module | Input | Output | Ops/sec | Speed-up |
|
||||
| :----------------- | :----- | :----- | ------: | -------: |
|
||||
| jimp | buffer | buffer | 2.13 | 1.0 |
|
||||
| imagemagick | file | file | 12.95 | 6.1 |
|
||||
| gm | buffer | buffer | 13.53 | 6.4 |
|
||||
| gm | file | file | 13.52 | 6.4 |
|
||||
| sharp | stream | stream | 46.58 | 21.9 |
|
||||
| sharp | file | file | 48.42 | 22.7 |
|
||||
| sharp | buffer | buffer | 50.16 | 23.6 |
|
||||
|
||||
### Task: PNG
|
||||
## Task: PNG
|
||||
|
||||
Decompress a 2048x1536 RGBA PNG image,
|
||||
premultiply the alpha channel,
|
||||
@ -106,25 +72,25 @@ and without adaptive filtering.
|
||||
|
||||
Note: jimp does not support premultiply/unpremultiply.
|
||||
|
||||
#### Results: PNG (AMD64)
|
||||
### Results: PNG (AMD64)
|
||||
|
||||
| Package | I/O | Ops/sec | Speed-up |
|
||||
| :---------- | :----- | ------: | -------: |
|
||||
| imagemagick | file | 6.06 | 1.0 |
|
||||
| gm | file | 8.44 | 1.4 |
|
||||
| jimp | buffer | 10.98 | 1.8 |
|
||||
| sharp | file | 28.26 | 4.7 |
|
||||
| sharp | buffer | 28.70 | 4.7 |
|
||||
| Module | Input | Output | Ops/sec | Speed-up |
|
||||
| :----------------- | :----- | :----- | ------: | -------: |
|
||||
| gm | file | file | 8.66 | 1.0 |
|
||||
| imagemagick | file | file | 8.79 | 1.0 |
|
||||
| jimp | buffer | buffer | 11.26 | 1.3 |
|
||||
| sharp | file | file | 27.93 | 3.2 |
|
||||
| sharp | buffer | buffer | 28.69 | 3.3 |
|
||||
|
||||
#### Results: PNG (ARM64)
|
||||
### Results: PNG (ARM64)
|
||||
|
||||
| Package | I/O | Ops/sec | Speed-up |
|
||||
| :---------- | :----- | ------: | -------: |
|
||||
| imagemagick | file | 7.09 | 1.0 |
|
||||
| gm | file | 8.93 | 1.3 |
|
||||
| jimp | buffer | 10.28 | 1.5 |
|
||||
| sharp | file | 23.81 | 3.4 |
|
||||
| sharp | buffer | 24.19 | 3.4 |
|
||||
| Module | Input | Output | Ops/sec | Speed-up |
|
||||
| :----------------- | :----- | :----- | ------: | -------: |
|
||||
| gm | file | file | 9.65 | 1.0 |
|
||||
| imagemagick | file | file | 9.72 | 1.0 |
|
||||
| jimp | buffer | buffer | 10.68 | 1.1 |
|
||||
| sharp | file | file | 23.90 | 2.5 |
|
||||
| sharp | buffer | buffer | 24.48 | 2.5 |
|
||||
|
||||
## Running the benchmark test
|
||||
|
||||
|
@ -153,6 +153,10 @@ const debuglog = util.debuglog('sharp');
|
||||
* @param {number} [options.ignoreIcc=false] - should the embedded ICC profile, if any, be ignored.
|
||||
* @param {number} [options.pages=1] - Number of pages to extract for multi-page input (GIF, WebP, TIFF), use -1 for all pages.
|
||||
* @param {number} [options.page=0] - Page number to start extracting from for multi-page input (GIF, WebP, TIFF), zero based.
|
||||
* @param {number} [options.subifd=-1] - subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image.
|
||||
* @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
|
||||
* @param {string|Object} [options.pdfBackground] - Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick.
|
||||
* @param {boolean} [options.jp2Oneshot=false] - Set to `true` to decode tiled JPEG 2000 images in a single operation, improving compatibility.
|
||||
* @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`.
|
||||
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||
* @param {number} [options.raw.width] - integral number of pixels wide.
|
||||
@ -160,17 +164,15 @@ const debuglog = util.debuglog('sharp');
|
||||
* @param {number} [options.raw.channels] - integral number of channels, between 1 and 4.
|
||||
* @param {boolean} [options.raw.premultiplied] - specifies that the raw input has already been premultiplied, set to `true`
|
||||
* to avoid sharp premultiplying the image. (optional, default `false`)
|
||||
* @param {number} [options.raw.pageHeight] - The pixel height of each page/frame for animated images, must be an integral factor of `raw.height`.
|
||||
* @param {Object} [options.create] - describes a new image to be created.
|
||||
* @param {number} [options.create.width] - integral number of pixels wide.
|
||||
* @param {number} [options.create.height] - integral number of pixels high.
|
||||
* @param {number} [options.create.channels] - integral number of channels, either 3 (RGB) or 4 (RGBA).
|
||||
* @param {string|Object} [options.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
||||
* @param {number} [options.create.pageHeight] - The pixel height of each page/frame for animated images, must be an integral factor of `create.height`.
|
||||
* @param {Object} [options.create.noise] - describes a noise to be created.
|
||||
* @param {string} [options.create.noise.type] - type of generated noise, currently only `gaussian` is supported.
|
||||
* @param {number} [options.create.noise.mean=128] - Mean value of pixels in the generated noise.
|
||||
* @param {number} [options.create.noise.sigma=30] - Standard deviation of pixel values in the generated noise.
|
||||
* @param {number} [options.create.noise.mean] - mean of pixels in generated noise.
|
||||
* @param {number} [options.create.noise.sigma] - standard deviation of pixels in generated noise.
|
||||
* @param {Object} [options.text] - describes a new text image to be created.
|
||||
* @param {string} [options.text.text] - text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`.
|
||||
* @param {string} [options.text.font] - font name to render with.
|
||||
@ -190,17 +192,7 @@ const debuglog = util.debuglog('sharp');
|
||||
* @param {string|Object} [options.join.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
||||
* @param {string} [options.join.halign='left'] - horizontal alignment style for images joined horizontally (`'left'`, `'centre'`, `'center'`, `'right'`).
|
||||
* @param {string} [options.join.valign='top'] - vertical alignment style for images joined vertically (`'top'`, `'centre'`, `'center'`, `'bottom'`).
|
||||
* @param {Object} [options.tiff] - Describes TIFF specific options.
|
||||
* @param {number} [options.tiff.subifd=-1] - Sub Image File Directory to extract for OME-TIFF, defaults to main image.
|
||||
* @param {Object} [options.svg] - Describes SVG specific options.
|
||||
* @param {string} [options.svg.stylesheet] - Custom CSS for SVG input, applied with a User Origin during the CSS cascade.
|
||||
* @param {boolean} [options.svg.highBitdepth=false] - Set to `true` to render SVG input at 32-bits per channel (128-bit) instead of 8-bits per channel (32-bit) RGBA.
|
||||
* @param {Object} [options.pdf] - Describes PDF specific options. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick.
|
||||
* @param {string|Object} [options.pdf.background] - Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
||||
* @param {Object} [options.openSlide] - Describes OpenSlide specific options. Requires the use of a globally-installed libvips compiled with support for OpenSlide.
|
||||
* @param {number} [options.openSlide.level=0] - Level to extract from a multi-level input, zero based.
|
||||
* @param {Object} [options.jp2] - Describes JPEG 2000 specific options. Requires the use of a globally-installed libvips compiled with support for OpenJPEG.
|
||||
* @param {boolean} [options.jp2.oneshot=false] - Set to `true` to decode tiled JPEG 2000 images in a single operation, improving compatibility.
|
||||
*
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
@ -306,7 +298,6 @@ const Sharp = function (input, options) {
|
||||
withIccProfile: '',
|
||||
withExif: {},
|
||||
withExifMerge: true,
|
||||
withXmp: '',
|
||||
resolveWithObject: false,
|
||||
loop: -1,
|
||||
delay: [],
|
||||
@ -347,7 +338,6 @@ const Sharp = function (input, options) {
|
||||
gifDither: 1,
|
||||
gifInterFrameMaxError: 0,
|
||||
gifInterPaletteMaxError: 3,
|
||||
gifKeepDuplicateFrames: false,
|
||||
gifReuse: true,
|
||||
gifProgressive: false,
|
||||
tiffQuality: 80,
|
||||
|
79
lib/index.d.ts
vendored
79
lib/index.d.ts
vendored
@ -730,20 +730,6 @@ declare namespace sharp {
|
||||
*/
|
||||
withIccProfile(icc: string, options?: WithIccProfileOptions): Sharp;
|
||||
|
||||
/**
|
||||
* Keep all XMP metadata from the input image in the output image.
|
||||
* @returns A sharp instance that can be used to chain operations
|
||||
*/
|
||||
keepXmp(): Sharp;
|
||||
|
||||
/**
|
||||
* Set XMP metadata in the output image.
|
||||
* @param {string} xmp - String containing XMP metadata to be embedded in the output image.
|
||||
* @returns A sharp instance that can be used to chain operations
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
withXmp(xmp: string): Sharp;
|
||||
|
||||
/**
|
||||
* Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
||||
* The default behaviour, when withMetadata is not used, is to strip all metadata and convert to the device-independent sRGB colour space.
|
||||
@ -1017,22 +1003,14 @@ declare namespace sharp {
|
||||
pages?: number | undefined;
|
||||
/** Page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based. (optional, default 0) */
|
||||
page?: number | undefined;
|
||||
/** TIFF specific input options */
|
||||
tiff?: TiffInputOptions | undefined;
|
||||
/** SVG specific input options */
|
||||
svg?: SvgInputOptions | undefined;
|
||||
/** PDF specific input options */
|
||||
pdf?: PdfInputOptions | undefined;
|
||||
/** OpenSlide specific input options */
|
||||
openSlide?: OpenSlideInputOptions | undefined;
|
||||
/** JPEG 2000 specific input options */
|
||||
jp2?: Jp2InputOptions | undefined;
|
||||
/** Deprecated: use tiff.subifd instead */
|
||||
/** subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. (optional, default -1) */
|
||||
subifd?: number | undefined;
|
||||
/** Deprecated: use pdf.background instead */
|
||||
pdfBackground?: Colour | Color | undefined;
|
||||
/** Deprecated: use openSlide.level instead */
|
||||
/** Level to extract from a multi-level input (OpenSlide), zero based. (optional, default 0) */
|
||||
level?: number | undefined;
|
||||
/** Background colour to use when PDF is partially transparent. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. */
|
||||
pdfBackground?: Colour | Color | undefined;
|
||||
/** Set to `true` to load JPEG 2000 images using [oneshot mode](https://github.com/libvips/libvips/issues/4205) */
|
||||
jp2Oneshot?: boolean | undefined;
|
||||
/** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default false) */
|
||||
animated?: boolean | undefined;
|
||||
/** Describes raw pixel input image data. See raw() for pixel ordering. */
|
||||
@ -1075,8 +1053,6 @@ declare namespace sharp {
|
||||
interface CreateRaw extends Raw {
|
||||
/** Specifies that the raw input has already been premultiplied, set to true to avoid sharp premultiplying the image. (optional, default false) */
|
||||
premultiplied?: boolean | undefined;
|
||||
/** The height of each page/frame for animated images, must be an integral factor of the overall image height. */
|
||||
pageHeight?: number | undefined;
|
||||
}
|
||||
|
||||
type CreateChannels = 3 | 4;
|
||||
@ -1092,9 +1068,6 @@ declare namespace sharp {
|
||||
background: Colour | Color;
|
||||
/** Describes a noise to be created. */
|
||||
noise?: Noise | undefined;
|
||||
/** The height of each page/frame for animated images, must be an integral factor of the overall image height. */
|
||||
pageHeight?: number | undefined;
|
||||
|
||||
}
|
||||
|
||||
interface CreateText {
|
||||
@ -1143,33 +1116,6 @@ declare namespace sharp {
|
||||
valign?: VerticalAlignment | undefined;
|
||||
}
|
||||
|
||||
interface TiffInputOptions {
|
||||
/** Sub Image File Directory to extract, defaults to main image. Use -1 for all subifds. */
|
||||
subifd?: number | undefined;
|
||||
}
|
||||
|
||||
interface SvgInputOptions {
|
||||
/** Custom CSS for SVG input, applied with a User Origin during the CSS cascade. */
|
||||
stylesheet?: string | undefined;
|
||||
/** Set to `true` to render SVG input at 32-bits per channel (128-bit) instead of 8-bits per channel (32-bit) RGBA. */
|
||||
highBitdepth?: boolean | undefined;
|
||||
}
|
||||
|
||||
interface PdfInputOptions {
|
||||
/** Background colour to use when PDF is partially transparent. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. */
|
||||
background?: Colour | Color | undefined;
|
||||
}
|
||||
|
||||
interface OpenSlideInputOptions {
|
||||
/** Level to extract from a multi-level input, zero based. (optional, default 0) */
|
||||
level?: number | undefined;
|
||||
}
|
||||
|
||||
interface Jp2InputOptions {
|
||||
/** Set to `true` to load JPEG 2000 images using [oneshot mode](https://github.com/libvips/libvips/issues/4205) */
|
||||
oneshot?: boolean | undefined;
|
||||
}
|
||||
|
||||
interface ExifDir {
|
||||
[k: string]: string;
|
||||
}
|
||||
@ -1254,8 +1200,6 @@ declare namespace sharp {
|
||||
iptc?: Buffer | undefined;
|
||||
/** Buffer containing raw XMP data, if present */
|
||||
xmp?: Buffer | undefined;
|
||||
/** String containing XMP data, if valid UTF-8 */
|
||||
xmpAsString?: string | undefined;
|
||||
/** Buffer containing raw TIFFTAG_PHOTOSHOP data, if present */
|
||||
tifftagPhotoshop?: Buffer | undefined;
|
||||
/** The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC) */
|
||||
@ -1448,11 +1392,9 @@ declare namespace sharp {
|
||||
/** Level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most) (optional, default 1.0) */
|
||||
dither?: number | undefined;
|
||||
/** Maximum inter-frame error for transparency, between 0 (lossless) and 32 (optional, default 0) */
|
||||
interFrameMaxError?: number | undefined;
|
||||
interFrameMaxError?: number;
|
||||
/** Maximum inter-palette error for palette reuse, between 0 and 256 (optional, default 3) */
|
||||
interPaletteMaxError?: number | undefined;
|
||||
/** Keep duplicate frames in the output instead of combining them (optional, default false) */
|
||||
keepDuplicateFrames?: boolean | undefined;
|
||||
interPaletteMaxError?: number;
|
||||
}
|
||||
|
||||
interface TiffOptions extends OutputOptions {
|
||||
@ -1570,7 +1512,7 @@ declare namespace sharp {
|
||||
|
||||
interface Noise {
|
||||
/** type of generated noise, currently only gaussian is supported. */
|
||||
type: 'gaussian';
|
||||
type?: 'gaussian' | undefined;
|
||||
/** mean of pixels in generated noise. */
|
||||
mean?: number | undefined;
|
||||
/** standard deviation of pixels in generated noise. */
|
||||
@ -1787,8 +1729,6 @@ declare namespace sharp {
|
||||
mitchell: 'mitchell';
|
||||
lanczos2: 'lanczos2';
|
||||
lanczos3: 'lanczos3';
|
||||
mks2013: 'mks2013';
|
||||
mks2021: 'mks2021';
|
||||
}
|
||||
|
||||
interface PresetEnum {
|
||||
@ -1906,7 +1846,6 @@ declare namespace sharp {
|
||||
|
||||
interface FormatEnum {
|
||||
avif: AvailableFormatInfo;
|
||||
dcraw: AvailableFormatInfo;
|
||||
dz: AvailableFormatInfo;
|
||||
exr: AvailableFormatInfo;
|
||||
fits: AvailableFormatInfo;
|
||||
|
122
lib/input.js
122
lib/input.js
@ -22,27 +22,14 @@ const align = {
|
||||
high: 'high'
|
||||
};
|
||||
|
||||
const inputStreamParameters = [
|
||||
// Limits and error handling
|
||||
'failOn', 'limitInputPixels', 'unlimited',
|
||||
// Format-generic
|
||||
'animated', 'autoOrient', 'density', 'ignoreIcc', 'page', 'pages', 'sequentialRead',
|
||||
// Format-specific
|
||||
'jp2', 'openSlide', 'pdf', 'raw', 'svg', 'tiff',
|
||||
// Deprecated
|
||||
'failOnError', 'openSlideLevel', 'pdfBackground', 'tiffSubifd'
|
||||
];
|
||||
|
||||
/**
|
||||
* Extract input options, if any, from an object.
|
||||
* @private
|
||||
*/
|
||||
function _inputOptionsFromObject (obj) {
|
||||
const params = inputStreamParameters
|
||||
.filter(p => is.defined(obj[p]))
|
||||
.map(p => ([p, obj[p]]));
|
||||
return params.length
|
||||
? Object.fromEntries(params)
|
||||
const { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient, jp2Oneshot } = obj;
|
||||
return [raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient, jp2Oneshot].some(is.defined)
|
||||
? { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient, jp2Oneshot }
|
||||
: undefined;
|
||||
}
|
||||
|
||||
@ -185,6 +172,8 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
inputDescriptor.rawWidth = inputOptions.raw.width;
|
||||
inputDescriptor.rawHeight = inputOptions.raw.height;
|
||||
inputDescriptor.rawChannels = inputOptions.raw.channels;
|
||||
inputDescriptor.rawPremultiplied = !!inputOptions.raw.premultiplied;
|
||||
|
||||
switch (input.constructor) {
|
||||
case Uint8Array:
|
||||
case Uint8ClampedArray:
|
||||
@ -218,25 +207,6 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
} else {
|
||||
throw new Error('Expected width, height and channels for raw pixel input');
|
||||
}
|
||||
inputDescriptor.rawPremultiplied = false;
|
||||
if (is.defined(inputOptions.raw.premultiplied)) {
|
||||
if (is.bool(inputOptions.raw.premultiplied)) {
|
||||
inputDescriptor.rawPremultiplied = inputOptions.raw.premultiplied;
|
||||
} else {
|
||||
throw is.invalidParameterError('raw.premultiplied', 'boolean', inputOptions.raw.premultiplied);
|
||||
}
|
||||
}
|
||||
inputDescriptor.rawPageHeight = 0;
|
||||
if (is.defined(inputOptions.raw.pageHeight)) {
|
||||
if (is.integer(inputOptions.raw.pageHeight) && inputOptions.raw.pageHeight > 0 && inputOptions.raw.pageHeight <= inputOptions.raw.height) {
|
||||
if (inputOptions.raw.height % inputOptions.raw.pageHeight !== 0) {
|
||||
throw new Error(`Expected raw.height ${inputOptions.raw.height} to be a multiple of raw.pageHeight ${inputOptions.raw.pageHeight}`);
|
||||
}
|
||||
inputDescriptor.rawPageHeight = inputOptions.raw.pageHeight;
|
||||
} else {
|
||||
throw is.invalidParameterError('raw.pageHeight', 'positive integer', inputOptions.raw.pageHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Multi-page input (GIF, TIFF, PDF)
|
||||
if (is.defined(inputOptions.animated)) {
|
||||
@ -260,66 +230,32 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
throw is.invalidParameterError('page', 'integer between 0 and 100000', inputOptions.page);
|
||||
}
|
||||
}
|
||||
// OpenSlide specific options
|
||||
if (is.object(inputOptions.openSlide) && is.defined(inputOptions.openSlide.level)) {
|
||||
if (is.integer(inputOptions.openSlide.level) && is.inRange(inputOptions.openSlide.level, 0, 256)) {
|
||||
inputDescriptor.openSlideLevel = inputOptions.openSlide.level;
|
||||
} else {
|
||||
throw is.invalidParameterError('openSlide.level', 'integer between 0 and 256', inputOptions.openSlide.level);
|
||||
}
|
||||
} else if (is.defined(inputOptions.level)) {
|
||||
// Deprecated
|
||||
// Multi-level input (OpenSlide)
|
||||
if (is.defined(inputOptions.level)) {
|
||||
if (is.integer(inputOptions.level) && is.inRange(inputOptions.level, 0, 256)) {
|
||||
inputDescriptor.openSlideLevel = inputOptions.level;
|
||||
inputDescriptor.level = inputOptions.level;
|
||||
} else {
|
||||
throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level);
|
||||
}
|
||||
}
|
||||
// TIFF specific options
|
||||
if (is.object(inputOptions.tiff) && is.defined(inputOptions.tiff.subifd)) {
|
||||
if (is.integer(inputOptions.tiff.subifd) && is.inRange(inputOptions.tiff.subifd, -1, 100000)) {
|
||||
inputDescriptor.tiffSubifd = inputOptions.tiff.subifd;
|
||||
} else {
|
||||
throw is.invalidParameterError('tiff.subifd', 'integer between -1 and 100000', inputOptions.tiff.subifd);
|
||||
}
|
||||
} else if (is.defined(inputOptions.subifd)) {
|
||||
// Deprecated
|
||||
// Sub Image File Directory (TIFF)
|
||||
if (is.defined(inputOptions.subifd)) {
|
||||
if (is.integer(inputOptions.subifd) && is.inRange(inputOptions.subifd, -1, 100000)) {
|
||||
inputDescriptor.tiffSubifd = inputOptions.subifd;
|
||||
inputDescriptor.subifd = inputOptions.subifd;
|
||||
} else {
|
||||
throw is.invalidParameterError('subifd', 'integer between -1 and 100000', inputOptions.subifd);
|
||||
}
|
||||
}
|
||||
// SVG specific options
|
||||
if (is.object(inputOptions.svg)) {
|
||||
if (is.defined(inputOptions.svg.stylesheet)) {
|
||||
if (is.string(inputOptions.svg.stylesheet)) {
|
||||
inputDescriptor.svgStylesheet = inputOptions.svg.stylesheet;
|
||||
} else {
|
||||
throw is.invalidParameterError('svg.stylesheet', 'string', inputOptions.svg.stylesheet);
|
||||
}
|
||||
}
|
||||
if (is.defined(inputOptions.svg.highBitdepth)) {
|
||||
if (is.bool(inputOptions.svg.highBitdepth)) {
|
||||
inputDescriptor.svgHighBitdepth = inputOptions.svg.highBitdepth;
|
||||
} else {
|
||||
throw is.invalidParameterError('svg.highBitdepth', 'boolean', inputOptions.svg.highBitdepth);
|
||||
}
|
||||
}
|
||||
}
|
||||
// PDF specific options
|
||||
if (is.object(inputOptions.pdf) && is.defined(inputOptions.pdf.background)) {
|
||||
inputDescriptor.pdfBackground = this._getBackgroundColourOption(inputOptions.pdf.background);
|
||||
} else if (is.defined(inputOptions.pdfBackground)) {
|
||||
// Deprecated
|
||||
// PDF background colour
|
||||
if (is.defined(inputOptions.pdfBackground)) {
|
||||
inputDescriptor.pdfBackground = this._getBackgroundColourOption(inputOptions.pdfBackground);
|
||||
}
|
||||
// JPEG 2000 specific options
|
||||
if (is.object(inputOptions.jp2) && is.defined(inputOptions.jp2.oneshot)) {
|
||||
if (is.bool(inputOptions.jp2.oneshot)) {
|
||||
inputDescriptor.jp2Oneshot = inputOptions.jp2.oneshot;
|
||||
// JP2 oneshot
|
||||
if (is.defined(inputOptions.jp2Oneshot)) {
|
||||
if (is.bool(inputOptions.jp2Oneshot)) {
|
||||
inputDescriptor.jp2Oneshot = inputOptions.jp2Oneshot;
|
||||
} else {
|
||||
throw is.invalidParameterError('jp2.oneshot', 'boolean', inputOptions.jp2.oneshot);
|
||||
throw is.invalidParameterError('jp2Oneshot', 'boolean', inputOptions.jp2Oneshot);
|
||||
}
|
||||
}
|
||||
// Create new image
|
||||
@ -333,45 +269,28 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
inputDescriptor.createWidth = inputOptions.create.width;
|
||||
inputDescriptor.createHeight = inputOptions.create.height;
|
||||
inputDescriptor.createChannels = inputOptions.create.channels;
|
||||
inputDescriptor.createPageHeight = 0;
|
||||
if (is.defined(inputOptions.create.pageHeight)) {
|
||||
if (is.integer(inputOptions.create.pageHeight) && inputOptions.create.pageHeight > 0 && inputOptions.create.pageHeight <= inputOptions.create.height) {
|
||||
if (inputOptions.create.height % inputOptions.create.pageHeight !== 0) {
|
||||
throw new Error(`Expected create.height ${inputOptions.create.height} to be a multiple of create.pageHeight ${inputOptions.create.pageHeight}`);
|
||||
}
|
||||
inputDescriptor.createPageHeight = inputOptions.create.pageHeight;
|
||||
} else {
|
||||
throw is.invalidParameterError('create.pageHeight', 'positive integer', inputOptions.create.pageHeight);
|
||||
}
|
||||
}
|
||||
// Noise
|
||||
if (is.defined(inputOptions.create.noise)) {
|
||||
if (!is.object(inputOptions.create.noise)) {
|
||||
throw new Error('Expected noise to be an object');
|
||||
}
|
||||
if (inputOptions.create.noise.type !== 'gaussian') {
|
||||
if (!is.inArray(inputOptions.create.noise.type, ['gaussian'])) {
|
||||
throw new Error('Only gaussian noise is supported at the moment');
|
||||
}
|
||||
inputDescriptor.createNoiseType = inputOptions.create.noise.type;
|
||||
if (!is.inRange(inputOptions.create.channels, 1, 4)) {
|
||||
throw is.invalidParameterError('create.channels', 'number between 1 and 4', inputOptions.create.channels);
|
||||
}
|
||||
inputDescriptor.createNoiseMean = 128;
|
||||
if (is.defined(inputOptions.create.noise.mean)) {
|
||||
inputDescriptor.createNoiseType = inputOptions.create.noise.type;
|
||||
if (is.number(inputOptions.create.noise.mean) && is.inRange(inputOptions.create.noise.mean, 0, 10000)) {
|
||||
inputDescriptor.createNoiseMean = inputOptions.create.noise.mean;
|
||||
} else {
|
||||
throw is.invalidParameterError('create.noise.mean', 'number between 0 and 10000', inputOptions.create.noise.mean);
|
||||
}
|
||||
}
|
||||
inputDescriptor.createNoiseSigma = 30;
|
||||
if (is.defined(inputOptions.create.noise.sigma)) {
|
||||
if (is.number(inputOptions.create.noise.sigma) && is.inRange(inputOptions.create.noise.sigma, 0, 10000)) {
|
||||
inputDescriptor.createNoiseSigma = inputOptions.create.noise.sigma;
|
||||
} else {
|
||||
throw is.invalidParameterError('create.noise.sigma', 'number between 0 and 10000', inputOptions.create.noise.sigma);
|
||||
}
|
||||
}
|
||||
} else if (is.defined(inputOptions.create.background)) {
|
||||
if (!is.inRange(inputOptions.create.channels, 3, 4)) {
|
||||
throw is.invalidParameterError('create.channels', 'number between 3 and 4', inputOptions.create.channels);
|
||||
@ -605,7 +524,6 @@ function _isStreamInput () {
|
||||
* - `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
|
||||
* - `iptc`: Buffer containing raw IPTC data, if present
|
||||
* - `xmp`: Buffer containing raw XMP data, if present
|
||||
* - `xmpAsString`: String containing XMP data, if valid UTF-8.
|
||||
* - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
|
||||
* - `formatMagick`: String containing format for images loaded via *magick
|
||||
* - `comments`: Array of keyword/text pairs representing PNG text blocks, if present.
|
||||
|
@ -312,59 +312,6 @@ function withIccProfile (icc, options) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep XMP metadata from the input image in the output image.
|
||||
*
|
||||
* @since 0.34.3
|
||||
*
|
||||
* @example
|
||||
* const outputWithXmp = await sharp(inputWithXmp)
|
||||
* .keepXmp()
|
||||
* .toBuffer();
|
||||
*
|
||||
* @returns {Sharp}
|
||||
*/
|
||||
function keepXmp () {
|
||||
this.options.keepMetadata |= 0b00010;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set XMP metadata in the output image.
|
||||
*
|
||||
* Supported by PNG, JPEG, WebP, and TIFF output.
|
||||
*
|
||||
* @since 0.34.3
|
||||
*
|
||||
* @example
|
||||
* const xmpString = `
|
||||
* <?xml version="1.0"?>
|
||||
* <x:xmpmeta xmlns:x="adobe:ns:meta/">
|
||||
* <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
* <rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
* <dc:creator><rdf:Seq><rdf:li>John Doe</rdf:li></rdf:Seq></dc:creator>
|
||||
* </rdf:Description>
|
||||
* </rdf:RDF>
|
||||
* </x:xmpmeta>`;
|
||||
*
|
||||
* const data = await sharp(input)
|
||||
* .withXmp(xmpString)
|
||||
* .toBuffer();
|
||||
*
|
||||
* @param {string} xmp String containing XMP metadata to be embedded in the output image.
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
function withXmp (xmp) {
|
||||
if (is.string(xmp) && xmp.length > 0) {
|
||||
this.options.withXmp = xmp;
|
||||
this.options.keepMetadata |= 0b00010;
|
||||
} else {
|
||||
throw is.invalidParameterError('xmp', 'non-empty string', xmp);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep all metadata (EXIF, ICC, XMP, IPTC) from the input image in the output image.
|
||||
*
|
||||
@ -782,7 +729,6 @@ function webp (options) {
|
||||
* @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most)
|
||||
* @param {number} [options.interFrameMaxError=0] - maximum inter-frame error for transparency, between 0 (lossless) and 32
|
||||
* @param {number} [options.interPaletteMaxError=3] - maximum inter-palette error for palette reuse, between 0 and 256
|
||||
* @param {boolean} [options.keepDuplicateFrames=false] - keep duplicate frames in the output instead of combining them
|
||||
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
|
||||
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
|
||||
* @param {boolean} [options.force=true] - force GIF output, otherwise attempt to use input format
|
||||
@ -833,13 +779,6 @@ function gif (options) {
|
||||
throw is.invalidParameterError('interPaletteMaxError', 'number between 0.0 and 256.0', options.interPaletteMaxError);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.keepDuplicateFrames)) {
|
||||
if (is.bool(options.keepDuplicateFrames)) {
|
||||
this._setBooleanOption('gifKeepDuplicateFrames', options.keepDuplicateFrames);
|
||||
} else {
|
||||
throw is.invalidParameterError('keepDuplicateFrames', 'boolean', options.keepDuplicateFrames);
|
||||
}
|
||||
}
|
||||
}
|
||||
trySetAnimationOptions(options, this.options);
|
||||
return this._updateFormatOut('gif', options);
|
||||
@ -1629,8 +1568,6 @@ module.exports = function (Sharp) {
|
||||
withExifMerge,
|
||||
keepIccProfile,
|
||||
withIccProfile,
|
||||
keepXmp,
|
||||
withXmp,
|
||||
keepMetadata,
|
||||
withMetadata,
|
||||
toFormat,
|
||||
|
@ -150,8 +150,6 @@ function isResizeExpected (options) {
|
||||
* - `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
|
||||
* - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
|
||||
* - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
|
||||
* - `mks2013`: Use a [Magic Kernel Sharp](https://johncostella.com/magic/mks.pdf) 2013 kernel, as adopted by Facebook.
|
||||
* - `mks2021`: Use a Magic Kernel Sharp 2021 kernel, with more accurate (reduced) sharpening than the 2013 version.
|
||||
*
|
||||
* When upsampling, these kernels map to `nearest`, `linear` and `cubic` interpolators.
|
||||
* Downsampling kernels without a matching upsampling interpolator map to `cubic`.
|
||||
|
@ -135,9 +135,15 @@ cache(true);
|
||||
* e.g. libaom manages its own 4 threads when encoding AVIF images,
|
||||
* and these are independent of the value set here.
|
||||
*
|
||||
* :::note
|
||||
* Further {@link /performance|control over performance} is available.
|
||||
* :::
|
||||
* The maximum number of images that sharp can process in parallel
|
||||
* is controlled by libuv's `UV_THREADPOOL_SIZE` environment variable,
|
||||
* which defaults to 4.
|
||||
*
|
||||
* https://nodejs.org/api/cli.html#uv_threadpool_sizesize
|
||||
*
|
||||
* For example, by default, a machine with 8 CPU cores will process
|
||||
* 4 images in parallel and use up to 8 threads per image,
|
||||
* so there will be up to 32 concurrent threads.
|
||||
*
|
||||
* @example
|
||||
* const threads = sharp.concurrency(); // 4
|
||||
|
@ -15,7 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.0"
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
|
@ -15,7 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.0"
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
|
@ -15,7 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.2.0"
|
||||
"@img/sharp-libvips-linux-arm": "1.2.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
|
@ -15,7 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.0"
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
|
@ -15,7 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.0"
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
|
@ -15,7 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.0"
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
|
@ -15,7 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.2.0"
|
||||
"@img/sharp-libvips-linux-x64": "1.2.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
|
@ -15,7 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.0"
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
|
@ -15,7 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.0"
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
|
@ -31,7 +31,7 @@
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.4.4"
|
||||
"@emnapi/runtime": "^1.4.3"
|
||||
},
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
|
40
package.json
40
package.json
@ -143,15 +143,15 @@
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.34.3-rc.0",
|
||||
"@img/sharp-darwin-x64": "0.34.3-rc.0",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.0",
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.0",
|
||||
"@img/sharp-libvips-linux-arm": "1.2.0",
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.0",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.0",
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.0",
|
||||
"@img/sharp-libvips-linux-x64": "1.2.0",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.0",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.0",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.0-rc.2",
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.0-rc.2",
|
||||
"@img/sharp-libvips-linux-arm": "1.2.0-rc.2",
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.0-rc.2",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.0-rc.2",
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.0-rc.2",
|
||||
"@img/sharp-libvips-linux-x64": "1.2.0-rc.2",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.0-rc.2",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.0-rc.2",
|
||||
"@img/sharp-linux-arm": "0.34.3-rc.0",
|
||||
"@img/sharp-linux-arm64": "0.34.3-rc.0",
|
||||
"@img/sharp-linux-ppc64": "0.34.3-rc.0",
|
||||
@ -165,26 +165,26 @@
|
||||
"@img/sharp-win32-x64": "0.34.3-rc.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@emnapi/runtime": "^1.4.4",
|
||||
"@img/sharp-libvips-dev": "1.2.0",
|
||||
"@img/sharp-libvips-dev-wasm32": "1.2.0",
|
||||
"@img/sharp-libvips-win32-arm64": "1.2.0",
|
||||
"@img/sharp-libvips-win32-ia32": "1.2.0",
|
||||
"@img/sharp-libvips-win32-x64": "1.2.0",
|
||||
"@emnapi/runtime": "^1.4.3",
|
||||
"@img/sharp-libvips-dev": "1.2.0-rc.2",
|
||||
"@img/sharp-libvips-dev-wasm32": "1.2.0-rc.2",
|
||||
"@img/sharp-libvips-win32-arm64": "1.2.0-rc.2",
|
||||
"@img/sharp-libvips-win32-ia32": "1.2.0-rc.2",
|
||||
"@img/sharp-libvips-win32-x64": "1.2.0-rc.2",
|
||||
"@types/node": "*",
|
||||
"cc": "^3.0.1",
|
||||
"emnapi": "^1.4.4",
|
||||
"emnapi": "^1.4.3",
|
||||
"exif-reader": "^2.0.2",
|
||||
"extract-zip": "^2.0.1",
|
||||
"icc": "^3.0.0",
|
||||
"jsdoc-to-markdown": "^9.1.1",
|
||||
"license-checker": "^25.0.1",
|
||||
"mocha": "^11.7.1",
|
||||
"node-addon-api": "^8.4.0",
|
||||
"mocha": "^11.6.0",
|
||||
"node-addon-api": "^8.3.1",
|
||||
"node-gyp": "^11.2.0",
|
||||
"nyc": "^17.1.0",
|
||||
"semistandard": "^17.0.0",
|
||||
"tar-fs": "^3.1.0",
|
||||
"tar-fs": "^3.0.9",
|
||||
"tsd": "^0.32.0"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
@ -192,7 +192,7 @@
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"config": {
|
||||
"libvips": ">=8.17.1"
|
||||
"libvips": ">=8.17.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
|
126
src/common.cc
126
src/common.cc
@ -93,7 +93,6 @@ namespace sharp {
|
||||
descriptor->rawWidth = AttrAsUint32(input, "rawWidth");
|
||||
descriptor->rawHeight = AttrAsUint32(input, "rawHeight");
|
||||
descriptor->rawPremultiplied = AttrAsBool(input, "rawPremultiplied");
|
||||
descriptor->rawPageHeight = AttrAsUint32(input, "rawPageHeight");
|
||||
}
|
||||
// Multi-page input (GIF, TIFF, PDF)
|
||||
if (HasAttr(input, "pages")) {
|
||||
@ -102,20 +101,13 @@ namespace sharp {
|
||||
if (HasAttr(input, "page")) {
|
||||
descriptor->page = AttrAsUint32(input, "page");
|
||||
}
|
||||
// SVG
|
||||
if (HasAttr(input, "svgStylesheet")) {
|
||||
descriptor->svgStylesheet = AttrAsStr(input, "svgStylesheet");
|
||||
}
|
||||
if (HasAttr(input, "svgHighBitdepth")) {
|
||||
descriptor->svgHighBitdepth = AttrAsBool(input, "svgHighBitdepth");
|
||||
}
|
||||
// Multi-level input (OpenSlide)
|
||||
if (HasAttr(input, "openSlideLevel")) {
|
||||
descriptor->openSlideLevel = AttrAsUint32(input, "openSlideLevel");
|
||||
if (HasAttr(input, "level")) {
|
||||
descriptor->level = AttrAsUint32(input, "level");
|
||||
}
|
||||
// subIFD (OME-TIFF)
|
||||
if (HasAttr(input, "subifd")) {
|
||||
descriptor->tiffSubifd = AttrAsInt32(input, "tiffSubifd");
|
||||
descriptor->subifd = AttrAsInt32(input, "subifd");
|
||||
}
|
||||
// // PDF background color
|
||||
if (HasAttr(input, "pdfBackground")) {
|
||||
@ -130,7 +122,6 @@ namespace sharp {
|
||||
descriptor->createChannels = AttrAsUint32(input, "createChannels");
|
||||
descriptor->createWidth = AttrAsUint32(input, "createWidth");
|
||||
descriptor->createHeight = AttrAsUint32(input, "createHeight");
|
||||
descriptor->createPageHeight = AttrAsUint32(input, "createPageHeight");
|
||||
if (HasAttr(input, "createNoiseType")) {
|
||||
descriptor->createNoiseType = AttrAsStr(input, "createNoiseType");
|
||||
descriptor->createNoiseMean = AttrAsDouble(input, "createNoiseMean");
|
||||
@ -284,7 +275,6 @@ namespace sharp {
|
||||
case ImageType::EXR: id = "exr"; break;
|
||||
case ImageType::JXL: id = "jxl"; break;
|
||||
case ImageType::RAD: id = "rad"; break;
|
||||
case ImageType::DCRAW: id = "dcraw"; break;
|
||||
case ImageType::VIPS: id = "vips"; break;
|
||||
case ImageType::RAW: id = "raw"; break;
|
||||
case ImageType::UNKNOWN: id = "unknown"; break;
|
||||
@ -333,8 +323,6 @@ namespace sharp {
|
||||
{ "VipsForeignLoadJxlBuffer", ImageType::JXL },
|
||||
{ "VipsForeignLoadRadFile", ImageType::RAD },
|
||||
{ "VipsForeignLoadRadBuffer", ImageType::RAD },
|
||||
{ "VipsForeignLoadDcRawFile", ImageType::DCRAW },
|
||||
{ "VipsForeignLoadDcRawBuffer", ImageType::DCRAW },
|
||||
{ "VipsForeignLoadVips", ImageType::VIPS },
|
||||
{ "VipsForeignLoadVipsFile", ImageType::VIPS },
|
||||
{ "VipsForeignLoadRaw", ImageType::RAW }
|
||||
@ -399,48 +387,6 @@ namespace sharp {
|
||||
imageType == ImageType::HEIF;
|
||||
}
|
||||
|
||||
/*
|
||||
Format-specific options builder
|
||||
*/
|
||||
vips::VOption* GetOptionsForImageType(ImageType imageType, InputDescriptor *descriptor) {
|
||||
vips::VOption *option = VImage::option()
|
||||
->set("access", descriptor->access)
|
||||
->set("fail_on", descriptor->failOn);
|
||||
if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
|
||||
option->set("unlimited", true);
|
||||
}
|
||||
if (ImageTypeSupportsPage(imageType)) {
|
||||
option->set("n", descriptor->pages);
|
||||
option->set("page", descriptor->page);
|
||||
}
|
||||
switch (imageType) {
|
||||
case ImageType::SVG:
|
||||
option->set("dpi", descriptor->density)
|
||||
->set("stylesheet", descriptor->svgStylesheet.data())
|
||||
->set("high_bitdepth", descriptor->svgHighBitdepth);
|
||||
break;
|
||||
case ImageType::TIFF:
|
||||
option->set("tiffSubifd", descriptor->tiffSubifd);
|
||||
break;
|
||||
case ImageType::PDF:
|
||||
option->set("dpi", descriptor->density)
|
||||
->set("background", descriptor->pdfBackground);
|
||||
break;
|
||||
case ImageType::OPENSLIDE:
|
||||
option->set("openSlideLevel", descriptor->openSlideLevel);
|
||||
break;
|
||||
case ImageType::JP2:
|
||||
option->set("oneshot", descriptor->jp2Oneshot);
|
||||
break;
|
||||
case ImageType::MAGICK:
|
||||
option->set("density", std::to_string(descriptor->density).data());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return option;
|
||||
}
|
||||
|
||||
/*
|
||||
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
|
||||
*/
|
||||
@ -458,10 +404,6 @@ namespace sharp {
|
||||
} else {
|
||||
image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_RGB16;
|
||||
}
|
||||
if (descriptor->rawPageHeight > 0) {
|
||||
image.set(VIPS_META_PAGE_HEIGHT, descriptor->rawPageHeight);
|
||||
image.set(VIPS_META_N_PAGES, static_cast<int>(descriptor->rawHeight / descriptor->rawPageHeight));
|
||||
}
|
||||
if (descriptor->rawPremultiplied) {
|
||||
image = image.unpremultiply();
|
||||
}
|
||||
@ -471,7 +413,34 @@ namespace sharp {
|
||||
imageType = DetermineImageType(descriptor->buffer, descriptor->bufferLength);
|
||||
if (imageType != ImageType::UNKNOWN) {
|
||||
try {
|
||||
vips::VOption *option = GetOptionsForImageType(imageType, descriptor);
|
||||
vips::VOption *option = VImage::option()
|
||||
->set("access", descriptor->access)
|
||||
->set("fail_on", descriptor->failOn);
|
||||
if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
|
||||
option->set("unlimited", true);
|
||||
}
|
||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
|
||||
option->set("dpi", descriptor->density);
|
||||
}
|
||||
if (imageType == ImageType::MAGICK) {
|
||||
option->set("density", std::to_string(descriptor->density).data());
|
||||
}
|
||||
if (ImageTypeSupportsPage(imageType)) {
|
||||
option->set("n", descriptor->pages);
|
||||
option->set("page", descriptor->page);
|
||||
}
|
||||
if (imageType == ImageType::OPENSLIDE) {
|
||||
option->set("level", descriptor->level);
|
||||
}
|
||||
if (imageType == ImageType::TIFF) {
|
||||
option->set("subifd", descriptor->subifd);
|
||||
}
|
||||
if (imageType == ImageType::PDF) {
|
||||
option->set("background", descriptor->pdfBackground);
|
||||
}
|
||||
if (imageType == ImageType::JP2) {
|
||||
option->set("oneshot", descriptor->jp2Oneshot);
|
||||
}
|
||||
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
|
||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||
image = SetDensity(image, descriptor->density);
|
||||
@ -511,10 +480,6 @@ namespace sharp {
|
||||
channels < 3 ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_sRGB))
|
||||
.new_from_image(background);
|
||||
}
|
||||
if (descriptor->createPageHeight > 0) {
|
||||
image.set(VIPS_META_PAGE_HEIGHT, descriptor->createPageHeight);
|
||||
image.set(VIPS_META_N_PAGES, static_cast<int>(descriptor->createHeight / descriptor->createPageHeight));
|
||||
}
|
||||
image = image.cast(VIPS_FORMAT_UCHAR);
|
||||
imageType = ImageType::RAW;
|
||||
} else if (descriptor->textValue.length() > 0) {
|
||||
@ -558,7 +523,34 @@ namespace sharp {
|
||||
}
|
||||
if (imageType != ImageType::UNKNOWN) {
|
||||
try {
|
||||
vips::VOption *option = GetOptionsForImageType(imageType, descriptor);
|
||||
vips::VOption *option = VImage::option()
|
||||
->set("access", descriptor->access)
|
||||
->set("fail_on", descriptor->failOn);
|
||||
if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
|
||||
option->set("unlimited", true);
|
||||
}
|
||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
|
||||
option->set("dpi", descriptor->density);
|
||||
}
|
||||
if (imageType == ImageType::MAGICK) {
|
||||
option->set("density", std::to_string(descriptor->density).data());
|
||||
}
|
||||
if (ImageTypeSupportsPage(imageType)) {
|
||||
option->set("n", descriptor->pages);
|
||||
option->set("page", descriptor->page);
|
||||
}
|
||||
if (imageType == ImageType::OPENSLIDE) {
|
||||
option->set("level", descriptor->level);
|
||||
}
|
||||
if (imageType == ImageType::TIFF) {
|
||||
option->set("subifd", descriptor->subifd);
|
||||
}
|
||||
if (imageType == ImageType::PDF) {
|
||||
option->set("background", descriptor->pdfBackground);
|
||||
}
|
||||
if (imageType == ImageType::JP2) {
|
||||
option->set("oneshot", descriptor->jp2Oneshot);
|
||||
}
|
||||
image = VImage::new_from_file(descriptor->file.data(), option);
|
||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||
image = SetDensity(image, descriptor->density);
|
||||
|
29
src/common.h
29
src/common.h
@ -16,8 +16,8 @@
|
||||
|
||||
#if (VIPS_MAJOR_VERSION < 8) || \
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 17) || \
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 17 && VIPS_MICRO_VERSION < 1)
|
||||
#error "libvips version 8.17.1+ is required - please see https://sharp.pixelplumbing.com/install"
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 17 && VIPS_MICRO_VERSION < 0)
|
||||
#error "libvips version 8.17.0+ is required - please see https://sharp.pixelplumbing.com/install"
|
||||
#endif
|
||||
|
||||
#if defined(__has_include)
|
||||
@ -48,13 +48,13 @@ namespace sharp {
|
||||
int rawWidth;
|
||||
int rawHeight;
|
||||
bool rawPremultiplied;
|
||||
int rawPageHeight;
|
||||
int pages;
|
||||
int page;
|
||||
int level;
|
||||
int subifd;
|
||||
int createChannels;
|
||||
int createWidth;
|
||||
int createHeight;
|
||||
int createPageHeight;
|
||||
std::vector<double> createBackground;
|
||||
std::string createNoiseType;
|
||||
double createNoiseMean;
|
||||
@ -77,10 +77,6 @@ namespace sharp {
|
||||
std::vector<double> joinBackground;
|
||||
VipsAlign joinHalign;
|
||||
VipsAlign joinValign;
|
||||
std::string svgStylesheet;
|
||||
bool svgHighBitdepth;
|
||||
int tiffSubifd;
|
||||
int openSlideLevel;
|
||||
std::vector<double> pdfBackground;
|
||||
bool jp2Oneshot;
|
||||
|
||||
@ -100,13 +96,13 @@ namespace sharp {
|
||||
rawWidth(0),
|
||||
rawHeight(0),
|
||||
rawPremultiplied(false),
|
||||
rawPageHeight(0),
|
||||
pages(1),
|
||||
page(0),
|
||||
level(0),
|
||||
subifd(-1),
|
||||
createChannels(0),
|
||||
createWidth(0),
|
||||
createHeight(0),
|
||||
createPageHeight(0),
|
||||
createBackground{ 0.0, 0.0, 0.0, 255.0 },
|
||||
createNoiseMean(0.0),
|
||||
createNoiseSigma(0.0),
|
||||
@ -125,9 +121,6 @@ namespace sharp {
|
||||
joinBackground{ 0.0, 0.0, 0.0, 255.0 },
|
||||
joinHalign(VIPS_ALIGN_LOW),
|
||||
joinValign(VIPS_ALIGN_LOW),
|
||||
svgHighBitdepth(false),
|
||||
tiffSubifd(-1),
|
||||
openSlideLevel(0),
|
||||
pdfBackground{ 255.0, 255.0, 255.0, 255.0 },
|
||||
jp2Oneshot(false) {}
|
||||
};
|
||||
@ -169,7 +162,6 @@ namespace sharp {
|
||||
EXR,
|
||||
JXL,
|
||||
RAD,
|
||||
DCRAW,
|
||||
VIPS,
|
||||
RAW,
|
||||
UNKNOWN,
|
||||
@ -226,9 +218,14 @@ namespace sharp {
|
||||
ImageType DetermineImageType(char const *file);
|
||||
|
||||
/*
|
||||
Format-specific options builder
|
||||
Does this image type support multiple pages?
|
||||
*/
|
||||
vips::VOption* GetOptionsForImageType(ImageType imageType, InputDescriptor *descriptor);
|
||||
bool ImageTypeSupportsPage(ImageType imageType);
|
||||
|
||||
/*
|
||||
Does this image type support removal of safety limits?
|
||||
*/
|
||||
bool ImageTypeSupportsUnlimited(ImageType imageType);
|
||||
|
||||
/*
|
||||
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
|
||||
|
@ -262,10 +262,6 @@ class MetadataWorker : public Napi::AsyncWorker {
|
||||
}
|
||||
if (baton->xmpLength > 0) {
|
||||
info.Set("xmp", Napi::Buffer<char>::NewOrCopy(env, baton->xmp, baton->xmpLength, sharp::FreeCallback));
|
||||
if (g_utf8_validate(static_cast<char const *>(baton->xmp), baton->xmpLength, nullptr)) {
|
||||
info.Set("xmpAsString",
|
||||
Napi::String::New(env, static_cast<char const *>(baton->xmp), baton->xmpLength));
|
||||
}
|
||||
}
|
||||
if (baton->tifftagPhotoshopLength > 0) {
|
||||
info.Set("tifftagPhotoshop",
|
||||
|
@ -241,7 +241,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// factor for jpegload*, a double scale factor for webpload*,
|
||||
// pdfload* and svgload*
|
||||
if (jpegShrinkOnLoad > 1) {
|
||||
vips::VOption *option = GetOptionsForImageType(inputImageType, baton->input)->set("shrink", jpegShrinkOnLoad);
|
||||
vips::VOption *option = VImage::option()
|
||||
->set("access", access)
|
||||
->set("shrink", jpegShrinkOnLoad)
|
||||
->set("unlimited", baton->input->unlimited)
|
||||
->set("fail_on", baton->input->failOn);
|
||||
if (baton->input->buffer != nullptr) {
|
||||
// Reload JPEG buffer
|
||||
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
||||
@ -252,8 +256,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
image = VImage::jpegload(const_cast<char*>(baton->input->file.data()), option);
|
||||
}
|
||||
} else if (scale != 1.0) {
|
||||
vips::VOption *option = GetOptionsForImageType(inputImageType, baton->input)->set("scale", scale);
|
||||
vips::VOption *option = VImage::option()
|
||||
->set("access", access)
|
||||
->set("scale", scale)
|
||||
->set("fail_on", baton->input->failOn);
|
||||
if (inputImageType == sharp::ImageType::WEBP) {
|
||||
option->set("n", baton->input->pages);
|
||||
option->set("page", baton->input->page);
|
||||
|
||||
if (baton->input->buffer != nullptr) {
|
||||
// Reload WebP buffer
|
||||
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
||||
@ -264,6 +274,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
image = VImage::webpload(const_cast<char*>(baton->input->file.data()), option);
|
||||
}
|
||||
} else if (inputImageType == sharp::ImageType::SVG) {
|
||||
option->set("unlimited", baton->input->unlimited);
|
||||
option->set("dpi", baton->input->density);
|
||||
|
||||
if (baton->input->buffer != nullptr) {
|
||||
// Reload SVG buffer
|
||||
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
||||
@ -278,6 +291,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
throw vips::VError("Input SVG image will exceed 32767x32767 pixel limit when scaled");
|
||||
}
|
||||
} else if (inputImageType == sharp::ImageType::PDF) {
|
||||
option->set("n", baton->input->pages);
|
||||
option->set("page", baton->input->page);
|
||||
option->set("dpi", baton->input->density);
|
||||
option->set("background", baton->input->pdfBackground);
|
||||
|
||||
if (baton->input->buffer != nullptr) {
|
||||
// Reload PDF buffer
|
||||
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
||||
@ -287,6 +305,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// Reload PDF file
|
||||
image = VImage::pdfload(const_cast<char*>(baton->input->file.data()), option);
|
||||
}
|
||||
|
||||
sharp::SetDensity(image, baton->input->density);
|
||||
}
|
||||
} else {
|
||||
@ -876,12 +895,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
image.set(s.first.data(), s.second.data());
|
||||
}
|
||||
}
|
||||
// XMP buffer
|
||||
if ((baton->keepMetadata & VIPS_FOREIGN_KEEP_XMP) && !baton->withXmp.empty()) {
|
||||
image = image.copy();
|
||||
image.set(VIPS_META_XMP_NAME, nullptr,
|
||||
const_cast<void*>(static_cast<void const*>(baton->withXmp.c_str())), baton->withXmp.size());
|
||||
}
|
||||
|
||||
// Number of channels used in output image
|
||||
baton->channels = image.bands();
|
||||
baton->width = image.width();
|
||||
@ -992,7 +1006,6 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("interlace", baton->gifProgressive)
|
||||
->set("interframe_maxerror", baton->gifInterFrameMaxError)
|
||||
->set("interpalette_maxerror", baton->gifInterPaletteMaxError)
|
||||
->set("keep_duplicate_frames", baton->gifKeepDuplicateFrames)
|
||||
->set("dither", baton->gifDither)));
|
||||
baton->bufferOut = static_cast<char*>(area->data);
|
||||
baton->bufferOutLength = area->length;
|
||||
@ -1196,9 +1209,6 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("effort", baton->gifEffort)
|
||||
->set("reuse", baton->gifReuse)
|
||||
->set("interlace", baton->gifProgressive)
|
||||
->set("interframe_maxerror", baton->gifInterFrameMaxError)
|
||||
->set("interpalette_maxerror", baton->gifInterPaletteMaxError)
|
||||
->set("keep_duplicate_frames", baton->gifKeepDuplicateFrames)
|
||||
->set("dither", baton->gifDither));
|
||||
baton->formatOut = "gif";
|
||||
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
||||
@ -1711,7 +1721,6 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
}
|
||||
}
|
||||
baton->withExifMerge = sharp::AttrAsBool(options, "withExifMerge");
|
||||
baton->withXmp = sharp::AttrAsStr(options, "withXmp");
|
||||
baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
|
||||
baton->loop = sharp::AttrAsUint32(options, "loop");
|
||||
baton->delay = sharp::AttrAsInt32Vector(options, "delay");
|
||||
@ -1752,7 +1761,6 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->gifDither = sharp::AttrAsDouble(options, "gifDither");
|
||||
baton->gifInterFrameMaxError = sharp::AttrAsDouble(options, "gifInterFrameMaxError");
|
||||
baton->gifInterPaletteMaxError = sharp::AttrAsDouble(options, "gifInterPaletteMaxError");
|
||||
baton->gifKeepDuplicateFrames = sharp::AttrAsBool(options, "gifKeepDuplicateFrames");
|
||||
baton->gifReuse = sharp::AttrAsBool(options, "gifReuse");
|
||||
baton->gifProgressive = sharp::AttrAsBool(options, "gifProgressive");
|
||||
baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
|
||||
|
@ -169,7 +169,6 @@ struct PipelineBaton {
|
||||
double gifDither;
|
||||
double gifInterFrameMaxError;
|
||||
double gifInterPaletteMaxError;
|
||||
bool gifKeepDuplicateFrames;
|
||||
bool gifReuse;
|
||||
bool gifProgressive;
|
||||
int tiffQuality;
|
||||
@ -202,7 +201,6 @@ struct PipelineBaton {
|
||||
std::string withIccProfile;
|
||||
std::unordered_map<std::string, std::string> withExif;
|
||||
bool withExifMerge;
|
||||
std::string withXmp;
|
||||
int timeoutSeconds;
|
||||
std::vector<double> convKernel;
|
||||
int convKernelWidth;
|
||||
@ -344,7 +342,6 @@ struct PipelineBaton {
|
||||
gifDither(1.0),
|
||||
gifInterFrameMaxError(0.0),
|
||||
gifInterPaletteMaxError(3.0),
|
||||
gifKeepDuplicateFrames(false),
|
||||
gifReuse(true),
|
||||
gifProgressive(false),
|
||||
tiffQuality(80),
|
||||
|
@ -119,7 +119,7 @@ Napi::Value format(const Napi::CallbackInfo& info) {
|
||||
Napi::Object format = Napi::Object::New(env);
|
||||
for (std::string const f : {
|
||||
"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz",
|
||||
"ppm", "fits", "gif", "svg", "heif", "pdf", "vips", "jp2k", "jxl", "rad", "dcraw"
|
||||
"ppm", "fits", "gif", "svg", "heif", "pdf", "vips", "jp2k", "jxl", "rad"
|
||||
}) {
|
||||
// Input
|
||||
const VipsObjectClass *oc = vips_class_find("VipsOperation", (f + "load").c_str());
|
||||
|
@ -1,11 +1,11 @@
|
||||
FROM ubuntu:25.04
|
||||
FROM ubuntu:24.10
|
||||
ARG BRANCH=main
|
||||
|
||||
# Install basic dependencies
|
||||
RUN apt-get -y update && apt-get install -y build-essential curl git ca-certificates gnupg
|
||||
|
||||
# Install latest Node.js LTS
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_24.x -o nodesource_setup.sh
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_22.x -o nodesource_setup.sh
|
||||
RUN bash nodesource_setup.sh
|
||||
RUN apt-get install -y nodejs
|
||||
|
||||
|
@ -552,32 +552,6 @@ async.series({
|
||||
}
|
||||
});
|
||||
}
|
||||
}).add('sharp-mks2013', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height, { kernel: 'mks2013' })
|
||||
.toBuffer(function (err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
}).add('sharp-mks2021', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height, { kernel: 'mks2021' })
|
||||
.toBuffer(function (err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
}).on('cycle', function (event) {
|
||||
console.log('kernels ' + String(event.target));
|
||||
}).on('complete', function () {
|
||||
|
@ -188,8 +188,6 @@ sharp(input)
|
||||
// of the image data in inputBuffer
|
||||
});
|
||||
|
||||
sharp(input).resize({ kernel: 'mks2013' });
|
||||
|
||||
transformer = sharp()
|
||||
.resize(200, 200, {
|
||||
fit: 'cover',
|
||||
@ -375,8 +373,6 @@ sharp(input)
|
||||
.gif({ reuse: false })
|
||||
.gif({ progressive: true })
|
||||
.gif({ progressive: false })
|
||||
.gif({ keepDuplicateFrames: true })
|
||||
.gif({ keepDuplicateFrames: false })
|
||||
.toBuffer({ resolveWithObject: true })
|
||||
.then(({ data, info }) => {
|
||||
console.log(data);
|
||||
@ -418,7 +414,6 @@ sharp({
|
||||
channels: 4,
|
||||
height: 25000,
|
||||
width: 25000,
|
||||
pageHeight: 1000,
|
||||
},
|
||||
limitInputPixels: false,
|
||||
})
|
||||
@ -436,6 +431,9 @@ sharp('input.jpg').clahe({ width: 10, height: 10, maxSlope: 5 }).toFile('outfile
|
||||
// Support `unlimited` input option
|
||||
sharp('input.png', { unlimited: true }).resize(320, 240).toFile('outfile.png');
|
||||
|
||||
// Support `subifd` input option for tiffs
|
||||
sharp('input.tiff', { subifd: 3 }).resize(320, 240).toFile('outfile.png');
|
||||
|
||||
// Support creating with noise
|
||||
sharp({
|
||||
create: {
|
||||
@ -692,8 +690,6 @@ sharp(input)
|
||||
k2: 'v2'
|
||||
}
|
||||
})
|
||||
.keepXmp()
|
||||
.withXmp('test')
|
||||
.keepIccProfile()
|
||||
.withIccProfile('filename')
|
||||
.withIccProfile('filename', { attach: false });
|
||||
@ -720,29 +716,13 @@ sharp(input).composite([
|
||||
}
|
||||
])
|
||||
|
||||
// Support format-specific input options
|
||||
const colour: sharp.Colour = '#fff';
|
||||
const color: sharp.Color = '#fff';
|
||||
sharp({ pdf: { background: colour } });
|
||||
sharp({ pdf: { background: color } });
|
||||
sharp({ pdfBackground: colour }); // Deprecated
|
||||
sharp({ pdfBackground: color }); // Deprecated
|
||||
sharp({ tiff: { subifd: 3 } });
|
||||
sharp({ subifd: 3 }); // Deprecated
|
||||
sharp({ openSlide: { level: 0 } });
|
||||
sharp({ level: 0 }); // Deprecated
|
||||
sharp({ jp2: { oneshot: true } });
|
||||
sharp({ jp2: { oneshot: false } });
|
||||
sharp({ svg: { stylesheet: 'test' }});
|
||||
sharp({ svg: { highBitdepth: true }});
|
||||
sharp({ svg: { highBitdepth: false }});
|
||||
sharp({ pdfBackground: colour });
|
||||
sharp({ pdfBackground: color });
|
||||
|
||||
// Raw input options
|
||||
const raw: sharp.Raw = { width: 1, height: 1, channels: 3 };
|
||||
sharp({ raw });
|
||||
sharp({ raw: { ...raw, premultiplied: true } });
|
||||
sharp({ raw: { ...raw, premultiplied: false } });
|
||||
sharp({ raw: { ...raw, pageHeight: 1 } });
|
||||
sharp({ jp2Oneshot: true });
|
||||
sharp({ jp2Oneshot: false });
|
||||
|
||||
sharp({ autoOrient: true });
|
||||
sharp({ autoOrient: false });
|
||||
|
@ -187,17 +187,6 @@ describe('GIF input', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('invalid keepDuplicateFrames throws', () => {
|
||||
assert.throws(
|
||||
() => sharp().gif({ keepDuplicateFrames: -1 }),
|
||||
/Expected boolean for keepDuplicateFrames but received -1 of type number/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp().gif({ keepDuplicateFrames: 'fail' }),
|
||||
/Expected boolean for keepDuplicateFrames but received fail of type string/
|
||||
);
|
||||
});
|
||||
|
||||
it('should work with streams when only animated is set', function (done) {
|
||||
fs.createReadStream(fixtures.inputGifAnimated)
|
||||
.pipe(sharp({ animated: true }))
|
||||
@ -236,20 +225,6 @@ describe('GIF input', () => {
|
||||
assert.strict(before.length > after.length);
|
||||
});
|
||||
|
||||
it('should keep duplicate frames via keepDuplicateFrames', async () => {
|
||||
const create = { width: 8, height: 8, channels: 4, background: 'blue' };
|
||||
const input = sharp([{ create }, { create }], { join: { animated: true } });
|
||||
|
||||
const before = await input.gif({ keepDuplicateFrames: false }).toBuffer();
|
||||
const after = await input.gif({ keepDuplicateFrames: true }).toBuffer();
|
||||
assert.strict(before.length < after.length);
|
||||
|
||||
const beforeMeta = await sharp(before).metadata();
|
||||
const afterMeta = await sharp(after).metadata();
|
||||
assert.strictEqual(beforeMeta.pages, 1);
|
||||
assert.strictEqual(afterMeta.pages, 2);
|
||||
});
|
||||
|
||||
it('non-animated input defaults to no-loop', async () => {
|
||||
for (const input of [fixtures.inputGif, fixtures.inputPng]) {
|
||||
const data = await sharp(input)
|
||||
|
103
test/unit/io.js
103
test/unit/io.js
@ -867,91 +867,52 @@ describe('Input/output', function () {
|
||||
sharp({ pages: '1' });
|
||||
}, /Expected integer between -1 and 100000 for pages but received 1 of type string/);
|
||||
});
|
||||
it('Valid openSlide.level property', function () {
|
||||
sharp({ openSlide: { level: 1 } });
|
||||
it('Valid level property', function () {
|
||||
sharp({ level: 1 });
|
||||
});
|
||||
it('Invalid openSlide.level property (string) throws', function () {
|
||||
assert.throws(
|
||||
() => sharp({ openSlide: { level: '1' } }),
|
||||
/Expected integer between 0 and 256 for openSlide.level but received 1 of type string/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ level: '1' }),
|
||||
/Expected integer between 0 and 256 for level but received 1 of type string/
|
||||
);
|
||||
it('Invalid level property (string) throws', function () {
|
||||
assert.throws(function () {
|
||||
sharp({ level: '1' });
|
||||
}, /Expected integer between 0 and 256 for level but received 1 of type string/);
|
||||
});
|
||||
it('Invalid openSlide.level property (negative) throws', function () {
|
||||
assert.throws(
|
||||
() => sharp({ openSlide: { level: -1 } }),
|
||||
/Expected integer between 0 and 256 for openSlide\.level but received -1 of type number/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ level: -1 }),
|
||||
/Expected integer between 0 and 256 for level but received -1 of type number/
|
||||
);
|
||||
it('Invalid level property (negative) throws', function () {
|
||||
assert.throws(function () {
|
||||
sharp({ level: -1 });
|
||||
}, /Expected integer between 0 and 256 for level but received -1 of type number/);
|
||||
});
|
||||
it('Valid tiff.subifd property', function () {
|
||||
sharp({ tiff: { subifd: 1 } });
|
||||
it('Valid subifd property', function () {
|
||||
sharp({ subifd: 1 });
|
||||
});
|
||||
it('Invalid tiff.subifd property (string) throws', function () {
|
||||
assert.throws(
|
||||
() => sharp({ tiff: { subifd: '1' } }),
|
||||
/Expected integer between -1 and 100000 for tiff\.subifd but received 1 of type string/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ subifd: '1' }),
|
||||
/Expected integer between -1 and 100000 for subifd but received 1 of type string/
|
||||
);
|
||||
it('Invalid subifd property (string) throws', function () {
|
||||
assert.throws(function () {
|
||||
sharp({ subifd: '1' });
|
||||
}, /Expected integer between -1 and 100000 for subifd but received 1 of type string/);
|
||||
});
|
||||
it('Invalid tiff.subifd property (float) throws', function () {
|
||||
assert.throws(
|
||||
() => sharp({ tiff: { subifd: 1.2 } }),
|
||||
/Expected integer between -1 and 100000 for tiff\.subifd but received 1.2 of type number/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ subifd: 1.2 }),
|
||||
/Expected integer between -1 and 100000 for subifd but received 1.2 of type number/
|
||||
);
|
||||
it('Invalid subifd property (float) throws', function () {
|
||||
assert.throws(function () {
|
||||
sharp({ subifd: 1.2 });
|
||||
}, /Expected integer between -1 and 100000 for subifd but received 1.2 of type number/);
|
||||
});
|
||||
it('Valid pdf.background property (string)', function () {
|
||||
sharp({ pdf: { background: '#00ff00' } });
|
||||
it('Valid pdfBackground property (string)', function () {
|
||||
sharp({ pdfBackground: '#00ff00' });
|
||||
});
|
||||
it('Valid pdf.background property (object)', function () {
|
||||
sharp({ pdf: { background: { r: 0, g: 255, b: 0 } } });
|
||||
it('Valid pdfBackground property (object)', function () {
|
||||
sharp({ pdfBackground: { r: 0, g: 255, b: 0 } });
|
||||
});
|
||||
it('Invalid pdf.background property (string) throws', function () {
|
||||
assert.throws(
|
||||
() => sharp({ pdf: { background: '00ff00' } }),
|
||||
/Unable to parse color from string/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ pdfBackground: '00ff00' }),
|
||||
/Unable to parse color from string/
|
||||
);
|
||||
it('Invalid pdfBackground property (string) throws', function () {
|
||||
assert.throws(function () {
|
||||
sharp({ pdfBackground: '00ff00' });
|
||||
}, /Unable to parse color from string/);
|
||||
});
|
||||
it('Invalid pdf.background property (number) throws', function () {
|
||||
assert.throws(
|
||||
() => sharp({ pdf: { background: 255 } }),
|
||||
/Expected object or string for background/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ pdf: { background: 255 } }),
|
||||
/Expected object or string for background/
|
||||
);
|
||||
it('Invalid pdfBackground property (number) throws', function () {
|
||||
assert.throws(function () {
|
||||
sharp({ pdfBackground: 255 });
|
||||
}, /Expected object or string for background/);
|
||||
});
|
||||
it('Invalid pdf.background property (object)', function () {
|
||||
assert.throws(
|
||||
() => sharp({ pdf: { background: { red: 0, green: 255, blue: 0 } } }),
|
||||
/Unable to parse color from object/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ pdfBackground: { red: 0, green: 255, blue: 0 } }),
|
||||
/Unable to parse color from object/
|
||||
);
|
||||
it('Invalid pdfBackground property (object)', function () {
|
||||
assert.throws(function () {
|
||||
sharp({ pdfBackground: { red: 0, green: 255, blue: 0 } });
|
||||
}, /Unable to parse color from object/);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -117,14 +117,14 @@ describe('JP2 output', () => {
|
||||
|
||||
it('valid JP2 oneshot value does not throw error', () => {
|
||||
assert.doesNotThrow(
|
||||
() => sharp({ jp2: { oneshot: true } })
|
||||
() => sharp(fixtures.inputJp2TileParts, { jp2Oneshot: true })
|
||||
);
|
||||
});
|
||||
|
||||
it('invalid JP2 oneshot value throws error', () => {
|
||||
assert.throws(
|
||||
() => sharp({ jp2: { oneshot: 'fail' } }),
|
||||
/Expected boolean for jp2.oneshot but received fail of type string/
|
||||
() => sharp(fixtures.inputJp2TileParts, { jp2Oneshot: 'fail' }),
|
||||
/Expected boolean for jp2Oneshot but received fail of type string/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -179,7 +179,7 @@ describe('libvips binaries', function () {
|
||||
process.env.npm_config_arch = 's390x';
|
||||
process.env.npm_config_libc = '';
|
||||
const locatorHash = libvips.yarnLocator();
|
||||
assert.strictEqual(locatorHash, '30afd744f9');
|
||||
assert.strictEqual(locatorHash, 'e23686d7dd');
|
||||
delete process.env.npm_config_platform;
|
||||
delete process.env.npm_config_arch;
|
||||
delete process.env.npm_config_libc;
|
||||
|
@ -82,7 +82,6 @@ describe('Image metadata', function () {
|
||||
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||
assert.strictEqual(12466, metadata.xmp.byteLength);
|
||||
assert.strictEqual(metadata.xmp.indexOf(Buffer.from('<?xpacket begin="')), 0);
|
||||
assert(metadata.xmpAsString.startsWith('<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -107,8 +106,6 @@ describe('Image metadata', function () {
|
||||
assert.strictEqual(3248, metadata.autoOrient.height);
|
||||
assert.strictEqual('undefined', typeof metadata.exif);
|
||||
assert.strictEqual('undefined', typeof metadata.icc);
|
||||
assert.strictEqual('undefined', typeof metadata.xmp);
|
||||
assert.strictEqual('undefined', typeof metadata.xmpAsString);
|
||||
assert.strictEqual('inch', metadata.resolutionUnit);
|
||||
done();
|
||||
});
|
||||
@ -1103,170 +1100,6 @@ describe('Image metadata', function () {
|
||||
assert.strictEqual(exif2.Image.Software, 'sharp');
|
||||
});
|
||||
|
||||
describe('XMP metadata tests', function () {
|
||||
it('withMetadata preserves existing XMP metadata from input', async () => {
|
||||
const data = await sharp(fixtures.inputJpgWithIptcAndXmp)
|
||||
.resize(320, 240)
|
||||
.withMetadata()
|
||||
.toBuffer();
|
||||
|
||||
const metadata = await sharp(data).metadata();
|
||||
assert.strictEqual('object', typeof metadata.xmp);
|
||||
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||
assert.strictEqual(true, metadata.xmp.length > 0);
|
||||
// Check that XMP starts with the expected XML declaration
|
||||
assert.strictEqual(metadata.xmp.indexOf(Buffer.from('<?xpacket begin="')), 0);
|
||||
});
|
||||
|
||||
it('keepXmp preserves existing XMP metadata from input', async () => {
|
||||
const data = await sharp(fixtures.inputJpgWithIptcAndXmp)
|
||||
.resize(320, 240)
|
||||
.keepXmp()
|
||||
.toBuffer();
|
||||
|
||||
const metadata = await sharp(data).metadata();
|
||||
assert.strictEqual('object', typeof metadata.xmp);
|
||||
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||
assert.strictEqual(true, metadata.xmp.length > 0);
|
||||
// Check that XMP starts with the expected XML declaration
|
||||
assert.strictEqual(metadata.xmp.indexOf(Buffer.from('<?xpacket begin="')), 0);
|
||||
});
|
||||
|
||||
it('withXmp with custom XMP replaces existing XMP', async () => {
|
||||
const customXmp = '<?xml version="1.0"?><x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:creator><rdf:Seq><rdf:li>Test Creator</rdf:li></rdf:Seq></dc:creator><dc:title><rdf:Alt><rdf:li xml:lang="x-default">Test Title</rdf:li></rdf:Alt></dc:title></rdf:Description></rdf:RDF></x:xmpmeta>';
|
||||
|
||||
const data = await sharp(fixtures.inputJpgWithIptcAndXmp)
|
||||
.resize(320, 240)
|
||||
.withXmp(customXmp)
|
||||
.toBuffer();
|
||||
|
||||
const metadata = await sharp(data).metadata();
|
||||
assert.strictEqual('object', typeof metadata.xmp);
|
||||
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||
|
||||
// Check that the XMP contains our custom content
|
||||
const xmpString = metadata.xmp.toString();
|
||||
assert.strictEqual(true, xmpString.includes('Test Creator'));
|
||||
assert.strictEqual(true, xmpString.includes('Test Title'));
|
||||
});
|
||||
|
||||
it('withXmp with custom XMP buffer on image without existing XMP', async () => {
|
||||
const customXmp = '<?xml version="1.0"?><x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:description><rdf:Alt><rdf:li xml:lang="x-default">Added via Sharp</rdf:li></rdf:Alt></dc:description></rdf:Description></rdf:RDF></x:xmpmeta>';
|
||||
|
||||
const data = await sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.withXmp(customXmp)
|
||||
.toBuffer();
|
||||
|
||||
const metadata = await sharp(data).metadata();
|
||||
assert.strictEqual('object', typeof metadata.xmp);
|
||||
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||
|
||||
// Check that the XMP contains our custom content
|
||||
const xmpString = metadata.xmp.toString();
|
||||
assert.strictEqual(true, xmpString.includes('Added via Sharp'));
|
||||
});
|
||||
|
||||
it('withXmp with valid XMP metadata for different image formats', async () => {
|
||||
const customXmp = '<?xml version="1.0"?><x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:subject><rdf:Bag><rdf:li>test</rdf:li><rdf:li>metadata</rdf:li></rdf:Bag></dc:subject></rdf:Description></rdf:RDF></x:xmpmeta>';
|
||||
|
||||
// Test with JPEG output
|
||||
const jpegData = await sharp(fixtures.inputJpg)
|
||||
.resize(100, 100)
|
||||
.jpeg()
|
||||
.withXmp(customXmp)
|
||||
.toBuffer();
|
||||
|
||||
const jpegMetadata = await sharp(jpegData).metadata();
|
||||
assert.strictEqual('object', typeof jpegMetadata.xmp);
|
||||
assert.strictEqual(true, jpegMetadata.xmp instanceof Buffer);
|
||||
assert.strictEqual(true, jpegMetadata.xmp.toString().includes('test'));
|
||||
|
||||
// Test with PNG output (PNG should also support XMP metadata)
|
||||
const pngData = await sharp(fixtures.inputJpg)
|
||||
.resize(100, 100)
|
||||
.png()
|
||||
.withXmp(customXmp)
|
||||
.toBuffer();
|
||||
|
||||
const pngMetadata = await sharp(pngData).metadata();
|
||||
// PNG format should preserve XMP metadata when using withXmp
|
||||
assert.strictEqual('object', typeof pngMetadata.xmp);
|
||||
assert.strictEqual(true, pngMetadata.xmp instanceof Buffer);
|
||||
assert.strictEqual(true, pngMetadata.xmp.toString().includes('test'));
|
||||
|
||||
// Test with WebP output (WebP should also support XMP metadata)
|
||||
const webpData = await sharp(fixtures.inputJpg)
|
||||
.resize(100, 100)
|
||||
.webp()
|
||||
.withXmp(customXmp)
|
||||
.toBuffer();
|
||||
|
||||
const webpMetadata = await sharp(webpData).metadata();
|
||||
// WebP format should preserve XMP metadata when using withXmp
|
||||
assert.strictEqual('object', typeof webpMetadata.xmp);
|
||||
assert.strictEqual(true, webpMetadata.xmp instanceof Buffer);
|
||||
assert.strictEqual(true, webpMetadata.xmp.toString().includes('test'));
|
||||
});
|
||||
|
||||
it('XMP metadata persists through multiple operations', async () => {
|
||||
const customXmp = '<?xml version="1.0"?><x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:identifier>persistent-test</dc:identifier></rdf:Description></rdf:RDF></x:xmpmeta>';
|
||||
|
||||
const data = await sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.withXmp(customXmp)
|
||||
.rotate(90)
|
||||
.blur(1)
|
||||
.sharpen()
|
||||
.toBuffer();
|
||||
|
||||
const metadata = await sharp(data).metadata();
|
||||
assert.strictEqual('object', typeof metadata.xmp);
|
||||
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||
assert.strictEqual(true, metadata.xmp.toString().includes('persistent-test'));
|
||||
});
|
||||
|
||||
it('withXmp XMP works with WebP format specifically', async () => {
|
||||
const webpXmp = '<?xml version="1.0"?><x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:creator><rdf:Seq><rdf:li>WebP Creator</rdf:li></rdf:Seq></dc:creator><dc:format>image/webp</dc:format></rdf:Description></rdf:RDF></x:xmpmeta>';
|
||||
|
||||
const data = await sharp(fixtures.inputJpg)
|
||||
.resize(120, 80)
|
||||
.webp({ quality: 80 })
|
||||
.withXmp(webpXmp)
|
||||
.toBuffer();
|
||||
|
||||
const metadata = await sharp(data).metadata();
|
||||
assert.strictEqual('webp', metadata.format);
|
||||
assert.strictEqual('object', typeof metadata.xmp);
|
||||
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||
|
||||
const xmpString = metadata.xmp.toString();
|
||||
assert.strictEqual(true, xmpString.includes('WebP Creator'));
|
||||
assert.strictEqual(true, xmpString.includes('image/webp'));
|
||||
});
|
||||
|
||||
it('withXmp XMP validation - non-string input', function () {
|
||||
assert.throws(
|
||||
() => sharp().withXmp(123),
|
||||
/Expected non-empty string for xmp but received 123 of type number/
|
||||
);
|
||||
});
|
||||
|
||||
it('withXmp XMP validation - null input', function () {
|
||||
assert.throws(
|
||||
() => sharp().withXmp(null),
|
||||
/Expected non-empty string for xmp but received null of type object/
|
||||
);
|
||||
});
|
||||
|
||||
it('withXmp XMP validation - empty string', function () {
|
||||
assert.throws(
|
||||
() => sharp().withXmp(''),
|
||||
/Expected non-empty string for xmp/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invalid parameters', function () {
|
||||
it('String orientation', function () {
|
||||
assert.throws(function () {
|
||||
|
@ -173,26 +173,6 @@ describe('Gaussian noise', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('animated noise', async () => {
|
||||
const gif = await sharp({
|
||||
create: {
|
||||
width: 16,
|
||||
height: 64,
|
||||
pageHeight: 16,
|
||||
channels: 3,
|
||||
noise: { type: 'gaussian' }
|
||||
}
|
||||
})
|
||||
.gif()
|
||||
.toBuffer();
|
||||
|
||||
const { width, height, pages, delay } = await sharp(gif).metadata();
|
||||
assert.strictEqual(width, 16);
|
||||
assert.strictEqual(height, 16);
|
||||
assert.strictEqual(pages, 4);
|
||||
assert.strictEqual(delay.length, 4);
|
||||
});
|
||||
|
||||
it('no create object properties specified', function () {
|
||||
assert.throws(function () {
|
||||
sharp({
|
||||
@ -279,29 +259,4 @@ describe('Gaussian noise', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Invalid pageHeight', () => {
|
||||
const create = {
|
||||
width: 8,
|
||||
height: 8,
|
||||
channels: 4,
|
||||
noise: { type: 'gaussian' }
|
||||
};
|
||||
assert.throws(
|
||||
() => sharp({ create: { ...create, pageHeight: 'zoinks' } }),
|
||||
/Expected positive integer for create\.pageHeight but received zoinks of type string/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ create: { ...create, pageHeight: -1 } }),
|
||||
/Expected positive integer for create\.pageHeight but received -1 of type number/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ create: { ...create, pageHeight: 9 } }),
|
||||
/Expected positive integer for create\.pageHeight but received 9 of type number/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ create: { ...create, pageHeight: 3 } }),
|
||||
/Expected create\.height 8 to be a multiple of create\.pageHeight 3/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -55,35 +55,6 @@ describe('Raw pixel data', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Invalid premultiplied', () => {
|
||||
assert.throws(
|
||||
() => sharp({ raw: { width: 1, height: 1, channels: 4, premultiplied: 'zoinks' } }),
|
||||
/Expected boolean for raw\.premultiplied but received zoinks of type string/
|
||||
);
|
||||
});
|
||||
|
||||
it('Invalid pageHeight', () => {
|
||||
const width = 8;
|
||||
const height = 8;
|
||||
const channels = 4;
|
||||
assert.throws(
|
||||
() => sharp({ raw: { width, height, channels, pageHeight: 'zoinks' } }),
|
||||
/Expected positive integer for raw\.pageHeight but received zoinks of type string/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ raw: { width, height, channels, pageHeight: -1 } }),
|
||||
/Expected positive integer for raw\.pageHeight but received -1 of type number/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ raw: { width, height, channels, pageHeight: 9 } }),
|
||||
/Expected positive integer for raw\.pageHeight but received 9 of type number/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ raw: { width, height, channels, pageHeight: 3 } }),
|
||||
/Expected raw\.height 8 to be a multiple of raw\.pageHeight 3/
|
||||
);
|
||||
});
|
||||
|
||||
it('RGB', function (done) {
|
||||
// Convert to raw pixel data
|
||||
sharp(fixtures.inputJpg)
|
||||
@ -314,23 +285,6 @@ describe('Raw pixel data', function () {
|
||||
}
|
||||
});
|
||||
|
||||
it('Animated', async () => {
|
||||
const gif = await sharp(
|
||||
Buffer.alloc(8),
|
||||
{ raw: { width: 1, height: 2, channels: 4, pageHeight: 1 }, animated: true }
|
||||
)
|
||||
.gif({ keepDuplicateFrames: true })
|
||||
.toBuffer();
|
||||
|
||||
console.log(await sharp(gif).metadata());
|
||||
|
||||
const { width, height, pages, delay } = await sharp(gif).metadata();
|
||||
assert.strictEqual(width, 1);
|
||||
assert.strictEqual(height, 1);
|
||||
assert.strictEqual(pages, 2);
|
||||
assert.strictEqual(delay.length, 2);
|
||||
});
|
||||
|
||||
describe('16-bit roundtrip', () => {
|
||||
it('grey', async () => {
|
||||
const grey = 42000;
|
||||
|
@ -139,41 +139,6 @@ describe('SVG input', function () {
|
||||
assert.strictEqual(info.channels, 4);
|
||||
});
|
||||
|
||||
it('Can apply custom CSS', async () => {
|
||||
const svg = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="10" height="10" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="5" cy="5" r="4" fill="green" />
|
||||
</svg>`;
|
||||
const stylesheet = 'circle { fill: red }';
|
||||
|
||||
const [r, g, b, a] = await sharp(Buffer.from(svg), { svg: { stylesheet } })
|
||||
.extract({ left: 5, top: 5, width: 1, height: 1 })
|
||||
.raw()
|
||||
.toBuffer();
|
||||
|
||||
assert.deepEqual([r, g, b, a], [255, 0, 0, 255]);
|
||||
});
|
||||
|
||||
it('Invalid stylesheet input option throws', () =>
|
||||
assert.throws(
|
||||
() => sharp({ svg: { stylesheet: 123 } }),
|
||||
/Expected string for svg\.stylesheet but received 123 of type number/
|
||||
)
|
||||
);
|
||||
|
||||
it('Valid highBitdepth input option does not throw', () =>
|
||||
assert.doesNotThrow(
|
||||
() => sharp({ svg: { highBitdepth: true } })
|
||||
)
|
||||
);
|
||||
|
||||
it('Invalid highBitdepth input option throws', () =>
|
||||
assert.throws(
|
||||
() => sharp({ svg: { highBitdepth: 123 } }),
|
||||
/Expected boolean for svg\.highBitdepth but received 123 of type number/
|
||||
)
|
||||
);
|
||||
|
||||
it('Fails to render SVG larger than 32767x32767', () =>
|
||||
assert.rejects(
|
||||
() => sharp(Buffer.from('<svg xmlns="http://www.w3.org/2000/svg" width="32768" height="1" />')).toBuffer(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user