Compare commits

...

4 Commits

Author SHA1 Message Date
Lovell Fuller
6d18c6fdc6 Add Media Type to metadata response #4492 2026-02-01 21:20:16 +00:00
Lovell Fuller
2291c0b864 Switch from custom VError to standard runtime_error 2026-01-23 22:42:23 +00:00
Lovell Fuller
ed6b7384d0 Ensure TIFF output bitdepth option is limited to 1, 2 or 4 2026-01-23 21:29:40 +00:00
Lovell Fuller
ef77388a73 Force MSVC to use exception handling
As of 8.18.0, libvips C++ wrapper retrieves error messages at
exception construction time rather than lazily when accessed.

On Windows this led to error messages being referenced rather
than copied, leading to access beyond their lifetime and possible
corruption.
2026-01-22 12:52:48 +00:00
20 changed files with 137 additions and 50 deletions

View File

@@ -18,6 +18,7 @@ Dimensions in the response will respect the `page` and `pages` properties of the
A `Promise` is returned when `callback` is not provided.
- `format`: Name of decoder used to parse image e.g. `jpeg`, `png`, `webp`, `gif`, `svg`, `heif`, `tiff`
- `mediaType`: Media Type (MIME Type) e.g. `image/jpeg`, `image/png`, `image/svg+xml`, `image/avif`
- `size`: Total size of image in bytes, for Stream and Buffer input only
- `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
- `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)

View File

@@ -717,7 +717,7 @@ instead of providing `xres` and `yres` in pixels/mm.
| [options.xres] | <code>number</code> | <code>1.0</code> | horizontal resolution in pixels/mm |
| [options.yres] | <code>number</code> | <code>1.0</code> | vertical resolution in pixels/mm |
| [options.resolutionUnit] | <code>string</code> | <code>&quot;&#x27;inch&#x27;&quot;</code> | resolution unit options: inch, cm |
| [options.bitdepth] | <code>number</code> | <code>8</code> | reduce bitdepth to 1, 2 or 4 bit |
| [options.bitdepth] | <code>number</code> | <code>0</code> | reduce bitdepth to 1, 2 or 4 bit |
| [options.miniswhite] | <code>boolean</code> | <code>false</code> | write 1-bit images as miniswhite |
**Example**

View File

@@ -20,6 +20,8 @@ slug: changelog/v0.35.0
* Upgrade to libvips v8.18.0 for upstream bug fixes.
* Ensure TIFF output `bitdepth` option is limited to 1, 2 or 4.
* Deprecate Windows 32-bit (win32-ia32) prebuilt binaries.
* Add AVIF/HEIF `tune` option for control over quality metrics.
@@ -41,4 +43,7 @@ slug: changelog/v0.35.0
* Ensure HEIF primary item is used as default page/frame.
[#4487](https://github.com/lovell/sharp/issues/4487)
* Add image Media Type (MIME Type) to metadata response.
[#4492](https://github.com/lovell/sharp/issues/4492)
* Add WebP `exact` option for control over transparent pixel colour values.

View File

@@ -366,7 +366,7 @@ const Sharp = function (input, options) {
tiffPredictor: 'horizontal',
tiffPyramid: false,
tiffMiniswhite: false,
tiffBitdepth: 8,
tiffBitdepth: 0,
tiffTile: false,
tiffTileHeight: 256,
tiffTileWidth: 256,

4
lib/index.d.ts vendored
View File

@@ -1479,8 +1479,8 @@ declare namespace sharp {
xres?: number | undefined;
/** Vertical resolution in pixels/mm (optional, default 1.0) */
yres?: number | undefined;
/** Reduce bitdepth to 1, 2 or 4 bit (optional, default 8) */
bitdepth?: 1 | 2 | 4 | 8 | undefined;
/** Reduce bitdepth to 1, 2 or 4 bit (optional) */
bitdepth?: 1 | 2 | 4 | undefined;
/** Write 1-bit images as miniswhite (optional, default false) */
miniswhite?: boolean | undefined;
/** Resolution unit options: inch, cm (optional, default 'inch') */

View File

@@ -567,6 +567,7 @@ function _isStreamInput () {
* A `Promise` is returned when `callback` is not provided.
*
* - `format`: Name of decoder used to parse image e.g. `jpeg`, `png`, `webp`, `gif`, `svg`, `heif`, `tiff`
* - `mediaType`: Media Type (MIME Type) e.g. `image/jpeg`, `image/png`, `image/svg+xml`, `image/avif`
* - `size`: Total size of image in bytes, for Stream and Buffer input only
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
* - `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)

View File

@@ -1055,7 +1055,7 @@ function trySetAnimationOptions (source, target) {
* @param {number} [options.xres=1.0] - horizontal resolution in pixels/mm
* @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
* @param {string} [options.resolutionUnit='inch'] - resolution unit options: inch, cm
* @param {number} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
* @param {number} [options.bitdepth=0] - reduce bitdepth to 1, 2 or 4 bit
* @param {boolean} [options.miniswhite=false] - write 1-bit images as miniswhite
* @returns {Sharp}
* @throws {Error} Invalid options
@@ -1070,10 +1070,10 @@ function tiff (options) {
}
}
if (is.defined(options.bitdepth)) {
if (is.integer(options.bitdepth) && is.inArray(options.bitdepth, [1, 2, 4, 8])) {
if (is.integer(options.bitdepth) && is.inArray(options.bitdepth, [1, 2, 4])) {
this.options.tiffBitdepth = options.bitdepth;
} else {
throw is.invalidParameterError('bitdepth', '1, 2, 4 or 8', options.bitdepth);
throw is.invalidParameterError('bitdepth', '1, 2 or 4', options.bitdepth);
}
}
// tiling

View File

@@ -21,6 +21,7 @@
'defines': [
'_VIPS_PUBLIC=__declspec(dllexport)',
'_ALLOW_KEYWORD_MACROS',
'_HAS_EXCEPTIONS=1',
'G_DISABLE_ASSERT',
'G_DISABLE_CAST_CHECKS',
'G_DISABLE_CHECKS'
@@ -148,7 +149,8 @@
['OS == "win"', {
'defines': [
'_ALLOW_KEYWORD_MACROS',
'_FILE_OFFSET_BITS=64'
'_FILE_OFFSET_BITS=64',
'_HAS_EXCEPTIONS=1'
],
'link_settings': {
'libraries': [

View File

@@ -510,11 +510,11 @@ namespace sharp {
option = GetOptionsForImageType(imageType, descriptor);
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
}
} catch (vips::VError const &err) {
throw vips::VError(std::string("Input buffer has corrupt header: ") + err.what());
} catch (std::runtime_error const &err) {
throw std::runtime_error(std::string("Input buffer has corrupt header: ") + err.what());
}
} else {
throw vips::VError("Input buffer contains unsupported image format");
throw std::runtime_error("Input buffer contains unsupported image format");
}
}
} else {
@@ -585,10 +585,10 @@ namespace sharp {
imageType = DetermineImageType(descriptor->file.data());
if (imageType == ImageType::MISSING) {
if (descriptor->file.find("<svg") != std::string::npos) {
throw vips::VError("Input file is missing, did you mean "
throw std::runtime_error("Input file is missing, did you mean "
"sharp(Buffer.from('" + descriptor->file.substr(0, 8) + "...')?");
}
throw vips::VError("Input file is missing: " + descriptor->file);
throw std::runtime_error("Input file is missing: " + descriptor->file);
}
if (imageType != ImageType::UNKNOWN) {
try {
@@ -600,11 +600,11 @@ namespace sharp {
option = GetOptionsForImageType(imageType, descriptor);
image = VImage::new_from_file(descriptor->file.data(), option);
}
} catch (vips::VError const &err) {
throw vips::VError(std::string("Input file has corrupt header: ") + err.what());
} catch (std::runtime_error const &err) {
throw std::runtime_error(std::string("Input file has corrupt header: ") + err.what());
}
} else {
throw vips::VError("Input file contains unsupported image format");
throw std::runtime_error("Input file contains unsupported image format");
}
}
}
@@ -612,7 +612,7 @@ namespace sharp {
// Limit input images to a given number of pixels, where pixels = width * height
if (descriptor->limitInputPixels > 0 &&
static_cast<uint64_t>(image.width()) * image.height() > descriptor->limitInputPixels) {
throw vips::VError("Input image exceeds pixel limit");
throw std::runtime_error("Input image exceeds pixel limit");
}
return std::make_tuple(image, imageType);
}
@@ -788,19 +788,19 @@ namespace sharp {
: image.height();
if (imageType == ImageType::JPEG) {
if (image.width() > 65535 || height > 65535) {
throw vips::VError("Processed image is too large for the JPEG format");
throw std::runtime_error("Processed image is too large for the JPEG format");
}
} else if (imageType == ImageType::WEBP) {
if (image.width() > 16383 || height > 16383) {
throw vips::VError("Processed image is too large for the WebP format");
throw std::runtime_error("Processed image is too large for the WebP format");
}
} else if (imageType == ImageType::GIF) {
if (image.width() > 65535 || height > 65535) {
throw vips::VError("Processed image is too large for the GIF format");
throw std::runtime_error("Processed image is too large for the GIF format");
}
} else if (imageType == ImageType::HEIF) {
if (image.width() > 16384 || height > 16384) {
throw vips::VError("Processed image is too large for the HEIF format");
throw std::runtime_error("Processed image is too large for the HEIF format");
}
}
}

View File

@@ -31,7 +31,7 @@ class MetadataWorker : public Napi::AsyncWorker {
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
try {
std::tie(image, imageType) = OpenInput(baton->input);
} catch (vips::VError const &err) {
} catch (std::runtime_error const &err) {
(baton->err).append(err.what());
}
if (imageType != sharp::ImageType::UNKNOWN) {
@@ -151,6 +151,50 @@ class MetadataWorker : public Napi::AsyncWorker {
}
// PNG comments
vips_image_map(image.get_image(), readPNGComment, &baton->comments);
// Media type
std::string mediaType;
switch (imageType) {
case sharp::ImageType::JPEG:
case sharp::ImageType::PNG:
case sharp::ImageType::WEBP:
case sharp::ImageType::JP2:
case sharp::ImageType::TIFF:
case sharp::ImageType::GIF:
case sharp::ImageType::FITS:
case sharp::ImageType::JXL:
baton->mediaType = "image/" + baton->format;
break;
case sharp::ImageType::SVG:
baton->mediaType = "image/svg+xml";
break;
case sharp::ImageType::HEIF:
if (baton->compression == "av1") {
baton->mediaType = "image/avif";
} else if (baton->compression == "hevc") {
baton->mediaType = "image/heic";
}
break;
case sharp::ImageType::PDF:
baton->mediaType = "application/pdf";
break;
case sharp::ImageType::OPENSLIDE:
baton->mediaType = "image/tiff";
break;
case sharp::ImageType::PPM:
baton->mediaType = "image/x-portable-pixmap";
break;
case sharp::ImageType::EXR:
baton->mediaType = "image/x-exr";
break;
case sharp::ImageType::RAD:
baton->mediaType = "image/vnd.radiance";
break;
case sharp::ImageType::UHDR:
baton->mediaType = "image/jpeg";
break;
default:
break;
}
}
// Clean up
@@ -172,6 +216,9 @@ class MetadataWorker : public Napi::AsyncWorker {
if (baton->err.empty()) {
Napi::Object info = Napi::Object::New(env);
info.Set("format", baton->format);
if (!baton->mediaType.empty()) {
info.Set("mediaType", baton->mediaType);
}
if (baton->input->bufferLength > 0) {
info.Set("size", baton->input->bufferLength);
}

View File

@@ -19,6 +19,7 @@ struct MetadataBaton {
sharp::InputDescriptor *input;
// Output
std::string format;
std::string mediaType;
int width;
int height;
std::string space;

View File

@@ -14,7 +14,6 @@
#include "./operations.h"
using vips::VImage;
using vips::VError;
namespace sharp {
/*
@@ -287,7 +286,7 @@ namespace sharp {
*/
VImage Trim(VImage image, std::vector<double> background, double threshold, bool const lineArt, int const margin) {
if (image.width() < 3 && image.height() < 3) {
throw VError("Image to trim must be at least 3x3 pixels");
throw std::runtime_error("Image to trim must be at least 3x3 pixels");
}
if (background.size() == 0) {
// Top-left pixel provides the default background colour if none is given
@@ -361,7 +360,7 @@ namespace sharp {
VImage Linear(VImage image, std::vector<double> const a, std::vector<double> const b) {
size_t const bands = static_cast<size_t>(image.bands());
if (a.size() > bands) {
throw VError("Band expansion using linear is unsupported");
throw std::runtime_error("Band expansion using linear is unsupported");
}
bool const uchar = !Is16Bit(image.interpretation());
if (image.has_alpha() && a.size() != bands && (a.size() == 1 || a.size() == bands - 1 || bands - 1 == 1)) {

View File

@@ -274,7 +274,7 @@ class PipelineWorker : public Napi::AsyncWorker {
}
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");
throw std::runtime_error("Input SVG image will exceed 32767x32767 pixel limit when scaled");
}
} else if (inputImageType == sharp::ImageType::PDF) {
if (baton->input->buffer != nullptr) {
@@ -290,7 +290,7 @@ class PipelineWorker : public Napi::AsyncWorker {
}
} else {
if (inputImageType == sharp::ImageType::SVG && (image.width() > 32767 || image.height() > 32767)) {
throw vips::VError("Input SVG image exceeds 32767x32767 pixel limit");
throw std::runtime_error("Input SVG image exceeds 32767x32767 pixel limit");
}
}
if (baton->input->autoOrient) {
@@ -675,7 +675,7 @@ class PipelineWorker : public Napi::AsyncWorker {
// Verify within current dimensions
if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
throw vips::VError("Image to composite must have same dimensions or smaller");
throw std::runtime_error("Image to composite must have same dimensions or smaller");
}
// Check if overlay is tiled
if (composite->tile) {
@@ -1086,20 +1086,19 @@ class PipelineWorker : public Napi::AsyncWorker {
// Get raw image data
baton->bufferOut = static_cast<char*>(image.write_to_memory(&baton->bufferOutLength));
if (baton->bufferOut == nullptr) {
(baton->err).append("Could not allocate enough memory for raw output");
return Error();
throw std::runtime_error("Could not allocate enough memory for raw output");
}
baton->formatOut = "raw";
} else {
// Unsupported output format
(baton->err).append("Unsupported output format ");
auto unsupported = std::string("Unsupported output format ");
if (baton->formatOut == "input") {
(baton->err).append("when trying to match input format of ");
(baton->err).append(ImageTypeId(inputImageType));
unsupported.append("when trying to match input format of ");
unsupported.append(ImageTypeId(inputImageType));
} else {
(baton->err).append(baton->formatOut);
unsupported.append(baton->formatOut);
}
return Error();
throw std::runtime_error(unsupported);
}
} else {
// File output
@@ -1274,7 +1273,7 @@ class PipelineWorker : public Napi::AsyncWorker {
return Error();
}
}
} catch (vips::VError const &err) {
} catch (std::runtime_error const &err) {
char const *what = err.what();
if (what && what[0]) {
(baton->err).append(what);
@@ -1306,7 +1305,6 @@ class PipelineWorker : public Napi::AsyncWorker {
}
warning = sharp::VipsWarningPop();
}
if (baton->err.empty()) {
int width = baton->width;
int height = baton->height;
@@ -1407,7 +1405,7 @@ class PipelineWorker : public Napi::AsyncWorker {
void MultiPageUnsupported(int const pages, std::string op) {
if (pages > 1) {
throw vips::VError(op + " is not supported for multi-page images");
throw std::runtime_error(op + " is not supported for multi-page images");
}
}

View File

@@ -365,7 +365,7 @@ struct PipelineBaton {
tiffBigtiff(false),
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
tiffPyramid(false),
tiffBitdepth(8),
tiffBitdepth(0),
tiffMiniswhite(false),
tiffTile(false),
tiffTileHeight(256),

View File

@@ -39,7 +39,7 @@ class StatsWorker : public Napi::AsyncWorker {
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
try {
std::tie(image, imageType) = OpenInput(baton->input);
} catch (vips::VError const &err) {
} catch (std::runtime_error const &err) {
(baton->err).append(err.what());
}
if (imageType != sharp::ImageType::UNKNOWN) {
@@ -92,7 +92,7 @@ class StatsWorker : public Napi::AsyncWorker {
baton->dominantRed = dx * 16 + 8;
baton->dominantGreen = dy * 16 + 8;
baton->dominantBlue = dz * 16 + 8;
} catch (vips::VError const &err) {
} catch (std::runtime_error const &err) {
(baton->err).append(err.what());
}
}
@@ -112,7 +112,6 @@ class StatsWorker : public Napi::AsyncWorker {
debuglog.Call(Receiver().Value(), { Napi::String::New(env, warning) });
warning = sharp::VipsWarningPop();
}
if (baton->err.empty()) {
// Stats Object
Napi::Object info = Napi::Object::New(env);

View File

@@ -244,7 +244,7 @@ Napi::Value _maxColourDistance(const Napi::CallbackInfo& info) {
}
// Calculate colour distance
maxColourDistance = image1.dE00(image2).max();
} catch (vips::VError const &err) {
} catch (std::runtime_error const &err) {
throw Napi::Error::New(env, err.what());
}

View File

@@ -36,6 +36,7 @@ describe('AVIF', () => {
density: 72,
depth: 'uchar',
format: 'jpeg',
mediaType: 'image/jpeg',
hasAlpha: false,
hasProfile: false,
// 32 / (2048 / 858) = 13.40625
@@ -64,6 +65,7 @@ describe('AVIF', () => {
compression: 'av1',
depth: 'uchar',
format: 'heif',
mediaType: 'image/avif',
hasAlpha: false,
hasProfile: false,
height: 26,
@@ -93,6 +95,7 @@ describe('AVIF', () => {
compression: 'av1',
depth: 'uchar',
format: 'heif',
mediaType: 'image/avif',
hasAlpha: false,
hasProfile: false,
height: 24,
@@ -119,6 +122,7 @@ describe('AVIF', () => {
compression: 'av1',
depth: 'uchar',
format: 'heif',
mediaType: 'image/avif',
hasAlpha: false,
hasProfile: false,
height: 13,
@@ -148,6 +152,7 @@ describe('AVIF', () => {
compression: 'av1',
depth: 'uchar',
format: 'heif',
mediaType: 'image/avif',
hasAlpha: true,
hasProfile: false,
height: 300,
@@ -178,6 +183,7 @@ describe('AVIF', () => {
compression: 'av1',
depth: 'uchar',
format: 'heif',
mediaType: 'image/avif',
hasAlpha: false,
hasProfile: false,
height: 26,
@@ -236,6 +242,7 @@ describe('AVIF', () => {
void exif;
assert.deepStrictEqual(metadata, {
format: 'heif',
mediaType: 'image/avif',
width: 4096,
height: 800,
space: 'srgb',
@@ -259,6 +266,7 @@ describe('AVIF', () => {
const { size, ...pngMetadata } = await sharp(data).metadata();
assert.deepStrictEqual(pngMetadata, {
format: 'png',
mediaType: 'image/png',
width: 4096,
height: 800,
space: 'srgb',

View File

@@ -19,6 +19,7 @@ describe('Image metadata', () => {
sharp(fixtures.inputJpg).metadata((err, metadata) => {
if (err) throw err;
assert.strictEqual('jpeg', metadata.format);
assert.strictEqual('image/jpeg', metadata.mediaType);
assert.strictEqual('undefined', typeof metadata.size);
assert.strictEqual(2725, metadata.width);
assert.strictEqual(2225, metadata.height);
@@ -41,6 +42,7 @@ describe('Image metadata', () => {
sharp(fixtures.inputJpgWithExif).metadata((err, metadata) => {
if (err) throw err;
assert.strictEqual('jpeg', metadata.format);
assert.strictEqual('image/jpeg', metadata.mediaType);
assert.strictEqual('undefined', typeof metadata.size);
assert.strictEqual(450, metadata.width);
assert.strictEqual(600, metadata.height);
@@ -66,6 +68,7 @@ describe('Image metadata', () => {
const profile = icc.parse(metadata.icc);
assert.strictEqual('object', typeof profile);
assert.strictEqual('Generic RGB Profile', profile.description);
assert.strictEqual('image/jpeg', metadata.mediaType);
done();
});
});
@@ -92,6 +95,7 @@ describe('Image metadata', () => {
sharp(fixtures.inputTiff).metadata((err, metadata) => {
if (err) throw err;
assert.strictEqual('tiff', metadata.format);
assert.strictEqual('image/tiff', metadata.mediaType);
assert.strictEqual('undefined', typeof metadata.size);
assert.strictEqual(2464, metadata.width);
assert.strictEqual(3248, metadata.height);
@@ -111,6 +115,7 @@ describe('Image metadata', () => {
assert.strictEqual('undefined', typeof metadata.xmp);
assert.strictEqual('undefined', typeof metadata.xmpAsString);
assert.strictEqual('inch', metadata.resolutionUnit);
assert.strictEqual('image/tiff', metadata.mediaType);
done();
});
});
@@ -119,6 +124,7 @@ describe('Image metadata', () => {
sharp(fixtures.inputTiffMultipage).metadata((err, metadata) => {
if (err) throw err;
assert.strictEqual('tiff', metadata.format);
assert.strictEqual('image/tiff', metadata.mediaType);
assert.strictEqual('undefined', typeof metadata.size);
assert.strictEqual(2464, metadata.width);
assert.strictEqual(3248, metadata.height);
@@ -142,6 +148,7 @@ describe('Image metadata', () => {
sharp(fixtures.inputPng).metadata((err, metadata) => {
if (err) throw err;
assert.strictEqual('png', metadata.format);
assert.strictEqual('image/png', metadata.mediaType);
assert.strictEqual('undefined', typeof metadata.size);
assert.strictEqual(2809, metadata.width);
assert.strictEqual(2074, metadata.height);
@@ -166,6 +173,7 @@ describe('Image metadata', () => {
sharp(fixtures.inputPngTestJoinChannel).metadata((err, metadata) => {
if (err) throw err;
assert.strictEqual('png', metadata.format);
assert.strictEqual('image/png', metadata.mediaType);
assert.strictEqual('undefined', typeof metadata.size);
assert.strictEqual(320, metadata.width);
assert.strictEqual(240, metadata.height);
@@ -191,6 +199,7 @@ describe('Image metadata', () => {
sharp(fixtures.inputPngWithTransparency).metadata((err, metadata) => {
if (err) throw err;
assert.strictEqual('png', metadata.format);
assert.strictEqual('image/png', metadata.mediaType);
assert.strictEqual('undefined', typeof metadata.size);
assert.strictEqual(2048, metadata.width);
assert.strictEqual(1536, metadata.height);
@@ -225,6 +234,7 @@ describe('Image metadata', () => {
height: 32,
isPalette: false,
isProgressive: false,
mediaType: 'image/png',
space: 'b-w',
width: 32,
autoOrient: {
@@ -250,6 +260,7 @@ describe('Image metadata', () => {
height: 32,
isPalette: false,
isProgressive: false,
mediaType: 'image/png',
space: 'grey16',
width: 32,
autoOrient: {
@@ -263,6 +274,7 @@ describe('Image metadata', () => {
sharp(fixtures.inputWebP).metadata((err, metadata) => {
if (err) throw err;
assert.strictEqual('webp', metadata.format);
assert.strictEqual('image/webp', metadata.mediaType);
assert.strictEqual('undefined', typeof metadata.size);
assert.strictEqual(1024, metadata.width);
assert.strictEqual(772, metadata.height);
@@ -285,11 +297,12 @@ describe('Image metadata', () => {
sharp(fixtures.inputWebPAnimated)
.metadata()
.then(({
format, width, height, space, channels, depth,
format, mediaType, width, height, space, channels, depth,
isProgressive, pages, loop, delay, hasProfile,
hasAlpha
}) => {
assert.strictEqual(format, 'webp');
assert.strictEqual(mediaType, 'image/webp');
assert.strictEqual(width, 80);
assert.strictEqual(height, 80);
assert.strictEqual(space, 'srgb');
@@ -308,11 +321,12 @@ describe('Image metadata', () => {
sharp(fixtures.inputWebPAnimated, { pages: -1 })
.metadata()
.then(({
format, width, height, space, channels, depth,
format, mediaType, width, height, space, channels, depth,
isProgressive, pages, pageHeight, loop, delay,
hasProfile, hasAlpha
}) => {
assert.strictEqual(format, 'webp');
assert.strictEqual(mediaType, 'image/webp');
assert.strictEqual(width, 80);
assert.strictEqual(height, 720);
assert.strictEqual(space, 'srgb');
@@ -332,11 +346,12 @@ describe('Image metadata', () => {
sharp(fixtures.inputWebPAnimatedLoop3)
.metadata()
.then(({
format, width, height, space, channels, depth,
format, mediaType, width, height, space, channels, depth,
isProgressive, pages, loop, delay, hasProfile,
hasAlpha
}) => {
assert.strictEqual(format, 'webp');
assert.strictEqual(mediaType, 'image/webp');
assert.strictEqual(width, 370);
assert.strictEqual(height, 285);
assert.strictEqual(space, 'srgb');
@@ -355,6 +370,7 @@ describe('Image metadata', () => {
sharp(fixtures.inputGif).metadata((err, metadata) => {
if (err) throw err;
assert.strictEqual('gif', metadata.format);
assert.strictEqual('image/gif', metadata.mediaType);
assert.strictEqual('undefined', typeof metadata.size);
assert.strictEqual(800, metadata.width);
assert.strictEqual(533, metadata.height);
@@ -375,6 +391,7 @@ describe('Image metadata', () => {
sharp(fixtures.inputGifGreyPlusAlpha).metadata((err, metadata) => {
if (err) throw err;
assert.strictEqual('gif', metadata.format);
assert.strictEqual('image/gif', metadata.mediaType);
assert.strictEqual('undefined', typeof metadata.size);
assert.strictEqual(2, metadata.width);
assert.strictEqual(1, metadata.height);
@@ -395,11 +412,12 @@ describe('Image metadata', () => {
sharp(fixtures.inputGifAnimated)
.metadata()
.then(({
format, width, height, space, channels, depth,
format, mediaType, width, height, space, channels, depth,
isProgressive, pages, loop, delay, background,
hasProfile, hasAlpha
}) => {
assert.strictEqual(format, 'gif');
assert.strictEqual(mediaType, 'image/gif');
assert.strictEqual(width, 80);
assert.strictEqual(height, 80);
assert.strictEqual(space, 'srgb');
@@ -419,11 +437,12 @@ describe('Image metadata', () => {
sharp(fixtures.inputGifAnimatedLoop3)
.metadata()
.then(({
format, width, height, space, channels, depth,
format, mediaType, width, height, space, channels, depth,
isProgressive, pages, loop, delay, hasProfile,
hasAlpha
}) => {
assert.strictEqual(format, 'gif');
assert.strictEqual(mediaType, 'image/gif');
assert.strictEqual(width, 370);
assert.strictEqual(height, 285);
assert.strictEqual(space, 'srgb');
@@ -462,6 +481,7 @@ describe('Image metadata', () => {
it('File in, Promise out', (_t, done) => {
sharp(fixtures.inputJpg).metadata().then((metadata) => {
assert.strictEqual('jpeg', metadata.format);
assert.strictEqual('image/jpeg', metadata.mediaType);
assert.strictEqual('undefined', typeof metadata.size);
assert.strictEqual(2725, metadata.width);
assert.strictEqual(2225, metadata.height);
@@ -508,6 +528,7 @@ describe('Image metadata', () => {
const pipeline = sharp();
pipeline.metadata().then((metadata) => {
assert.strictEqual('jpeg', metadata.format);
assert.strictEqual('image/jpeg', metadata.mediaType);
assert.strictEqual(829183, metadata.size);
assert.strictEqual(2725, metadata.width);
assert.strictEqual(2225, metadata.height);
@@ -559,6 +580,7 @@ describe('Image metadata', () => {
const pipeline = sharp().metadata((err, metadata) => {
if (err) throw err;
assert.strictEqual('jpeg', metadata.format);
assert.strictEqual('image/jpeg', metadata.mediaType);
assert.strictEqual(829183, metadata.size);
assert.strictEqual(2725, metadata.width);
assert.strictEqual(2225, metadata.height);
@@ -583,6 +605,7 @@ describe('Image metadata', () => {
image.metadata((err, metadata) => {
if (err) throw err;
assert.strictEqual('jpeg', metadata.format);
assert.strictEqual('image/jpeg', metadata.mediaType);
assert.strictEqual('undefined', typeof metadata.size);
assert.strictEqual(2725, metadata.width);
assert.strictEqual(2225, metadata.height);
@@ -917,6 +940,7 @@ describe('Image metadata', () => {
.metadata()
.then(metadata => {
assert.strictEqual(metadata.format, 'tiff');
assert.strictEqual(metadata.mediaType, 'image/tiff');
assert.strictEqual(metadata.width, 317);
assert.strictEqual(metadata.height, 211);
assert.strictEqual(metadata.space, 'rgb16');
@@ -931,6 +955,7 @@ describe('Image metadata', () => {
const metadata = await sharp(fixtures.inputAvif).metadata();
assert.deepStrictEqual(metadata, {
format: 'heif',
mediaType: 'image/avif',
width: 2048,
height: 858,
space: 'srgb',
@@ -1014,6 +1039,7 @@ describe('Image metadata', () => {
const metadata = await sharp(fixtures.inputJpgLossless).metadata();
assert.deepStrictEqual(metadata, {
format: 'jpeg',
mediaType: 'image/jpeg',
width: 227,
height: 149,
space: 'srgb',

View File

@@ -145,6 +145,7 @@ describe('PNG', () => {
width: 68
},
format: 'png',
mediaType: 'image/png',
width: 68,
height: 68,
space: 'srgb',

View File

@@ -122,7 +122,6 @@ describe('TIFF', () => {
sharp(fixtures.inputTiff8BitDepth)
.toColourspace('b-w') // can only squash 1 band uchar images
.tiff({
bitdepth: 8,
compression: 'none',
predictor: 'none'
})
@@ -154,7 +153,7 @@ describe('TIFF', () => {
it('Invalid TIFF bitdepth value throws error', () => {
assert.throws(() => {
sharp().tiff({ bitdepth: 3 });
}, /Error: Expected 1, 2, 4 or 8 for bitdepth but received 3 of type number/);
}, /Error: Expected 1, 2 or 4 for bitdepth but received 3 of type number/);
});
it('TIFF setting xres and yres on file', () =>