From 1a563360c64878d3ce9060cddff9c10c1b350401 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Tue, 8 Nov 2022 19:53:14 +0000 Subject: [PATCH] Fix errors for missing OpenJPEG (#3442) Fixes couple of minor issues with JP2 errors: 1. The tests passed as false-positives even if regex is changed to arbitary pattern, because the promise returned from `assert.rejects` was ignored and the test ended prematurely. This is fixed by removing `{ ... }` around the test function body. 2. This, in turn, hid an issue with `toFile` not throwing the expected error message which was instead propagating `Error: VipsOperation: class "jp2ksave" not found` from libvips. This is now fixed by manually checking the extension before calling into libvips. 3. Pre-creating error instances like `errJp2Save` did is sometimes tempting, but is problematic for debugging because it hides the actual stacktrace of the error (the stacktrace is collected at the moment of `new Error` creation). This is now turned into a function that creates error with the right stack. --- lib/output.js | 8 ++++++-- test/unit/jp2.js | 16 ++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/output.js b/lib/output.js index 65281123..a5194bff 100644 --- a/lib/output.js +++ b/lib/output.js @@ -25,7 +25,9 @@ const formats = new Map([ ['j2c', 'jp2'] ]); -const errJp2Save = new Error('JP2 output requires libvips with support for OpenJPEG'); +const jp2Regex = /\.jp[2x]|j2[kc]$/i; + +const errJp2Save = () => new Error('JP2 output requires libvips with support for OpenJPEG'); const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math.log2(colours))); @@ -68,6 +70,8 @@ function toFile (fileOut, callback) { err = new Error('Missing output file path'); } else if (is.string(this.options.input.file) && path.resolve(this.options.input.file) === path.resolve(fileOut)) { err = new Error('Cannot use same file for input and output'); + } else if (jp2Regex.test(fileOut) && !this.constructor.format.jp2k.output.file) { + err = errJp2Save(); } if (err) { if (is.fn(callback)) { @@ -630,7 +634,7 @@ function gif (options) { /* istanbul ignore next */ function jp2 (options) { if (!this.constructor.format.jp2k.output.buffer) { - throw errJp2Save; + throw errJp2Save(); } if (is.object(options)) { if (is.defined(options.quality)) { diff --git a/test/unit/jp2.js b/test/unit/jp2.js index fd305f77..4b7dcf4c 100644 --- a/test/unit/jp2.js +++ b/test/unit/jp2.js @@ -8,20 +8,20 @@ const fixtures = require('../fixtures'); describe('JP2 output', () => { if (!sharp.format.jp2k.input.buffer) { - it('JP2 output should fail due to missing OpenJPEG', () => { - assert.rejects(() => + it('JP2 output should fail due to missing OpenJPEG', () => + assert.rejects(async () => sharp(fixtures.inputJpg) .jp2() .toBuffer(), /JP2 output requires libvips with support for OpenJPEG/ - ); - }); + ) + ); - it('JP2 file output should fail due to missing OpenJPEG', () => { - assert.rejects(async () => await sharp().toFile('test.jp2'), + it('JP2 file output should fail due to missing OpenJPEG', () => + assert.rejects(async () => sharp(fixtures.inputJpg).toFile('test.jp2'), /JP2 output requires libvips with support for OpenJPEG/ - ); - }); + ) + ); } else { it('JP2 Buffer to PNG Buffer', () => { sharp(fs.readFileSync(fixtures.inputJp2))