Expose unlimited option for SVG and PNG input #2984

This commit is contained in:
Lovell Fuller 2021-11-24 12:35:20 +00:00
parent 549219f32a
commit 407bfcb42a
6 changed files with 38 additions and 6 deletions

View File

@ -18,6 +18,9 @@ Requires libvips v8.12.0
[#2976](https://github.com/lovell/sharp/pull/2976) [#2976](https://github.com/lovell/sharp/pull/2976)
[@driannaude](https://github.com/driannaude) [@driannaude](https://github.com/driannaude)
* Expose `unlimited` option for SVG and PNG input, switches off safety features.
[#2984](https://github.com/lovell/sharp/issues/2984)
## v0.29 - *circle* ## v0.29 - *circle*
Requires libvips v8.11.3 Requires libvips v8.11.3

View File

@ -103,6 +103,7 @@ const debuglog = util.debuglog('sharp');
* @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels * @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. * (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). * An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
* @param {boolean} [options.unlimited=false] - Set this to `true` to remove safety features that help prevent memory exhaustion (SVG, PNG).
* @param {boolean} [options.sequentialRead=false] - Set this to `true` to use sequential rather than random access where possible. * @param {boolean} [options.sequentialRead=false] - 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. * This can reduce memory usage and might improve performance on some systems.
* @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000. * @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.

View File

@ -9,9 +9,9 @@ const sharp = require('./sharp');
* @private * @private
*/ */
function _inputOptionsFromObject (obj) { function _inputOptionsFromObject (obj) {
const { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages, subifd } = obj; const { raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd } = obj;
return [raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages, subifd].some(is.defined) return [raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd].some(is.defined)
? { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages, subifd } ? { raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd }
: undefined; : undefined;
} }
@ -23,6 +23,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
const inputDescriptor = { const inputDescriptor = {
failOnError: true, failOnError: true,
limitInputPixels: Math.pow(0x3FFF, 2), limitInputPixels: Math.pow(0x3FFF, 2),
unlimited: false,
sequentialRead: false sequentialRead: false
}; };
if (is.string(input)) { if (is.string(input)) {
@ -83,6 +84,14 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
throw is.invalidParameterError('limitInputPixels', 'integer >= 0', inputOptions.limitInputPixels); throw is.invalidParameterError('limitInputPixels', 'integer >= 0', inputOptions.limitInputPixels);
} }
} }
// unlimited
if (is.defined(inputOptions.unlimited)) {
if (is.bool(inputOptions.unlimited)) {
inputDescriptor.unlimited = inputOptions.unlimited;
} else {
throw is.invalidParameterError('unlimited', 'boolean', inputOptions.unlimited);
}
}
// sequentialRead // sequentialRead
if (is.defined(inputOptions.sequentialRead)) { if (is.defined(inputOptions.sequentialRead)) {
if (is.bool(inputOptions.sequentialRead)) { if (is.bool(inputOptions.sequentialRead)) {

View File

@ -132,6 +132,8 @@ namespace sharp {
descriptor->limitInputPixels = AttrAsUint32(input, "limitInputPixels"); descriptor->limitInputPixels = AttrAsUint32(input, "limitInputPixels");
// Allow switch from random to sequential access // Allow switch from random to sequential access
descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM; descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
// Remove safety features and allow unlimited SVG/PNG input
descriptor->unlimited = AttrAsBool(input, "unlimited");
return descriptor; return descriptor;
} }
@ -328,7 +330,7 @@ namespace sharp {
vips::VOption *option = VImage::option() vips::VOption *option = VImage::option()
->set("access", descriptor->access) ->set("access", descriptor->access)
->set("fail", descriptor->failOnError); ->set("fail", descriptor->failOnError);
if (imageType == ImageType::SVG) { if (descriptor->unlimited && (imageType == ImageType::SVG || imageType == ImageType::PNG)) {
option->set("unlimited", TRUE); option->set("unlimited", TRUE);
} }
if (imageType == ImageType::SVG || imageType == ImageType::PDF) { if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
@ -403,7 +405,7 @@ namespace sharp {
vips::VOption *option = VImage::option() vips::VOption *option = VImage::option()
->set("access", descriptor->access) ->set("access", descriptor->access)
->set("fail", descriptor->failOnError); ->set("fail", descriptor->failOnError);
if (imageType == ImageType::SVG) { if (descriptor->unlimited && (imageType == ImageType::SVG || imageType == ImageType::PNG)) {
option->set("unlimited", TRUE); option->set("unlimited", TRUE);
} }
if (imageType == ImageType::SVG || imageType == ImageType::PDF) { if (imageType == ImageType::SVG || imageType == ImageType::PDF) {

View File

@ -50,6 +50,7 @@ namespace sharp {
char *buffer; char *buffer;
bool failOnError; bool failOnError;
int limitInputPixels; int limitInputPixels;
bool unlimited;
VipsAccess access; VipsAccess access;
size_t bufferLength; size_t bufferLength;
bool isBuffer; bool isBuffer;
@ -75,6 +76,7 @@ namespace sharp {
buffer(nullptr), buffer(nullptr),
failOnError(TRUE), failOnError(TRUE),
limitInputPixels(0x3FFF * 0x3FFF), limitInputPixels(0x3FFF * 0x3FFF),
unlimited(FALSE),
access(VIPS_ACCESS_RANDOM), access(VIPS_ACCESS_RANDOM),
bufferLength(0), bufferLength(0),
isBuffer(FALSE), isBuffer(FALSE),

View File

@ -648,6 +648,19 @@ describe('Input/output', function () {
}); });
}); });
describe('Switch off safety limits for PNG/SVG input', () => {
it('Valid', () => {
assert.doesNotThrow(() => {
sharp({ unlimited: true });
});
});
it('Invalid', () => {
assert.throws(() => {
sharp({ unlimited: -1 });
}, /Expected boolean for unlimited but received -1 of type number/);
});
});
describe('Limit pixel count of input image', () => { describe('Limit pixel count of input image', () => {
it('Invalid fails - negative', () => { it('Invalid fails - negative', () => {
assert.throws(() => { assert.throws(() => {
@ -671,7 +684,9 @@ describe('Input/output', function () {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.metadata() .metadata()
.then(({ width, height }) => .then(({ width, height }) =>
sharp(fixtures.inputJpg, { limitInputPixels: width * height }).toBuffer() sharp(fixtures.inputJpg, { limitInputPixels: width * height })
.resize(2)
.toBuffer()
) )
); );