'use strict'; var fs = require('fs'); var assert = require('assert'); var semver = require('semver'); var sharp = require('../../index'); var fixtures = require('../fixtures'); sharp.cache(0); describe('Input/output', function() { it('Read from File and write to Stream', function(done) { var writable = fs.createWriteStream(fixtures.outputJpg); writable.on('finish', function() { sharp(fixtures.outputJpg).toBuffer(function(err, data, info) { if (err) throw err; assert.strictEqual(true, data.length > 0); assert.strictEqual(data.length, info.size); assert.strictEqual('jpeg', info.format); assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); fs.unlinkSync(fixtures.outputJpg); done(); }); }); sharp(fixtures.inputJpg).resize(320, 240).pipe(writable); }); it('Read from Buffer and write to Stream', function(done) { var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg); var writable = fs.createWriteStream(fixtures.outputJpg); writable.on('finish', function() { sharp(fixtures.outputJpg).toBuffer(function(err, data, info) { if (err) throw err; assert.strictEqual(true, data.length > 0); assert.strictEqual(data.length, info.size); assert.strictEqual('jpeg', info.format); assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); fs.unlinkSync(fixtures.outputJpg); done(); }); }); sharp(inputJpgBuffer).resize(320, 240).pipe(writable); }); it('Read from Stream and write to File', function(done) { var readable = fs.createReadStream(fixtures.inputJpg); var pipeline = sharp().resize(320, 240).toFile(fixtures.outputJpg, function(err, info) { if (err) throw err; assert.strictEqual(true, info.size > 0); assert.strictEqual('jpeg', info.format); assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); fs.unlinkSync(fixtures.outputJpg); done(); }); readable.pipe(pipeline); }); it('Read from Stream and write to Buffer', function(done) { var readable = fs.createReadStream(fixtures.inputJpg); var pipeline = sharp().resize(320, 240).toBuffer(function(err, data, info) { if (err) throw err; assert.strictEqual(true, data.length > 0); assert.strictEqual(data.length, info.size); assert.strictEqual('jpeg', info.format); assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); done(); }); readable.pipe(pipeline); }); it('Read from Stream and write to Buffer via Promise', function(done) { var readable = fs.createReadStream(fixtures.inputJpg); var pipeline = sharp().resize(1, 1); pipeline.toBuffer().then(function(data) { assert.strictEqual(true, data.length > 0); done(); }).catch(function(err) { throw err; }); readable.pipe(pipeline); }); it('Read from Stream and write to Stream', function(done) { var readable = fs.createReadStream(fixtures.inputJpg); var writable = fs.createWriteStream(fixtures.outputJpg); writable.on('finish', function() { sharp(fixtures.outputJpg).toBuffer(function(err, data, info) { if (err) throw err; assert.strictEqual(true, data.length > 0); assert.strictEqual(data.length, info.size); assert.strictEqual('jpeg', info.format); assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); fs.unlinkSync(fixtures.outputJpg); done(); }); }); var pipeline = sharp().resize(320, 240); readable.pipe(pipeline).pipe(writable); }); it('Handle Stream to Stream error ', function(done) { var pipeline = sharp().resize(320, 240); var anErrorWasEmitted = false; pipeline.on('error', function(err) { anErrorWasEmitted = !!err; }).on('end', function() { assert(anErrorWasEmitted); fs.unlinkSync(fixtures.outputJpg); done(); }); var readableButNotAnImage = fs.createReadStream(__filename); var writable = fs.createWriteStream(fixtures.outputJpg); readableButNotAnImage.pipe(pipeline).pipe(writable); }); it('Handle File to Stream error', function(done) { var readableButNotAnImage = sharp(__filename).resize(320, 240); var anErrorWasEmitted = false; readableButNotAnImage.on('error', function(err) { anErrorWasEmitted = !!err; }).on('end', function() { assert(anErrorWasEmitted); fs.unlinkSync(fixtures.outputJpg); done(); }); var writable = fs.createWriteStream(fixtures.outputJpg); readableButNotAnImage.pipe(writable); }); it('Sequential read, force JPEG', function(done) { sharp(fixtures.inputJpg) .sequentialRead() .resize(320, 240) .toFormat(sharp.format.jpeg) .toBuffer(function(err, data, info) { if (err) throw err; assert.strictEqual(true, data.length > 0); assert.strictEqual(data.length, info.size); assert.strictEqual('jpeg', info.format); assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); done(); }); }); it('Not sequential read, force JPEG', function(done) { sharp(fixtures.inputJpg) .sequentialRead(false) .resize(320, 240) .toFormat('jpeg') .toBuffer(function(err, data, info) { if (err) throw err; assert.strictEqual(true, data.length > 0); assert.strictEqual(data.length, info.size); assert.strictEqual('jpeg', info.format); assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); done(); }); }); it('Fail when output File is input File', function(done) { sharp(fixtures.inputJpg).toFile(fixtures.inputJpg, function(err) { assert(!!err); done(); }); }); it('Fail when output File is input File via Promise', function(done) { sharp(fixtures.inputJpg).toFile(fixtures.inputJpg).then(function(data) { assert(false); done(); }).catch(function(err) { assert(!!err); done(); }); }); it('Fail when output File is empty', function(done) { sharp(fixtures.inputJpg).toFile('', function(err) { assert(!!err); done(); }); }); it('Fail when output File is empty via Promise', function(done) { sharp(fixtures.inputJpg).toFile('').then(function(data) { assert(false); done(); }).catch(function(err) { assert(!!err); done(); }); }); it('Fail when input is empty Buffer', function(done) { sharp(new Buffer(0)).toBuffer().then(function () { assert(false); done(); }).catch(function (err) { assert(err instanceof Error); done(); }); }); it('Fail when input is invalid Buffer', function(done) { sharp(new Buffer([0x1, 0x2, 0x3, 0x4])).toBuffer().then(function () { assert(false); done(); }).catch(function (err) { assert(err instanceof Error); done(); }); }); it('Promises/A+', function(done) { sharp(fixtures.inputJpg).resize(320, 240).toBuffer().then(function(data) { sharp(data).toBuffer(function(err, data, info) { if (err) throw err; assert.strictEqual(true, data.length > 0); assert.strictEqual(data.length, info.size); assert.strictEqual('jpeg', info.format); assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); done(); }); }).catch(function(err) { throw err; }); }); it('JPEG quality', function(done) { sharp(fixtures.inputJpg).resize(320, 240).quality(70).toBuffer(function(err, buffer70) { if (err) throw err; sharp(fixtures.inputJpg).resize(320, 240).toBuffer(function(err, buffer80) { if (err) throw err; sharp(fixtures.inputJpg).resize(320, 240).quality(90).toBuffer(function(err, buffer90) { if (err) throw err; assert(buffer70.length < buffer80.length); assert(buffer80.length < buffer90.length); done(); }); }); }); }); it('Invalid quality', function(done) { var isValid = true; try { sharp(fixtures.inputJpg).quality(-1); } catch (err) { isValid = false; } assert.strictEqual(false, isValid); done(); }); it('Progressive image', function(done) { sharp(fixtures.inputJpg) .resize(320, 240) .png() .progressive(false) .toBuffer(function(err, nonProgressiveData, nonProgressiveInfo) { if (err) throw err; assert.strictEqual(true, nonProgressiveData.length > 0); assert.strictEqual(nonProgressiveData.length, nonProgressiveInfo.size); assert.strictEqual('png', nonProgressiveInfo.format); assert.strictEqual(320, nonProgressiveInfo.width); assert.strictEqual(240, nonProgressiveInfo.height); sharp(nonProgressiveData) .progressive() .toBuffer(function(err, progressiveData, progressiveInfo) { if (err) throw err; assert.strictEqual(true, progressiveData.length > 0); assert.strictEqual(progressiveData.length, progressiveInfo.size); assert.strictEqual(true, progressiveData.length > nonProgressiveData.length); assert.strictEqual('png', progressiveInfo.format); assert.strictEqual(320, progressiveInfo.width); assert.strictEqual(240, progressiveInfo.height); done(); }); }); }); it('WebP output', function(done) { sharp(fixtures.inputJpg) .resize(320, 240) .toFormat(sharp.format.webp) .toBuffer(function(err, data, info) { if (err) throw err; assert.strictEqual(true, data.length > 0); assert.strictEqual('webp', info.format); assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); done(); }); }); it('Invalid output format', function(done) { var isValid = false; try { sharp().toFormat('zoinks'); isValid = true; } catch (e) {} assert(!isValid); done(); }); it('File input with corrupt header fails gracefully', function(done) { sharp(fixtures.inputJpgWithCorruptHeader) .toBuffer(function(err) { assert.strictEqual(true, !!err); done(); }); }); it('Buffer input with corrupt header fails gracefully', function(done) { sharp(fs.readFileSync(fixtures.inputJpgWithCorruptHeader)) .toBuffer(function(err) { assert.strictEqual(true, !!err); done(); }); }); describe('Output filename without extension uses input format', function() { it('JPEG', function(done) { sharp(fixtures.inputJpg).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) { if (err) throw err; assert.strictEqual(true, info.size > 0); assert.strictEqual('jpeg', info.format); assert.strictEqual(320, info.width); assert.strictEqual(80, info.height); fs.unlinkSync(fixtures.outputZoinks); done(); }); }); it('PNG', function(done) { sharp(fixtures.inputPng).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) { if (err) throw err; assert.strictEqual(true, info.size > 0); assert.strictEqual('png', info.format); assert.strictEqual(320, info.width); assert.strictEqual(80, info.height); fs.unlinkSync(fixtures.outputZoinks); done(); }); }); it('Transparent PNG', function(done) { sharp(fixtures.inputPngWithTransparency).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) { if (err) throw err; assert.strictEqual(true, info.size > 0); assert.strictEqual('png', info.format); assert.strictEqual(320, info.width); assert.strictEqual(80, info.height); done(); }); }); it('WebP', function(done) { sharp(fixtures.inputWebP).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) { if (err) throw err; assert.strictEqual(true, info.size > 0); assert.strictEqual('webp', info.format); assert.strictEqual(320, info.width); assert.strictEqual(80, info.height); fs.unlinkSync(fixtures.outputZoinks); done(); }); }); it('TIFF', function(done) { sharp(fixtures.inputTiff).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) { if (err) throw err; assert.strictEqual(true, info.size > 0); assert.strictEqual('tiff', info.format); assert.strictEqual(320, info.width); assert.strictEqual(80, info.height); fs.unlinkSync(fixtures.outputZoinks); done(); }); }); it('Fail with GIF', function(done) { sharp(fixtures.inputGif).resize(320, 80).toFile(fixtures.outputZoinks, function(err) { assert(!!err); done(); }); }); }); describe('PNG output', function() { it('compression level is valid', function(done) { var isValid = false; try { sharp().compressionLevel(0); isValid = true; } catch (e) {} assert(isValid); done(); }); it('compression level is invalid', function(done) { var isValid = false; try { sharp().compressionLevel(-1); isValid = true; } catch (e) {} assert(!isValid); done(); }); if (semver.gte(sharp.libvipsVersion(), '7.42.0')) { it('withoutAdaptiveFiltering generates smaller file [libvips ' + sharp.libvipsVersion() + '>=7.42.0]', function(done) { // First generate with adaptive filtering sharp(fixtures.inputPng) .resize(320, 240) .withoutAdaptiveFiltering(false) .toBuffer(function(err, adaptiveData, adaptiveInfo) { if (err) throw err; assert.strictEqual(true, adaptiveData.length > 0); assert.strictEqual(adaptiveData.length, adaptiveInfo.size); assert.strictEqual('png', adaptiveInfo.format); assert.strictEqual(320, adaptiveInfo.width); assert.strictEqual(240, adaptiveInfo.height); // Then generate without sharp(fixtures.inputPng) .resize(320, 240) .withoutAdaptiveFiltering() .toBuffer(function(err, withoutAdaptiveData, withoutAdaptiveInfo) { if (err) throw err; assert.strictEqual(true, withoutAdaptiveData.length > 0); assert.strictEqual(withoutAdaptiveData.length, withoutAdaptiveInfo.size); assert.strictEqual('png', withoutAdaptiveInfo.format); assert.strictEqual(320, withoutAdaptiveInfo.width); assert.strictEqual(240, withoutAdaptiveInfo.height); assert.strictEqual(true, withoutAdaptiveData.length < adaptiveData.length); done(); }); }); }); } }); it('Without chroma subsampling generates larger file', function(done) { // First generate with chroma subsampling (default) sharp(fixtures.inputJpg) .resize(320, 240) .withoutChromaSubsampling(false) .toBuffer(function(err, withChromaSubsamplingData, withChromaSubsamplingInfo) { if (err) throw err; assert.strictEqual(true, withChromaSubsamplingData.length > 0); assert.strictEqual(withChromaSubsamplingData.length, withChromaSubsamplingInfo.size); assert.strictEqual('jpeg', withChromaSubsamplingInfo.format); assert.strictEqual(320, withChromaSubsamplingInfo.width); assert.strictEqual(240, withChromaSubsamplingInfo.height); // Then generate without sharp(fixtures.inputJpg) .resize(320, 240) .withoutChromaSubsampling() .toBuffer(function(err, withoutChromaSubsamplingData, withoutChromaSubsamplingInfo) { if (err) throw err; assert.strictEqual(true, withoutChromaSubsamplingData.length > 0); assert.strictEqual(withoutChromaSubsamplingData.length, withoutChromaSubsamplingInfo.size); assert.strictEqual('jpeg', withoutChromaSubsamplingInfo.format); assert.strictEqual(320, withoutChromaSubsamplingInfo.width); assert.strictEqual(240, withoutChromaSubsamplingInfo.height); assert.strictEqual(true, withChromaSubsamplingData.length < withoutChromaSubsamplingData.length); done(); }); }); }); if (sharp.format.magick.input.file) { it('Convert SVG, if supported, to PNG', function(done) { sharp(fixtures.inputSvg) .resize(100, 100) .toFormat('png') .toFile(fixtures.path('output.svg.png'), function(err, info) { if (err) { assert.strictEqual('Input file is of an unsupported image format', err.message); } else { assert.strictEqual(true, info.size > 0); assert.strictEqual('png', info.format); assert.strictEqual(100, info.width); assert.strictEqual(100, info.height); } done(); }); }); } if (sharp.format.magick.input.file) { it('Convert PSD to PNG', function(done) { sharp(fixtures.inputPsd) .resize(320, 240) .toFormat(sharp.format.png) .toFile(fixtures.path('output.psd.png'), function(err, info) { if (err) throw err; assert.strictEqual(true, info.size > 0); assert.strictEqual('png', info.format); assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); done(); }); }); } if (sharp.format.dz.output.file) { it('Convert JPEG to DZ', function(done) { sharp(fixtures.inputJpg) .resize(320, 240) .toFormat(sharp.format.dz) .toFile(fixtures.path('output.jpg.dzi'), function(err, info) { if (err) throw err; assert.strictEqual(true, info.size > 0); assert.strictEqual('dz', info.format); assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); assert.strictEqual(256, info.tileSize); assert.strictEqual(0, info.tileOverlap); done(); }); }); it('Convert JPEG to DZ (test tileSize and tileOverlap)', function(done) { sharp(fixtures.inputJpg) .resize(320, 240) .toFormat(sharp.format.dz) .tileSize(512) .tileOverlap(10) .toFile(fixtures.path('output.tileTest.jpg.dzi'), function(err, info) { if (err) throw err; assert.strictEqual(true, info.size > 0); assert.strictEqual('dz', info.format); assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); assert.strictEqual(512, info.tileSize); assert.strictEqual(10, info.tileOverlap); done(); }); }); } if (sharp.format.tiff.input.buffer) { it('Load TIFF from Buffer', function(done) { var inputTiffBuffer = fs.readFileSync(fixtures.inputTiff); sharp(inputTiffBuffer) .resize(320, 240) .jpeg() .toBuffer(function(err, data, info) { if (err) throw err; assert.strictEqual(true, data.length > 0); assert.strictEqual(data.length, info.size); assert.strictEqual('jpeg', info.format); assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); done(); }); }); } if (sharp.format.magick.input.buffer) { it('Load GIF from Buffer', function(done) { var inputGifBuffer = fs.readFileSync(fixtures.inputGif); sharp(inputGifBuffer) .resize(320, 240) .jpeg() .toBuffer(function(err, data, info) { if (err) throw err; assert.strictEqual(true, data.length > 0); assert.strictEqual(data.length, info.size); assert.strictEqual('jpeg', info.format); assert.strictEqual(320, info.width); assert.strictEqual(240, info.height); done(); }); }); } if (sharp.format.raw.output.buffer) { describe('Ouput raw, uncompressed image data', function() { it('1 channel greyscale image', function(done) { sharp(fixtures.inputJpg) .greyscale() .resize(32, 24) .raw() .toBuffer(function(err, data, info) { assert.strictEqual(32 * 24 * 1, info.size); assert.strictEqual(data.length, info.size); assert.strictEqual('raw', info.format); assert.strictEqual(32, info.width); assert.strictEqual(24, info.height); done(); }); }); it('3 channel colour image without transparency', function(done) { sharp(fixtures.inputJpg) .resize(32, 24) .toFormat('raw') .toBuffer(function(err, data, info) { assert.strictEqual(32 * 24 * 3, info.size); assert.strictEqual(data.length, info.size); assert.strictEqual('raw', info.format); assert.strictEqual(32, info.width); assert.strictEqual(24, info.height); done(); }); }); it('4 channel colour image with transparency', function(done) { sharp(fixtures.inputPngWithTransparency) .resize(32, 24) .toFormat(sharp.format.raw) .toBuffer(function(err, data, info) { assert.strictEqual(32 * 24 * 4, info.size); assert.strictEqual(data.length, info.size); assert.strictEqual('raw', info.format); assert.strictEqual(32, info.width); assert.strictEqual(24, info.height); done(); }); }); }); } describe('Limit pixel count of input image', function() { it('Invalid fails - negative', function(done) { var isValid = false; try { sharp().limitInputPixels(-1); isValid = true; } catch (e) {} assert(!isValid); done(); }); it('Invalid fails - float', function(done) { var isValid = false; try { sharp().limitInputPixels(12.3); isValid = true; } catch (e) {} assert(!isValid); done(); }); it('Invalid fails - huge', function(done) { var isValid = false; try { sharp().limitInputPixels(Math.pow(0x3FFF, 2) + 1); isValid = true; } catch (e) {} assert(!isValid); done(); }); it('Invalid fails - string', function(done) { var isValid = false; try { sharp().limitInputPixels('fail'); isValid = true; } catch (e) {} assert(!isValid); done(); }); it('Same size as input works', function(done) { sharp(fixtures.inputJpg).metadata(function(err, metadata) { if (err) throw err; sharp(fixtures.inputJpg) .limitInputPixels(metadata.width * metadata.height) .toBuffer(function(err) { assert.strictEqual(true, !err); done(); }); }); }); it('Smaller than input fails', function(done) { sharp(fixtures.inputJpg).metadata(function(err, metadata) { if (err) throw err; sharp(fixtures.inputJpg) .limitInputPixels(metadata.width * metadata.height - 1) .toBuffer(function(err) { assert.strictEqual(true, !!err); done(); }); }); }); }); if(sharp.format.openslide.input.file) { describe('Openslide output', function() { it('Aperio - convert SVS to PNG', function(done) { sharp(fixtures.inputSvs) .toFile(fixtures.path('output.svs.png'), function(err, info) { if (err) throw err; assert.strictEqual(true, info.size > 0); assert.strictEqual('png', info.format); assert.strictEqual(2220, info.width); assert.strictEqual(2967, info.height); done(); }); }); it('Aperio - convert SVS to JPEG', function(done) { sharp(fixtures.inputSvs) .toFile(fixtures.path('output.svs.jpeg'), function(err, info) { if (err) throw err; assert.strictEqual(true, info.size > 0); assert.strictEqual('jpeg', info.format); assert.strictEqual(2220, info.width); assert.strictEqual(2967, info.height); done(); }); }); it('Aperio - convert SVS to TIFF', function(done) { sharp(fixtures.inputSvs) .toFile(fixtures.path('output.svs.tiff'), function(err, info) { if (err) throw err; assert.strictEqual(true, info.size > 0); assert.strictEqual('tiff', info.format); assert.strictEqual(2220, info.width); assert.strictEqual(2967, info.height); done(); }); }); it('Aperio - convert SVS to WEBP', function(done) { sharp(fixtures.inputSvs) .toFile(fixtures.path('output.svs.webp'), function(err, info) { if (err) throw err; assert.strictEqual(true, info.size > 0); assert.strictEqual('webp', info.format); assert.strictEqual(2220, info.width); assert.strictEqual(2967, info.height); done(); }); }); it('Aperio - convert SVS to DZI', function(done) { sharp(fixtures.inputSvs) .toFile(fixtures.path('output.aperio.svs.dzi'), function(err, info) { if (err) throw err; assert.strictEqual(true, info.size > 0); assert.strictEqual('dz', info.format); assert.strictEqual(2220, info.width); assert.strictEqual(2967, info.height); assert.strictEqual(256, info.tileSize); assert.strictEqual(0, info.tileOverlap); done(); }); }); }); } });