Compare commits

..

13 Commits

Author SHA1 Message Date
Lovell Fuller
d396a4e65d Release v0.30.4 2022-04-18 10:14:31 +01:00
Lovell Fuller
ae1dbcdf4e Bump deps 2022-04-18 10:11:30 +01:00
Lovell Fuller
4c29368b51 Docs: EXIF metadata unsupported for TIFF output #3074 2022-04-18 09:53:35 +01:00
Lovell Fuller
36e55969d2 Docs: mention npm's foreground-scripts option to aid debugging 2022-04-18 09:51:27 +01:00
Lovell Fuller
985e881e7a Bump deps 2022-04-05 21:14:09 +01:00
Lovell Fuller
0b116715aa Docs: changelog for #3178 2022-04-05 21:11:06 +01:00
Chris Hranj
9deac83322 Add missing file name to 'Input file is missing' error message (#3178) 2022-04-05 11:35:01 +01:00
Lovell Fuller
5d36f5f699 Improve error message for SVG render above limit #3167 2022-04-04 14:20:04 +01:00
Lovell Fuller
926572b41e Control sensitivity to invalid images via failOn
Deprecates failOnError, equivalent to failOn=warning
2022-04-04 12:27:45 +01:00
Lovell Fuller
d0c8e95641 Docs: expand info about use with worker threads 2022-03-30 10:09:15 +01:00
Lovell Fuller
b0ca23c3e7 Docs: changelog and credit for #3146 2022-03-28 21:32:38 +01:00
codepage949
c3a0d5f5d0 Add support for TypedArray input with byteOffset and length 2022-03-27 20:18:58 +01:00
Lovell Fuller
1d36936954 Ensure create has correct bit depth and colourspace #3139 2022-03-22 19:48:02 +00:00
25 changed files with 228 additions and 95 deletions

View File

@@ -32,11 +32,11 @@ If you are using npm v7 or later, does the user running `npm install` own the di
If you are using the `ignore-scripts` feature of `npm`, have you tried with the `npm install --ignore-scripts=false` flag?
### What is the complete output of running `npm install --verbose sharp`?
### What is the complete output of running `npm install --verbose --foreground-scripts sharp` in an empty directory?
<details>
<!-- Please provide output of `npm install --verbose sharp` here. -->
<!-- Please provide output of `npm install --verbose --foreground-scripts sharp` in an empty directory here. -->
</details>

View File

@@ -42,7 +42,7 @@ and [https://www.cairographics.org/operators/][2]
* `images[].raw.height` **[Number][7]?**
* `images[].raw.channels` **[Number][7]?**
* `images[].animated` **[boolean][9]** Set to `true` to read all frames/pages of an animated image. (optional, default `false`)
* `images[].failOnError` **[boolean][9]** @see [constructor parameters][10] (optional, default `true`)
* `images[].failOn` **[string][6]** @see [constructor parameters][10] (optional, default `'warning'`)
* `images[].limitInputPixels` **([number][7] | [boolean][9])** @see [constructor parameters][10] (optional, default `268402689`)
### Examples

View File

@@ -20,38 +20,37 @@ Implements the [stream.Duplex][1] class.
JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
* `options` **[Object][13]?** if present, is an Object with optional attributes.
* `options.failOnError` **[boolean][14]** by default halt processing and raise an error when loading invalid images.
Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid. (optional, default `true`)
* `options.limitInputPixels` **([number][15] | [boolean][14])** Do not process input images where the number of pixels
* `options.failOn` **[string][12]** level of sensitivity to invalid images, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels. (optional, default `'warning'`)
* `options.limitInputPixels` **([number][14] | [boolean][15])** Do not process input images where the number of pixels
(width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). (optional, default `268402689`)
* `options.unlimited` **[boolean][14]** Set this to `true` to remove safety features that help prevent memory exhaustion (SVG, PNG). (optional, default `false`)
* `options.sequentialRead` **[boolean][14]** Set this to `true` to use sequential rather than random access where possible.
* `options.unlimited` **[boolean][15]** Set this to `true` to remove safety features that help prevent memory exhaustion (SVG, PNG). (optional, default `false`)
* `options.sequentialRead` **[boolean][15]** Set this to `true` to use sequential rather than random access where possible.
This can reduce memory usage and might improve performance on some systems. (optional, default `false`)
* `options.density` **[number][15]** number representing the DPI for vector images in the range 1 to 100000. (optional, default `72`)
* `options.pages` **[number][15]** number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
* `options.page` **[number][15]** page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based. (optional, default `0`)
* `options.subifd` **[number][15]** subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. (optional, default `-1`)
* `options.level` **[number][15]** level to extract from a multi-level input (OpenSlide), zero based. (optional, default `0`)
* `options.animated` **[boolean][14]** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default `false`)
* `options.density` **[number][14]** number representing the DPI for vector images in the range 1 to 100000. (optional, default `72`)
* `options.pages` **[number][14]** number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
* `options.page` **[number][14]** page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based. (optional, default `0`)
* `options.subifd` **[number][14]** subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. (optional, default `-1`)
* `options.level` **[number][14]** level to extract from a multi-level input (OpenSlide), zero based. (optional, default `0`)
* `options.animated` **[boolean][15]** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default `false`)
* `options.raw` **[Object][13]?** describes raw pixel input image data. See `raw()` for pixel ordering.
* `options.raw.width` **[number][15]?** integral number of pixels wide.
* `options.raw.height` **[number][15]?** integral number of pixels high.
* `options.raw.channels` **[number][15]?** integral number of channels, between 1 and 4.
* `options.raw.premultiplied` **[boolean][14]?** specifies that the raw input has already been premultiplied, set to `true`
* `options.raw.width` **[number][14]?** integral number of pixels wide.
* `options.raw.height` **[number][14]?** integral number of pixels high.
* `options.raw.channels` **[number][14]?** integral number of channels, between 1 and 4.
* `options.raw.premultiplied` **[boolean][15]?** specifies that the raw input has already been premultiplied, set to `true`
to avoid sharp premultiplying the image. (optional, default `false`)
* `options.create` **[Object][13]?** describes a new image to be created.
* `options.create.width` **[number][15]?** integral number of pixels wide.
* `options.create.height` **[number][15]?** integral number of pixels high.
* `options.create.channels` **[number][15]?** integral number of channels, either 3 (RGB) or 4 (RGBA).
* `options.create.width` **[number][14]?** integral number of pixels wide.
* `options.create.height` **[number][14]?** integral number of pixels high.
* `options.create.channels` **[number][14]?** integral number of channels, either 3 (RGB) or 4 (RGBA).
* `options.create.background` **([string][12] | [Object][13])?** parsed by the [color][16] module to extract values for red, green, blue and alpha.
* `options.create.noise` **[Object][13]?** describes a noise to be created.
* `options.create.noise.type` **[string][12]?** type of generated noise, currently only `gaussian` is supported.
* `options.create.noise.mean` **[number][15]?** mean of pixels in generated noise.
* `options.create.noise.sigma` **[number][15]?** standard deviation of pixels in generated noise.
* `options.create.noise.mean` **[number][14]?** mean of pixels in generated noise.
* `options.create.noise.sigma` **[number][14]?** standard deviation of pixels in generated noise.
### Examples
@@ -154,9 +153,7 @@ readableStream.pipe(pipeline);
// Using Promises to know when the pipeline is complete
const fs = require("fs");
const got = require("got");
const sharpStream = sharp({
failOnError: false
});
const sharpStream = sharp({ failOn: 'none' });
const promises = [];
@@ -226,9 +223,9 @@ Returns **[Sharp][18]**
[13]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[14]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[14]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[15]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[15]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[16]: https://www.npmjs.org/package/color

View File

@@ -289,6 +289,20 @@ Produce the "negative" of the image.
* `options.alpha` **[Boolean][6]** Whether or not to negate any alpha channel (optional, default `true`)
### Examples
```javascript
const output = await sharp(input)
.negate()
.toBuffer();
```
```javascript
const output = await sharp(input)
.negate({ alpha: false })
.toBuffer();
```
Returns **Sharp**
## normalise

View File

@@ -118,6 +118,8 @@ output profile is provided.
The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
sRGB colour space and strip all metadata, including the removal of any ICC profile.
EXIF metadata is unsupported for TIFF output.
### Parameters
* `options` **[Object][6]?**

View File

@@ -4,6 +4,24 @@
Requires libvips v8.12.2
### v0.30.4 - 18th April 2022
* Increase control over sensitivity to invalid images via `failOn`, deprecate `failOnError` (equivalent to `failOn: 'warning'`).
* Ensure `create` input image has correct bit depth and colour space.
[#3139](https://github.com/lovell/sharp/issues/3139)
* Add support for `TypedArray` input with `byteOffset` and `length`.
[#3146](https://github.com/lovell/sharp/pull/3146)
[@codepage949](https://github.com/codepage949)
* Improve error message when attempting to render SVG input greater than 32767x32767.
[#3167](https://github.com/lovell/sharp/issues/3167)
* Add missing file name to 'Input file is missing' error message.
[#3178](https://github.com/lovell/sharp/pull/3178)
[@Brodan](https://github.com/Brodan)
### v0.30.3 - 14th March 2022
* Allow `sharpen` options to be provided more consistently as an Object.

View File

@@ -236,3 +236,9 @@ GitHub: https://github.com/gforge
Name: Chris Banks
GitHub: https://github.com/christopherbradleybanks
Name: codepage949
GitHub: https://github.com/codepage949
Name: Chris Hranj
GitHub: https://github.com/Brodan

View File

@@ -57,7 +57,7 @@ When using npm v7 or later, the user running `npm install` must own the director
The `npm install --ignore-scripts=false` flag must be used when `npm` has been configured to ignore installation scripts.
Check the output of running `npm install --verbose sharp` for useful error messages.
Check the output of running `npm install --verbose --foreground-scripts sharp` for useful error messages.
## Apple M1
@@ -248,11 +248,17 @@ esbuild app.js --bundle --platform=node --external:sharp
## Worker threads
The main thread must call `require('sharp')`
before worker threads are created
to ensure shared libraries remain loaded in memory
On some platforms, including glibc-based Linux,
the main thread must call `require('sharp')`
_before_ worker threads are created.
This is to ensure shared libraries remain loaded in memory
until after all threads are complete.
Without this, the following error may occur:
```
Module did not self-register
```
## Known conflicts
### Canvas and Windows

File diff suppressed because one or more lines are too long

View File

@@ -9,7 +9,7 @@ const extractDescription = (str) =>
.replace(/\s+/g, ' ')
.replace(/[^A-Za-z0-9_/\-,. ]/g, '')
.replace(/\s+/g, ' ')
.substr(0, 180)
.substring(0, 200)
.trim();
const extractParameters = (str) =>

View File

@@ -21,6 +21,7 @@ module.exports = [
'can',
'containing',
'contains',
'created',
'current',
'date',
'default',
@@ -43,6 +44,8 @@ module.exports = [
'how',
'image',
'implies',
'include',
'including',
'involve',
'its',
'last',
@@ -69,6 +72,7 @@ module.exports = [
'provided',
'ready',
'requires',
'requiresharp',
'returned',
'same',
'see',
@@ -77,6 +81,7 @@ module.exports = [
'should',
'since',
'site',
'some',
'specified',
'spelling',
'such',

View File

@@ -105,7 +105,7 @@ const blend = {
* @param {Number} [images[].raw.height]
* @param {Number} [images[].raw.channels]
* @param {boolean} [images[].animated=false] - Set to `true` to read all frames/pages of an animated image.
* @param {boolean} [images[].failOnError=true] - @see {@link /api-constructor#parameters|constructor parameters}
* @param {string} [images[].failOn='warning'] - @see {@link /api-constructor#parameters|constructor parameters}
* @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor#parameters|constructor parameters}
* @returns {Sharp}
* @throws {Error} Invalid parameters

View File

@@ -98,8 +98,7 @@ const debuglog = util.debuglog('sharp');
* a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
* JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
* @param {Object} [options] - if present, is an Object with optional attributes.
* @param {boolean} [options.failOnError=true] - by default halt processing and raise an error when loading invalid images.
* Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid.
* @param {string} [options.failOn='warning'] - level of sensitivity to invalid images, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels.
* @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
* (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
@@ -320,9 +319,7 @@ Object.setPrototypeOf(Sharp, stream.Duplex);
* // Using Promises to know when the pipeline is complete
* const fs = require("fs");
* const got = require("got");
* const sharpStream = sharp({
* failOnError: false
* });
* const sharpStream = sharp({ failOn: 'none' });
*
* const promises = [];
*

View File

@@ -9,9 +9,9 @@ const sharp = require('./sharp');
* @private
*/
function _inputOptionsFromObject (obj) {
const { raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd } = obj;
return [raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd].some(is.defined)
? { raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd }
const { raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd } = obj;
return [raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd].some(is.defined)
? { raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd }
: undefined;
}
@@ -21,7 +21,7 @@ function _inputOptionsFromObject (obj) {
*/
function _createInputDescriptor (input, inputOptions, containerOptions) {
const inputDescriptor = {
failOnError: true,
failOn: 'warning',
limitInputPixels: Math.pow(0x3FFF, 2),
unlimited: false,
sequentialRead: false
@@ -39,7 +39,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
if (input.length === 0) {
throw Error('Input Bit Array is empty');
}
inputDescriptor.buffer = Buffer.from(input.buffer);
inputDescriptor.buffer = Buffer.from(input.buffer, input.byteOffset, input.byteLength);
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
// Plain Object descriptor, e.g. create
inputOptions = input;
@@ -56,14 +56,22 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
}`);
}
if (is.object(inputOptions)) {
// Fail on error
// Deprecated: failOnError
if (is.defined(inputOptions.failOnError)) {
if (is.bool(inputOptions.failOnError)) {
inputDescriptor.failOnError = inputOptions.failOnError;
inputDescriptor.failOn = inputOptions.failOnError ? 'warning' : 'none';
} else {
throw is.invalidParameterError('failOnError', 'boolean', inputOptions.failOnError);
}
}
// failOn
if (is.defined(inputOptions.failOn)) {
if (is.string(inputOptions.failOn) && is.inArray(inputOptions.failOn, ['none', 'truncated', 'error', 'warning'])) {
inputDescriptor.failOn = inputOptions.failOn;
} else {
throw is.invalidParameterError('failOn', 'one of: none, truncated, error, warning', inputOptions.failOn);
}
}
// Density
if (is.defined(inputOptions.density)) {
if (is.inRange(inputOptions.density, 1, 100000)) {

View File

@@ -420,6 +420,17 @@ function gamma (gamma, gammaOut) {
/**
* Produce the "negative" of the image.
*
* @example
* const output = await sharp(input)
* .negate()
* .toBuffer();
*
* @example
* const output = await sharp(input)
* .negate({ alpha: false })
* .toBuffer();
*
* @param {Object} [options]
* @param {Boolean} [options.alpha=true] Whether or not to negate any alpha channel
* @returns {Sharp}

View File

@@ -151,6 +151,8 @@ function toBuffer (options, callback) {
* The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
*
* EXIF metadata is unsupported for TIFF output.
*
* @example
* sharp('input.jpg')
* .withMetadata()

View File

@@ -1,7 +1,7 @@
{
"name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
"version": "0.30.3",
"version": "0.30.4",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp",
"contributors": [
@@ -81,7 +81,8 @@
"Taneli Vatanen <taneli.vatanen@gmail.com>",
"Joris Dugué <zaruike10@gmail.com>",
"Chris Banks <christopher.bradley.banks@gmail.com>",
"Ompal Singh <ompal.hitm09@gmail.com>"
"Ompal Singh <ompal.hitm09@gmail.com>",
"Brodan <christopher.hranj@gmail.com"
],
"scripts": {
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)",
@@ -126,11 +127,11 @@
"vips"
],
"dependencies": {
"color": "^4.2.1",
"color": "^4.2.3",
"detect-libc": "^2.0.1",
"node-addon-api": "^4.3.0",
"prebuild-install": "^7.0.1",
"semver": "^7.3.5",
"semver": "^7.3.7",
"simple-get": "^4.0.1",
"tar-fs": "^2.1.1",
"tunnel-agent": "^0.6.0"

View File

@@ -85,7 +85,9 @@ namespace sharp {
descriptor->buffer = buffer.Data();
descriptor->isBuffer = TRUE;
}
descriptor->failOnError = AttrAsBool(input, "failOnError");
descriptor->failOn = static_cast<VipsFailOn>(
vips_enum_from_nick(nullptr, VIPS_TYPE_FAIL_ON,
AttrAsStr(input, "failOn").data()));
// Density for vector-based input
if (HasAttr(input, "density")) {
descriptor->density = AttrAsDouble(input, "density");
@@ -329,7 +331,7 @@ namespace sharp {
try {
vips::VOption *option = VImage::option()
->set("access", descriptor->access)
->set("fail", descriptor->failOnError);
->set("fail_on", descriptor->failOn);
if (descriptor->unlimited && (imageType == ImageType::SVG || imageType == ImageType::PNG)) {
option->set("unlimited", TRUE);
}
@@ -375,12 +377,6 @@ namespace sharp {
VImage::option()->set("mean", descriptor->createNoiseMean)->set("sigma", descriptor->createNoiseSigma)));
}
image = image.bandjoin(bands);
image = image.cast(VipsBandFormat::VIPS_FORMAT_UCHAR);
if (channels < 3) {
image = image.colourspace(VIPS_INTERPRETATION_B_W);
} else {
image = image.colourspace(VIPS_INTERPRETATION_sRGB);
}
} else {
std::vector<double> background = {
descriptor->createBackground[0],
@@ -392,7 +388,8 @@ namespace sharp {
}
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
}
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
image.get_image()->Type = image.guess_interpretation();
image = image.cast(VIPS_FORMAT_UCHAR);
imageType = ImageType::RAW;
} else {
// From filesystem
@@ -402,13 +399,13 @@ namespace sharp {
throw vips::VError("Input file is missing, did you mean "
"sharp(Buffer.from('" + descriptor->file.substr(0, 8) + "...')?");
}
throw vips::VError("Input file is missing");
throw vips::VError("Input file is missing: " + descriptor->file);
}
if (imageType != ImageType::UNKNOWN) {
try {
vips::VOption *option = VImage::option()
->set("access", descriptor->access)
->set("fail", descriptor->failOnError);
->set("fail_on", descriptor->failOn);
if (descriptor->unlimited && (imageType == ImageType::SVG || imageType == ImageType::PNG)) {
option->set("unlimited", TRUE);
}

View File

@@ -48,7 +48,7 @@ namespace sharp {
std::string name;
std::string file;
char *buffer;
bool failOnError;
VipsFailOn failOn;
int limitInputPixels;
bool unlimited;
VipsAccess access;
@@ -74,7 +74,7 @@ namespace sharp {
InputDescriptor():
buffer(nullptr),
failOnError(TRUE),
failOn(VIPS_FAIL_ON_WARNING),
limitInputPixels(0x3FFF * 0x3FFF),
unlimited(FALSE),
access(VIPS_ACCESS_RANDOM),

View File

@@ -200,7 +200,7 @@ class PipelineWorker : public Napi::AsyncWorker {
vips::VOption *option = VImage::option()
->set("access", baton->input->access)
->set("shrink", jpegShrinkOnLoad)
->set("fail", baton->input->failOnError);
->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);
@@ -214,7 +214,7 @@ class PipelineWorker : public Napi::AsyncWorker {
vips::VOption *option = VImage::option()
->set("access", baton->input->access)
->set("scale", scale)
->set("fail", baton->input->failOnError);
->set("fail_on", baton->input->failOn);
if (inputImageType == sharp::ImageType::WEBP) {
option->set("n", baton->input->pages);
option->set("page", baton->input->page);
@@ -241,8 +241,10 @@ class PipelineWorker : public Napi::AsyncWorker {
// Reload SVG file
image = VImage::svgload(const_cast<char*>(baton->input->file.data()), option);
}
sharp::SetDensity(image, baton->input->density);
if (image.width() > 32767 || image.height() > 32767) {
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);
@@ -260,6 +262,10 @@ class PipelineWorker : public Napi::AsyncWorker {
sharp::SetDensity(image, baton->input->density);
}
} else {
if (inputImageType == sharp::ImageType::SVG && (image.width() > 32767 || image.height() > 32767)) {
throw vips::VError("Input SVG image exceeds 32767x32767 pixel limit");
}
}
// Any pre-shrinking may already have been done

View File

@@ -3,56 +3,70 @@
const assert = require('assert');
const fs = require('fs');
const sharp = require('../../');
const sharp = require('../../lib');
const fixtures = require('../fixtures');
describe('failOnError', function () {
describe('failOn', () => {
it('handles truncated JPEG', function (done) {
sharp(fixtures.inputJpgTruncated, { failOnError: false })
.resize(320, 240)
sharp(fixtures.inputJpgTruncated, { failOn: 'none' })
.resize(32, 24)
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
assert.strictEqual(32, info.width);
assert.strictEqual(24, info.height);
fixtures.assertSimilar(fixtures.expected('truncated.jpg'), data, done);
});
});
it('handles truncated PNG, emits warnings', function (done) {
let isWarningEmitted = false;
sharp(fixtures.inputPngTruncated, { failOnError: false })
sharp(fixtures.inputPngTruncated, { failOn: 'none' })
.on('warning', function (warning) {
assert.ok(warning.includes('not enough data') || warning.includes('end of stream'));
isWarningEmitted = true;
})
.resize(320, 240)
.resize(32, 24)
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(true, isWarningEmitted);
assert.strictEqual('png', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
assert.strictEqual(32, info.width);
assert.strictEqual(24, info.height);
done();
});
});
it('rejects invalid values', function () {
assert.doesNotThrow(function () {
sharp(fixtures.inputJpg, { failOnError: true });
});
it('throws for invalid options', () => {
assert.throws(
() => sharp({ failOn: 'zoinks' }),
/Expected one of: none, truncated, error, warning for failOn but received zoinks of type string/
);
assert.throws(
() => sharp({ failOn: 1 }),
/Expected one of: none, truncated, error, warning for failOn but received 1 of type number/
);
});
assert.throws(function () {
sharp(fixtures.inputJpg, { failOnError: 'zoinks' });
});
assert.throws(function () {
sharp(fixtures.inputJpg, { failOnError: 1 });
});
it('deprecated failOnError', () => {
assert.doesNotThrow(
() => sharp({ failOnError: true })
);
assert.doesNotThrow(
() => sharp({ failOnError: false })
);
assert.throws(
() => sharp({ failOnError: 'zoinks' }),
/Expected boolean for failOnError but received zoinks of type string/
);
assert.throws(
() => sharp({ failOnError: 1 }),
/Expected boolean for failOnError but received 1 of type number/
);
});
it('returns errors to callback for truncated JPEG', function (done) {
sharp(fixtures.inputJpgTruncated).toBuffer(function (err, data, info) {
sharp(fixtures.inputJpgTruncated, { failOn: 'truncated' }).toBuffer(function (err, data, info) {
assert.ok(err.message.includes('VipsJpeg: Premature end of'), err);
assert.strictEqual(data, undefined);
assert.strictEqual(info, undefined);
@@ -61,7 +75,7 @@ describe('failOnError', function () {
});
it('returns errors to callback for truncated PNG', function (done) {
sharp(fixtures.inputPngTruncated).toBuffer(function (err, data, info) {
sharp(fixtures.inputPngTruncated, { failOn: 'truncated' }).toBuffer(function (err, data, info) {
assert.ok(err.message.includes('read error'), err);
assert.strictEqual(data, undefined);
assert.strictEqual(info, undefined);
@@ -70,7 +84,7 @@ describe('failOnError', function () {
});
it('rejects promises for truncated JPEG', function (done) {
sharp(fixtures.inputJpgTruncated)
sharp(fixtures.inputJpgTruncated, { failOn: 'error' })
.toBuffer()
.then(() => {
throw new Error('Expected rejection');
@@ -80,8 +94,8 @@ describe('failOnError', function () {
});
});
it('handles stream-based input', function () {
const writable = sharp({ failOnError: false });
it('handles stream-based input', async () => {
const writable = sharp({ failOn: 'none' }).resize(32, 24);
fs.createReadStream(fixtures.inputJpgTruncated).pipe(writable);
return writable.toBuffer();
});

View File

@@ -182,6 +182,24 @@ describe('Input/output', function () {
assert.strictEqual(info.height, 1);
});
it('Read from Uint8ClampedArray with byteOffset and output to Buffer', async () => {
// since a Uint8ClampedArray is the same as Uint8Array but clamps the values
// between 0-255 it seemed good to add this also
const uint8array = Uint8ClampedArray.from([0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255]);
const uint8ArrayWithByteOffset = new Uint8ClampedArray(uint8array.buffer, 3, 6);
const { data, info } = await sharp(uint8ArrayWithByteOffset, {
raw: {
width: 2,
height: 1,
channels: 3
}
}).toBuffer({ resolveWithObject: true });
assert.deepStrictEqual(Uint8ClampedArray.from([255, 255, 255, 0, 0, 0]), new Uint8ClampedArray(data));
assert.strictEqual(info.width, 2);
assert.strictEqual(info.height, 1);
});
it('Stream should emit info event', function (done) {
const readable = fs.createReadStream(fixtures.inputJpg);
const writable = fs.createWriteStream(outputJpg);
@@ -439,7 +457,7 @@ describe('Input/output', function () {
done();
}).catch(function (err) {
assert(err instanceof Error);
assert.strictEqual('Input file is missing', err.message);
assert.strictEqual('Input file is missing: does-not-exist', err.message);
done();
});
});

View File

@@ -186,6 +186,22 @@ describe('Negate', function () {
});
});
it('negate create', async () => {
const [r, g, b] = await sharp({
create: {
width: 1,
height: 1,
channels: 3,
background: { r: 10, g: 20, b: 30 }
}
})
.negate()
.raw()
.toBuffer();
assert.deepStrictEqual({ r, g, b }, { r: 245, g: 235, b: 225 });
});
it('invalid alpha value', function () {
assert.throws(function () {
sharp(fixtures.inputWebPWithTransparency).negate({ alpha: 'non-bool' });

View File

@@ -19,7 +19,7 @@ describe('Gaussian noise', function () {
sigma: 30
}
}
});
}).toColourspace('b-w');
noise.toFile(output, function (err, info) {
if (err) throw err;
assert.strictEqual('png', info.format);
@@ -131,6 +131,7 @@ describe('Gaussian noise', function () {
}
});
noise
.toColourspace('b-w')
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(1, info.channels);

View File

@@ -136,6 +136,20 @@ describe('SVG input', function () {
assert.strictEqual(info.channels, 4);
});
it('Fails to render SVG larger than 32767x32767', () =>
assert.rejects(
() => sharp(Buffer.from('<svg width="32768" height="1" />')).toBuffer(),
/Input SVG image exceeds 32767x32767 pixel limit/
)
);
it('Fails to render scaled SVG larger than 32767x32767', () =>
assert.rejects(
() => sharp(Buffer.from('<svg width="32767" height="1" />')).resize(32768).toBuffer(),
/Input SVG image will exceed 32767x32767 pixel limit when scaled/
)
);
it('Detects SVG passed as a string', () =>
assert.rejects(
() => sharp('<svg></svg>').toBuffer(),