mirror of
https://github.com/lovell/sharp.git
synced 2026-02-05 14:16:17 +01:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
596b38a3bb | ||
|
|
d31a91a599 | ||
|
|
400ef71b6f | ||
|
|
bb15cd9067 | ||
|
|
6ee6a226e1 | ||
|
|
94d51a94c8 |
@@ -44,6 +44,7 @@ A `Promise` is returned when `callback` is not provided.
|
|||||||
- `icc`: Buffer containing raw [ICC][3] profile data, if present
|
- `icc`: Buffer containing raw [ICC][3] profile data, if present
|
||||||
- `iptc`: Buffer containing raw IPTC data, if present
|
- `iptc`: Buffer containing raw IPTC data, if present
|
||||||
- `xmp`: Buffer containing raw XMP data, if present
|
- `xmp`: Buffer containing raw XMP data, if present
|
||||||
|
- `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,16 @@
|
|||||||
|
|
||||||
Requires libvips v8.8.1.
|
Requires libvips v8.8.1.
|
||||||
|
|
||||||
|
#### v0.23.4 - 5<sup>th</sup> December 2019
|
||||||
|
|
||||||
|
* Handle zero-length Buffer objects when using Node.js v13.2.0+.
|
||||||
|
|
||||||
|
* Expose raw TIFFTAG_PHOTOSHOP metadata.
|
||||||
|
[#1600](https://github.com/lovell/sharp/issues/1600)
|
||||||
|
|
||||||
|
* Improve thread safety by using copy-on-write when updating metadata.
|
||||||
|
[#1986](https://github.com/lovell/sharp/issues/1986)
|
||||||
|
|
||||||
#### v0.23.3 - 17<sup>th</sup> November 2019
|
#### v0.23.3 - 17<sup>th</sup> November 2019
|
||||||
|
|
||||||
* Ensure `trim` operation supports images contained in the alpha channel.
|
* Ensure `trim` operation supports images contained in the alpha channel.
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ Only 64-bit (x64) `node.exe` is supported.
|
|||||||
|
|
||||||
### FreeBSD
|
### FreeBSD
|
||||||
|
|
||||||
|
[](https://cirrus-ci.com/github/lovell/sharp)
|
||||||
|
|
||||||
libvips must be installed before `npm install` is run.
|
libvips must be installed before `npm install` is run.
|
||||||
|
|
||||||
This can be achieved via package or ports:
|
This can be achieved via package or ports:
|
||||||
@@ -126,6 +128,9 @@ https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=193528
|
|||||||
Set [NODE_MODULES_CACHE](https://devcenter.heroku.com/articles/nodejs-support#cache-behavior)
|
Set [NODE_MODULES_CACHE](https://devcenter.heroku.com/articles/nodejs-support#cache-behavior)
|
||||||
to `false` when using the `yarn` package manager.
|
to `false` when using the `yarn` package manager.
|
||||||
|
|
||||||
|
To reduce the effects of memory fragmentation, add the
|
||||||
|
[jemalloc buildpack](https://github.com/gaffneyc/heroku-buildpack-jemalloc).
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|
||||||
[Marc Bachmann](https://github.com/marcbachmann) maintains an
|
[Marc Bachmann](https://github.com/marcbachmann) maintains an
|
||||||
|
|||||||
@@ -207,6 +207,7 @@ function clone () {
|
|||||||
* - `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
|
* - `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
|
||||||
* - `iptc`: Buffer containing raw IPTC data, if present
|
* - `iptc`: Buffer containing raw IPTC data, if present
|
||||||
* - `xmp`: Buffer containing raw XMP data, if present
|
* - `xmp`: Buffer containing raw XMP data, if present
|
||||||
|
* - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const image = sharp(inputJpg);
|
* const image = sharp(inputJpg);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "sharp",
|
"name": "sharp",
|
||||||
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
|
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
|
||||||
"version": "0.23.3",
|
"version": "0.23.4",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://github.com/lovell/sharp",
|
"homepage": "https://github.com/lovell/sharp",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
"icc": "^1.0.0",
|
"icc": "^1.0.0",
|
||||||
"license-checker": "^25.0.1",
|
"license-checker": "^25.0.1",
|
||||||
"mocha": "^6.2.2",
|
"mocha": "^6.2.2",
|
||||||
"mock-fs": "^4.10.3",
|
"mock-fs": "^4.10.4",
|
||||||
"nyc": "^14.1.1",
|
"nyc": "^14.1.1",
|
||||||
"prebuild": "^9.1.1",
|
"prebuild": "^9.1.1",
|
||||||
"prebuild-ci": "^3.1.0",
|
"prebuild-ci": "^3.1.0",
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ namespace sharp {
|
|||||||
v8::Local<v8::Object> buffer = AttrAs<v8::Object>(input, "buffer");
|
v8::Local<v8::Object> buffer = AttrAs<v8::Object>(input, "buffer");
|
||||||
descriptor->bufferLength = node::Buffer::Length(buffer);
|
descriptor->bufferLength = node::Buffer::Length(buffer);
|
||||||
descriptor->buffer = node::Buffer::Data(buffer);
|
descriptor->buffer = node::Buffer::Data(buffer);
|
||||||
|
descriptor->isBuffer = TRUE;
|
||||||
buffersToPersist.push_back(buffer);
|
buffersToPersist.push_back(buffer);
|
||||||
}
|
}
|
||||||
descriptor->failOnError = AttrTo<bool>(input, "failOnError");
|
descriptor->failOnError = AttrTo<bool>(input, "failOnError");
|
||||||
@@ -246,7 +247,7 @@ namespace sharp {
|
|||||||
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor, VipsAccess accessMethod) {
|
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor, VipsAccess accessMethod) {
|
||||||
VImage image;
|
VImage image;
|
||||||
ImageType imageType;
|
ImageType imageType;
|
||||||
if (descriptor->buffer != nullptr) {
|
if (descriptor->isBuffer) {
|
||||||
if (descriptor->rawChannels > 0) {
|
if (descriptor->rawChannels > 0) {
|
||||||
// Raw, uncompressed pixel data
|
// Raw, uncompressed pixel data
|
||||||
image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
|
image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
|
||||||
@@ -277,7 +278,7 @@ namespace sharp {
|
|||||||
}
|
}
|
||||||
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
|
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
|
||||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||||
SetDensity(image, descriptor->density);
|
image = SetDensity(image, descriptor->density);
|
||||||
}
|
}
|
||||||
} catch (vips::VError const &err) {
|
} catch (vips::VError const &err) {
|
||||||
throw vips::VError(std::string("Input buffer has corrupt header: ") + err.what());
|
throw vips::VError(std::string("Input buffer has corrupt header: ") + err.what());
|
||||||
@@ -323,7 +324,7 @@ namespace sharp {
|
|||||||
}
|
}
|
||||||
image = VImage::new_from_file(descriptor->file.data(), option);
|
image = VImage::new_from_file(descriptor->file.data(), option);
|
||||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||||
SetDensity(image, descriptor->density);
|
image = SetDensity(image, descriptor->density);
|
||||||
}
|
}
|
||||||
} catch (vips::VError const &err) {
|
} catch (vips::VError const &err) {
|
||||||
throw vips::VError(std::string("Input file has corrupt header: ") + err.what());
|
throw vips::VError(std::string("Input file has corrupt header: ") + err.what());
|
||||||
@@ -370,15 +371,19 @@ namespace sharp {
|
|||||||
/*
|
/*
|
||||||
Set EXIF Orientation of image.
|
Set EXIF Orientation of image.
|
||||||
*/
|
*/
|
||||||
void SetExifOrientation(VImage image, int const orientation) {
|
VImage SetExifOrientation(VImage image, int const orientation) {
|
||||||
image.set(VIPS_META_ORIENTATION, orientation);
|
VImage copy = image.copy();
|
||||||
|
copy.set(VIPS_META_ORIENTATION, orientation);
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Remove EXIF Orientation from image.
|
Remove EXIF Orientation from image.
|
||||||
*/
|
*/
|
||||||
void RemoveExifOrientation(VImage image) {
|
VImage RemoveExifOrientation(VImage image) {
|
||||||
vips_image_remove(image.get_image(), VIPS_META_ORIENTATION);
|
VImage copy = image.copy();
|
||||||
|
copy.remove(VIPS_META_ORIENTATION);
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -398,11 +403,13 @@ namespace sharp {
|
|||||||
/*
|
/*
|
||||||
Set pixels/mm resolution based on a pixels/inch density.
|
Set pixels/mm resolution based on a pixels/inch density.
|
||||||
*/
|
*/
|
||||||
void SetDensity(VImage image, const double density) {
|
VImage SetDensity(VImage image, const double density) {
|
||||||
const double pixelsPerMm = density / 25.4;
|
const double pixelsPerMm = density / 25.4;
|
||||||
image.set("Xres", pixelsPerMm);
|
VImage copy = image.copy();
|
||||||
image.set("Yres", pixelsPerMm);
|
copy.set("Xres", pixelsPerMm);
|
||||||
image.set(VIPS_META_RESOLUTION_UNIT, "in");
|
copy.set("Yres", pixelsPerMm);
|
||||||
|
copy.set(VIPS_META_RESOLUTION_UNIT, "in");
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ namespace sharp {
|
|||||||
char *buffer;
|
char *buffer;
|
||||||
bool failOnError;
|
bool failOnError;
|
||||||
size_t bufferLength;
|
size_t bufferLength;
|
||||||
|
bool isBuffer;
|
||||||
double density;
|
double density;
|
||||||
int rawChannels;
|
int rawChannels;
|
||||||
int rawWidth;
|
int rawWidth;
|
||||||
@@ -64,6 +65,7 @@ namespace sharp {
|
|||||||
buffer(nullptr),
|
buffer(nullptr),
|
||||||
failOnError(TRUE),
|
failOnError(TRUE),
|
||||||
bufferLength(0),
|
bufferLength(0),
|
||||||
|
isBuffer(FALSE),
|
||||||
density(72.0),
|
density(72.0),
|
||||||
rawChannels(0),
|
rawChannels(0),
|
||||||
rawWidth(0),
|
rawWidth(0),
|
||||||
@@ -175,12 +177,12 @@ namespace sharp {
|
|||||||
/*
|
/*
|
||||||
Set EXIF Orientation of image.
|
Set EXIF Orientation of image.
|
||||||
*/
|
*/
|
||||||
void SetExifOrientation(VImage image, int const orientation);
|
VImage SetExifOrientation(VImage image, int const orientation);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Remove EXIF Orientation from image.
|
Remove EXIF Orientation from image.
|
||||||
*/
|
*/
|
||||||
void RemoveExifOrientation(VImage image);
|
VImage RemoveExifOrientation(VImage image);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Does this image have a non-default density?
|
Does this image have a non-default density?
|
||||||
@@ -195,7 +197,7 @@ namespace sharp {
|
|||||||
/*
|
/*
|
||||||
Set pixels/mm resolution based on a pixels/inch density.
|
Set pixels/mm resolution based on a pixels/inch density.
|
||||||
*/
|
*/
|
||||||
void SetDensity(VImage image, const double density);
|
VImage SetDensity(VImage image, const double density);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Check the proposed format supports the current dimensions.
|
Check the proposed format supports the current dimensions.
|
||||||
|
|||||||
@@ -116,6 +116,14 @@ class MetadataWorker : public Nan::AsyncWorker {
|
|||||||
memcpy(baton->xmp, xmp, xmpLength);
|
memcpy(baton->xmp, xmp, xmpLength);
|
||||||
baton->xmpLength = xmpLength;
|
baton->xmpLength = xmpLength;
|
||||||
}
|
}
|
||||||
|
// TIFFTAG_PHOTOSHOP
|
||||||
|
if (image.get_typeof(VIPS_META_PHOTOSHOP_NAME) == VIPS_TYPE_BLOB) {
|
||||||
|
size_t tifftagPhotoshopLength;
|
||||||
|
void const *tifftagPhotoshop = image.get_blob(VIPS_META_PHOTOSHOP_NAME, &tifftagPhotoshopLength);
|
||||||
|
baton->tifftagPhotoshop = static_cast<char *>(g_malloc(tifftagPhotoshopLength));
|
||||||
|
memcpy(baton->tifftagPhotoshop, tifftagPhotoshop, tifftagPhotoshopLength);
|
||||||
|
baton->tifftagPhotoshopLength = tifftagPhotoshopLength;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
@@ -189,6 +197,12 @@ class MetadataWorker : public Nan::AsyncWorker {
|
|||||||
New("xmp").ToLocalChecked(),
|
New("xmp").ToLocalChecked(),
|
||||||
Nan::NewBuffer(baton->xmp, baton->xmpLength, sharp::FreeCallback, nullptr).ToLocalChecked());
|
Nan::NewBuffer(baton->xmp, baton->xmpLength, sharp::FreeCallback, nullptr).ToLocalChecked());
|
||||||
}
|
}
|
||||||
|
if (baton->tifftagPhotoshopLength > 0) {
|
||||||
|
Set(info,
|
||||||
|
New("tifftagPhotoshop").ToLocalChecked(),
|
||||||
|
Nan::NewBuffer(baton->tifftagPhotoshop, baton->tifftagPhotoshopLength, sharp::FreeCallback, nullptr)
|
||||||
|
.ToLocalChecked());
|
||||||
|
}
|
||||||
argv[1] = info;
|
argv[1] = info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ struct MetadataBaton {
|
|||||||
size_t iptcLength;
|
size_t iptcLength;
|
||||||
char *xmp;
|
char *xmp;
|
||||||
size_t xmpLength;
|
size_t xmpLength;
|
||||||
|
char *tifftagPhotoshop;
|
||||||
|
size_t tifftagPhotoshopLength;
|
||||||
std::string err;
|
std::string err;
|
||||||
|
|
||||||
MetadataBaton():
|
MetadataBaton():
|
||||||
@@ -71,7 +73,9 @@ struct MetadataBaton {
|
|||||||
iptc(nullptr),
|
iptc(nullptr),
|
||||||
iptcLength(0),
|
iptcLength(0),
|
||||||
xmp(nullptr),
|
xmp(nullptr),
|
||||||
xmpLength(0) {}
|
xmpLength(0),
|
||||||
|
tifftagPhotoshop(nullptr),
|
||||||
|
tifftagPhotoshopLength(0) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
NAN_METHOD(metadata);
|
NAN_METHOD(metadata);
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
if (baton->rotateBeforePreExtract) {
|
if (baton->rotateBeforePreExtract) {
|
||||||
if (rotation != VIPS_ANGLE_D0) {
|
if (rotation != VIPS_ANGLE_D0) {
|
||||||
image = image.rot(rotation);
|
image = image.rot(rotation);
|
||||||
sharp::RemoveExifOrientation(image);
|
image = sharp::RemoveExifOrientation(image);
|
||||||
}
|
}
|
||||||
if (baton->rotationAngle != 0.0) {
|
if (baton->rotationAngle != 0.0) {
|
||||||
std::vector<double> background;
|
std::vector<double> background;
|
||||||
@@ -404,20 +404,20 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
// Rotate post-extract 90-angle
|
// Rotate post-extract 90-angle
|
||||||
if (!baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) {
|
if (!baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) {
|
||||||
image = image.rot(rotation);
|
image = image.rot(rotation);
|
||||||
sharp::RemoveExifOrientation(image);
|
image = sharp::RemoveExifOrientation(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Flip (mirror about Y axis)
|
// Flip (mirror about Y axis)
|
||||||
if (baton->flip) {
|
if (baton->flip) {
|
||||||
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
||||||
sharp::RemoveExifOrientation(image);
|
image = sharp::RemoveExifOrientation(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flop (mirror about X axis)
|
// Flop (mirror about X axis)
|
||||||
if (baton->flop) {
|
if (baton->flop) {
|
||||||
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
||||||
sharp::RemoveExifOrientation(image);
|
image = sharp::RemoveExifOrientation(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join additional color channels to the image
|
// Join additional color channels to the image
|
||||||
@@ -700,7 +700,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
|
|
||||||
// Override EXIF Orientation tag
|
// Override EXIF Orientation tag
|
||||||
if (baton->withMetadata && baton->withMetadataOrientation != -1) {
|
if (baton->withMetadata && baton->withMetadataOrientation != -1) {
|
||||||
sharp::SetExifOrientation(image, baton->withMetadataOrientation);
|
image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number of channels used in output image
|
// Number of channels used in output image
|
||||||
|
|||||||
1
test/fixtures/index.js
vendored
1
test/fixtures/index.js
vendored
@@ -97,6 +97,7 @@ module.exports = {
|
|||||||
inputTiffCielab: getPath('cielab-dagams.tiff'), // https://github.com/lovell/sharp/issues/646
|
inputTiffCielab: getPath('cielab-dagams.tiff'), // https://github.com/lovell/sharp/issues/646
|
||||||
inputTiffUncompressed: getPath('uncompressed_tiff.tiff'), // https://code.google.com/archive/p/imagetestsuite/wikis/TIFFTestSuite.wiki file: 0c84d07e1b22b76f24cccc70d8788e4a.tif
|
inputTiffUncompressed: getPath('uncompressed_tiff.tiff'), // https://code.google.com/archive/p/imagetestsuite/wikis/TIFFTestSuite.wiki file: 0c84d07e1b22b76f24cccc70d8788e4a.tif
|
||||||
inputTiff8BitDepth: getPath('8bit_depth.tiff'),
|
inputTiff8BitDepth: getPath('8bit_depth.tiff'),
|
||||||
|
inputTifftagPhotoshop: getPath('tifftag-photoshop.tiff'), // https://github.com/lovell/sharp/issues/1600
|
||||||
inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif
|
inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif
|
||||||
inputGifGreyPlusAlpha: getPath('grey-plus-alpha.gif'), // http://i.imgur.com/gZ5jlmE.gif
|
inputGifGreyPlusAlpha: getPath('grey-plus-alpha.gif'), // http://i.imgur.com/gZ5jlmE.gif
|
||||||
inputGifAnimated: getPath('rotating-squares.gif'), // CC0 https://loading.io/spinner/blocks/-rotating-squares-preloader-gif
|
inputGifAnimated: getPath('rotating-squares.gif'), // CC0 https://loading.io/spinner/blocks/-rotating-squares-preloader-gif
|
||||||
|
|||||||
BIN
test/fixtures/tifftag-photoshop.tiff
vendored
Normal file
BIN
test/fixtures/tifftag-photoshop.tiff
vendored
Normal file
Binary file not shown.
@@ -515,6 +515,21 @@ describe('Image metadata', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('16-bit TIFF with TIFFTAG_PHOTOSHOP metadata', () =>
|
||||||
|
sharp(fixtures.inputTifftagPhotoshop)
|
||||||
|
.metadata()
|
||||||
|
.then(metadata => {
|
||||||
|
assert.strictEqual(metadata.format, 'tiff');
|
||||||
|
assert.strictEqual(metadata.width, 317);
|
||||||
|
assert.strictEqual(metadata.height, 211);
|
||||||
|
assert.strictEqual(metadata.space, 'rgb16');
|
||||||
|
assert.strictEqual(metadata.channels, 3);
|
||||||
|
assert.strictEqual(typeof metadata.tifftagPhotoshop, 'object');
|
||||||
|
assert.strictEqual(metadata.tifftagPhotoshop instanceof Buffer, true);
|
||||||
|
assert.strictEqual(metadata.tifftagPhotoshop.length, 6634);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
it('File input with corrupt header fails gracefully', function (done) {
|
it('File input with corrupt header fails gracefully', function (done) {
|
||||||
sharp(fixtures.inputJpgWithCorruptHeader)
|
sharp(fixtures.inputJpgWithCorruptHeader)
|
||||||
.metadata(function (err) {
|
.metadata(function (err) {
|
||||||
|
|||||||
Reference in New Issue
Block a user