mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Add support to withMetadata for custom ICC profile #2271
This commit is contained in:
parent
05ca7d3129
commit
eaecb7347b
@ -91,7 +91,8 @@ Returns **[Promise][5]<[Buffer][8]>** when no callback is provided
|
|||||||
## withMetadata
|
## withMetadata
|
||||||
|
|
||||||
Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
||||||
This will also convert to and add a web-friendly sRGB ICC profile.
|
This will also convert to and add a web-friendly sRGB ICC profile unless a custom
|
||||||
|
output profile is provided.
|
||||||
|
|
||||||
The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
||||||
sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
||||||
@ -100,6 +101,7 @@ sRGB colour space and strip all metadata, including the removal of any ICC profi
|
|||||||
|
|
||||||
- `options` **[Object][6]?**
|
- `options` **[Object][6]?**
|
||||||
- `options.orientation` **[number][9]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
|
- `options.orientation` **[number][9]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
|
||||||
|
- `options.icc` **[string][2]?** filesystem path to output ICC profile, defaults to sRGB.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
|
@ -27,6 +27,10 @@ Requires libvips v8.10.0
|
|||||||
[#2259](https://github.com/lovell/sharp/pull/2259)
|
[#2259](https://github.com/lovell/sharp/pull/2259)
|
||||||
[@vouillon](https://github.com/vouillon)
|
[@vouillon](https://github.com/vouillon)
|
||||||
|
|
||||||
|
* Add support to `withMetadata` for custom ICC profile.
|
||||||
|
[#2271](https://github.com/lovell/sharp/pull/2271)
|
||||||
|
[@roborourke](https://github.com/roborourke)
|
||||||
|
|
||||||
* Ensure prebuilt binaries for ARM default to v7 when using Electron.
|
* Ensure prebuilt binaries for ARM default to v7 when using Electron.
|
||||||
[#2292](https://github.com/lovell/sharp/pull/2292)
|
[#2292](https://github.com/lovell/sharp/pull/2292)
|
||||||
[@diegodev3](https://github.com/diegodev3)
|
[@diegodev3](https://github.com/diegodev3)
|
||||||
|
@ -194,3 +194,6 @@ GitHub: https://github.com/vouillon
|
|||||||
|
|
||||||
Name: Tomáš Szabo
|
Name: Tomáš Szabo
|
||||||
GitHub: https://github.com/deftomat
|
GitHub: https://github.com/deftomat
|
||||||
|
|
||||||
|
Name: Robert O'Rourke
|
||||||
|
GitHub: https://github.com/roborourke
|
||||||
|
@ -192,6 +192,7 @@ const Sharp = function (input, options) {
|
|||||||
streamOut: false,
|
streamOut: false,
|
||||||
withMetadata: false,
|
withMetadata: false,
|
||||||
withMetadataOrientation: -1,
|
withMetadataOrientation: -1,
|
||||||
|
withMetadataIcc: '',
|
||||||
resolveWithObject: false,
|
resolveWithObject: false,
|
||||||
// output format
|
// output format
|
||||||
jpegQuality: 80,
|
jpegQuality: 80,
|
||||||
|
@ -119,7 +119,8 @@ function toBuffer (options, callback) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
* Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
||||||
* This will also convert to and add a web-friendly sRGB ICC profile.
|
* This will also convert to and add a web-friendly sRGB ICC profile unless a custom
|
||||||
|
* output profile is provided.
|
||||||
*
|
*
|
||||||
* The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
* The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
||||||
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
||||||
@ -132,6 +133,7 @@ function toBuffer (options, callback) {
|
|||||||
*
|
*
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
* @param {number} [options.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
|
* @param {number} [options.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
|
||||||
|
* @param {string} [options.icc] filesystem path to output ICC profile, defaults to sRGB.
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
*/
|
*/
|
||||||
@ -145,6 +147,13 @@ function withMetadata (options) {
|
|||||||
throw is.invalidParameterError('orientation', 'integer between 1 and 8', options.orientation);
|
throw is.invalidParameterError('orientation', 'integer between 1 and 8', options.orientation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (is.defined(options.icc)) {
|
||||||
|
if (is.string(options.icc)) {
|
||||||
|
this.options.withMetadataIcc = options.icc;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('icc', 'string filesystem path to ICC profile', options.icc);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,8 @@
|
|||||||
"Brychan Bennett-Odlum <git@brychan.io>",
|
"Brychan Bennett-Odlum <git@brychan.io>",
|
||||||
"Edward Silverton <e.silverton@gmail.com>",
|
"Edward Silverton <e.silverton@gmail.com>",
|
||||||
"Roman Malieiev <aromaleev@gmail.com>",
|
"Roman Malieiev <aromaleev@gmail.com>",
|
||||||
"Tomas Szabo <tomas.szabo@deftomat.com>"
|
"Tomas Szabo <tomas.szabo@deftomat.com>",
|
||||||
|
"Robert O'Rourke <robert@o-rourke.org>"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
||||||
|
@ -684,6 +684,15 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply output ICC profile
|
||||||
|
if (!baton->withMetadataIcc.empty()) {
|
||||||
|
image = image.icc_transform(
|
||||||
|
const_cast<char*>(baton->withMetadataIcc.data()),
|
||||||
|
VImage::option()
|
||||||
|
->set("input_profile", "srgb")
|
||||||
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
||||||
|
}
|
||||||
|
|
||||||
// Override EXIF Orientation tag
|
// Override EXIF Orientation tag
|
||||||
if (baton->withMetadata && baton->withMetadataOrientation != -1) {
|
if (baton->withMetadata && baton->withMetadataOrientation != -1) {
|
||||||
image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
|
image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
|
||||||
@ -1319,6 +1328,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
|
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
|
||||||
baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
|
baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
|
||||||
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
|
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
|
||||||
|
baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
|
||||||
// Format-specific
|
// Format-specific
|
||||||
baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
|
baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
|
||||||
baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
|
baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
|
||||||
|
@ -155,6 +155,7 @@ struct PipelineBaton {
|
|||||||
std::string err;
|
std::string err;
|
||||||
bool withMetadata;
|
bool withMetadata;
|
||||||
int withMetadataOrientation;
|
int withMetadataOrientation;
|
||||||
|
std::string withMetadataIcc;
|
||||||
std::unique_ptr<double[]> convKernel;
|
std::unique_ptr<double[]> convKernel;
|
||||||
int convKernelWidth;
|
int convKernelWidth;
|
||||||
int convKernelHeight;
|
int convKernelHeight;
|
||||||
|
BIN
test/fixtures/expected/hilutite.jpg
vendored
Normal file
BIN
test/fixtures/expected/hilutite.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 MiB |
BIN
test/fixtures/expected/icc-cmyk.jpg
vendored
Normal file
BIN
test/fixtures/expected/icc-cmyk.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 MiB |
BIN
test/fixtures/hilutite.icm
vendored
Normal file
BIN
test/fixtures/hilutite.icm
vendored
Normal file
Binary file not shown.
@ -501,6 +501,42 @@ describe('Image metadata', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Apply CMYK output ICC profile', function (done) {
|
||||||
|
const output = fixtures.path('output.icc-cmyk.jpg');
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.withMetadata({ icc: 'cmyk' })
|
||||||
|
.toFile(output, function (err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
sharp(output).metadata(function (err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, metadata.hasProfile);
|
||||||
|
assert.strictEqual('cmyk', metadata.space);
|
||||||
|
assert.strictEqual(4, metadata.channels);
|
||||||
|
// ICC
|
||||||
|
assert.strictEqual('object', typeof metadata.icc);
|
||||||
|
assert.strictEqual(true, metadata.icc instanceof Buffer);
|
||||||
|
const profile = icc.parse(metadata.icc);
|
||||||
|
assert.strictEqual('object', typeof profile);
|
||||||
|
assert.strictEqual('CMYK', profile.colorSpace);
|
||||||
|
assert.strictEqual('Relative', profile.intent);
|
||||||
|
assert.strictEqual('Printer', profile.deviceClass);
|
||||||
|
});
|
||||||
|
fixtures.assertSimilar(output, fixtures.path('expected/icc-cmyk.jpg'), { threshold: 0 }, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Apply custom output ICC profile', function (done) {
|
||||||
|
const output = fixtures.path('output.hilutite.jpg');
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.withMetadata({ icc: fixtures.path('hilutite.icm') })
|
||||||
|
.toFile(output, function (err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
fixtures.assertMaxColourDistance(output, fixtures.path('expected/hilutite.jpg'), 0);
|
||||||
|
fixtures.assertMaxColourDistance(output, fixtures.inputJpg, 16.5);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Include metadata in output, enabled via empty object', () =>
|
it('Include metadata in output, enabled via empty object', () =>
|
||||||
sharp(fixtures.inputJpgWithExif)
|
sharp(fixtures.inputJpgWithExif)
|
||||||
.withMetadata({})
|
.withMetadata({})
|
||||||
@ -675,5 +711,10 @@ describe('Image metadata', function () {
|
|||||||
sharp().withMetadata({ orientation: 9 });
|
sharp().withMetadata({ orientation: 9 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('Non string icc', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().withMetadata({ icc: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user