From 9755629cfd34140c5e99dfbda748a32d57a94b08 Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Sun, 12 Dec 2021 09:14:26 +0000 Subject: [PATCH] Ensure ops without multi-page support reject (#3010) --- src/pipeline.cc | 13 ++++++++++++- test/unit/affine.js | 9 +++++++++ test/unit/resize-cover.js | 24 ++++++++++++++++++++++++ test/unit/rotate.js | 28 ++++++++++++++++++++++++++++ test/unit/trim.js | 8 ++++++++ 5 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/pipeline.cc b/src/pipeline.cc index a6a041a0..6f51da18 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -98,6 +98,7 @@ class PipelineWorker : public Napi::AsyncWorker { image = sharp::RemoveExifOrientation(image); } if (baton->rotationAngle != 0.0) { + MultiPageUnsupported(nPages, "Rotate"); std::vector background; std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, FALSE); image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background)); @@ -106,6 +107,7 @@ class PipelineWorker : public Napi::AsyncWorker { // Trim if (baton->trimThreshold > 0.0) { + MultiPageUnsupported(nPages, "Trim"); image = sharp::Trim(image, baton->trimThreshold); baton->trimOffsetLeft = image.xoffset(); baton->trimOffsetTop = image.yoffset(); @@ -461,8 +463,9 @@ class PipelineWorker : public Napi::AsyncWorker { ? sharp::CropMultiPage(image, left, top, width, height, nPages, &targetPageHeight) : image.extract_area(left, top, width, height); - } else if (nPages == 1) { // Skip smart crop for multi-page images + } else { // Attention-based or Entropy-based crop + MultiPageUnsupported(nPages, "Resize strategy"); image = image.tilecache(VImage::option() ->set("access", VIPS_ACCESS_RANDOM) ->set("threaded", TRUE)); @@ -477,6 +480,7 @@ class PipelineWorker : public Napi::AsyncWorker { // Rotate post-extract non-90 angle if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) { + MultiPageUnsupported(nPages, "Rotate"); std::vector background; std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, shouldPremultiplyAlpha); image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background)); @@ -499,6 +503,7 @@ class PipelineWorker : public Napi::AsyncWorker { // Affine transform if (baton->affineMatrix.size() > 0) { + MultiPageUnsupported(nPages, "Affine"); std::vector background; std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha); image = image.affine(baton->affineMatrix, VImage::option()->set("background", background) @@ -1224,6 +1229,12 @@ class PipelineWorker : public Napi::AsyncWorker { Napi::FunctionReference debuglog; Napi::FunctionReference queueListener; + void MultiPageUnsupported(int const pages, std::string op) { + if (pages > 1) { + throw vips::VError(op + " is not supported for multi-page images"); + } + } + /* Calculate the angle of rotation and need-to-flip for the given Exif orientation By default, returns zero, i.e. no rotation. diff --git a/test/unit/affine.js b/test/unit/affine.js index 03f4aced..b430d8b5 100644 --- a/test/unit/affine.js +++ b/test/unit/affine.js @@ -155,6 +155,15 @@ describe('Affine transform', () => { fixtures.assertSimilar(fixtures.expected('affine-background-all-offsets-expected.jpg'), data, done); }); }); + + it('Animated image rejects', () => + assert.rejects(() => sharp(fixtures.inputGifAnimated, { animated: true }) + .affine([1, 1, 1, 1]) + .toBuffer(), + /Affine is not supported for multi-page images/ + ) + ); + describe('Interpolations', () => { const input = fixtures.inputJpg320x240; const inputWidth = 320; diff --git a/test/unit/resize-cover.js b/test/unit/resize-cover.js index 75d9a73a..4274c9cd 100644 --- a/test/unit/resize-cover.js +++ b/test/unit/resize-cover.js @@ -347,6 +347,18 @@ describe('Resize fit=cover', function () { fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done); }); }); + + it('Animated image rejects', () => + assert.rejects(() => sharp(fixtures.inputGifAnimated, { animated: true }) + .resize({ + width: 100, + height: 8, + position: sharp.strategy.entropy + }) + .toBuffer(), + /Resize strategy is not supported for multi-page images/ + ) + ); }); describe('Attention strategy', function () { @@ -403,5 +415,17 @@ describe('Resize fit=cover', function () { fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done); }); }); + + it('Animated image rejects', () => + assert.rejects(() => sharp(fixtures.inputGifAnimated, { animated: true }) + .resize({ + width: 100, + height: 8, + position: sharp.strategy.attention + }) + .toBuffer(), + /Resize strategy is not supported for multi-page images/ + ) + ); }); }); diff --git a/test/unit/rotate.js b/test/unit/rotate.js index 51bea7d9..c869940e 100644 --- a/test/unit/rotate.js +++ b/test/unit/rotate.js @@ -272,6 +272,34 @@ describe('Rotation', function () { }); }); + it('Animated image rotate-then-extract rejects', () => + assert.rejects(() => sharp(fixtures.inputGifAnimated, { animated: true }) + .rotate(1) + .extract({ + top: 1, + left: 1, + width: 10, + height: 10 + }) + .toBuffer(), + /Rotate is not supported for multi-page images/ + ) + ); + + it('Animated image extract-then-rotate rejects', () => + assert.rejects(() => sharp(fixtures.inputGifAnimated, { animated: true }) + .extract({ + top: 1, + left: 1, + width: 10, + height: 10 + }) + .rotate(1) + .toBuffer(), + /Rotate is not supported for multi-page images/ + ) + ); + it('Flip - vertical', function (done) { sharp(fixtures.inputJpg) .resize(320) diff --git a/test/unit/trim.js b/test/unit/trim.js index 2591c013..efb618f2 100644 --- a/test/unit/trim.js +++ b/test/unit/trim.js @@ -120,6 +120,14 @@ describe('Trim borders', function () { ) ); + it('Animated image rejects', () => + assert.rejects(() => sharp(fixtures.inputGifAnimated, { animated: true }) + .trim() + .toBuffer(), + /Trim is not supported for multi-page images/ + ) + ); + describe('Invalid thresholds', function () { [-1, 'fail', {}].forEach(function (threshold) { it(JSON.stringify(threshold), function () {