Skip to content

Commit

Permalink
Add support to withMetadata for custom ICC profile #2271
Browse files Browse the repository at this point in the history
  • Loading branch information
roborourke authored and lovell committed Aug 19, 2020
1 parent 05ca7d3 commit eaecb73
Show file tree
Hide file tree
Showing 12 changed files with 75 additions and 3 deletions.
4 changes: 3 additions & 1 deletion docs/api-output.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ Returns **[Promise][5]<[Buffer][8]>** when no callback is provided
## withMetadata

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
sRGB colour space and strip all metadata, including the removal of any ICC profile.
Expand All @@ -100,6 +101,7 @@ sRGB colour space and strip all metadata, including the removal of any ICC profi

- `options` **[Object][6]?**
- `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

Expand Down
4 changes: 4 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ Requires libvips v8.10.0
[#2259](https://github.com/lovell/sharp/pull/2259)
[@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.
[#2292](https://github.com/lovell/sharp/pull/2292)
[@diegodev3](https://github.com/diegodev3)
Expand Down
3 changes: 3 additions & 0 deletions docs/humans.txt
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,6 @@ GitHub: https://github.com/vouillon

Name: Tomáš Szabo
GitHub: https://github.com/deftomat

Name: Robert O'Rourke
GitHub: https://github.com/roborourke
1 change: 1 addition & 0 deletions lib/constructor.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ const Sharp = function (input, options) {
streamOut: false,
withMetadata: false,
withMetadataOrientation: -1,
withMetadataIcc: '',
resolveWithObject: false,
// output format
jpegQuality: 80,
Expand Down
11 changes: 10 additions & 1 deletion lib/output.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ function toBuffer (options, callback) {

/**
* 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
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
Expand All @@ -132,6 +133,7 @@ function toBuffer (options, callback) {
*
* @param {Object} [options]
* @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}
* @throws {Error} Invalid parameters
*/
Expand All @@ -145,6 +147,13 @@ function withMetadata (options) {
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;
}
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@
"Brychan Bennett-Odlum <git@brychan.io>",
"Edward Silverton <e.silverton@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": {
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
Expand Down
10 changes: 10 additions & 0 deletions src/pipeline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
if (baton->withMetadata && baton->withMetadataOrientation != -1) {
image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
Expand Down Expand Up @@ -1319,6 +1328,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
// Format-specific
baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
Expand Down
1 change: 1 addition & 0 deletions src/pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ struct PipelineBaton {
std::string err;
bool withMetadata;
int withMetadataOrientation;
std::string withMetadataIcc;
std::unique_ptr<double[]> convKernel;
int convKernelWidth;
int convKernelHeight;
Expand Down
Binary file added test/fixtures/expected/hilutite.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/expected/icc-cmyk.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/hilutite.icm
Binary file not shown.
41 changes: 41 additions & 0 deletions test/unit/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () =>
sharp(fixtures.inputJpgWithExif)
.withMetadata({})
Expand Down Expand Up @@ -675,5 +711,10 @@ describe('Image metadata', function () {
sharp().withMetadata({ orientation: 9 });
});
});
it('Non string icc', function () {
assert.throws(function () {
sharp().withMetadata({ icc: true });
});
});
});
});

0 comments on commit eaecb73

Please sign in to comment.