diff --git a/icc/USWebCoatedSWOP.icc b/icc/USWebCoatedSWOP.icc new file mode 100644 index 000000000..078a6443a Binary files /dev/null and b/icc/USWebCoatedSWOP.icc differ diff --git a/index.js b/index.js index 7a817d198..3104acac1 100755 --- a/index.js +++ b/index.js @@ -1,5 +1,6 @@ 'use strict'; +var path = require('path'); var util = require('util'); var stream = require('stream'); @@ -17,6 +18,8 @@ var Sharp = function(input) { // input options streamIn: false, sequentialRead: false, + // ICC profile to use when input CMYK image has no embedded profile + iccProfileCmyk: path.join(__dirname, 'icc', 'USWebCoatedSWOP.icc'), // resize options topOffsetPre: -1, leftOffsetPre: -1, diff --git a/package.json b/package.json index c859a3352..c5e9b3f49 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sharp", - "version": "0.7.1", + "version": "0.7.2", "author": "Lovell Fuller ", "contributors": [ "Pierre Inglebert ", @@ -41,9 +41,9 @@ "stream" ], "dependencies": { - "bluebird": "^2.3.9", + "bluebird": "^2.3.10", "color": "^0.7.1", - "nan": "^1.3.0" + "nan": "^1.4.0" }, "devDependencies": { "mocha": "^2.0.1", diff --git a/src/resize.cc b/src/resize.cc index eb111d4d1..7b35e8e06 100755 --- a/src/resize.cc +++ b/src/resize.cc @@ -30,6 +30,7 @@ struct ResizeBaton { std::string fileIn; void* bufferIn; size_t bufferInLength; + std::string iccProfileCmyk; std::string output; std::string outputFormat; void* bufferOut; @@ -236,16 +237,28 @@ class ResizeWorker : public NanAsyncWorker { } // Handle colour profile, if any, for non sRGB images - if (image->Type != VIPS_INTERPRETATION_sRGB && vips_image_get_typeof(image, VIPS_META_ICC_NAME)) { - // Import embedded profile - VipsImage *profile = vips_image_new(); - vips_object_local(hook, profile); - if (vips_icc_import(image, &profile, NULL, "embedded", TRUE, "pcs", VIPS_PCS_XYZ, NULL)) { - return Error(baton, hook); + if (image->Type != VIPS_INTERPRETATION_sRGB) { + // Get the input colour profile + if (vips_image_get_typeof(image, VIPS_META_ICC_NAME)) { + // Use embedded profile + VipsImage *profile = vips_image_new(); + vips_object_local(hook, profile); + if (vips_icc_import(image, &profile, "pcs", VIPS_PCS_XYZ, "embedded", TRUE, NULL)) { + return Error(baton, hook); + } + g_object_unref(image); + image = profile; + } else if (image->Type == VIPS_INTERPRETATION_CMYK) { + // CMYK with no embedded profile + VipsImage *profile = vips_image_new(); + vips_object_local(hook, profile); + if (vips_icc_import(image, &profile, "pcs", VIPS_PCS_XYZ, "input_profile", (baton->iccProfileCmyk).c_str(), NULL)) { + return Error(baton, hook); + } + g_object_unref(image); + image = profile; } - g_object_unref(image); - image = profile; - // Convert to sRGB colour space + // Attempt to convert to sRGB colour space VipsImage *colourspaced = vips_image_new(); vips_object_local(hook, colourspaced); if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) { @@ -738,6 +751,8 @@ NAN_METHOD(resize) { baton->bufferInLength = node::Buffer::Length(buffer); baton->bufferIn = node::Buffer::Data(buffer); } + // ICC profile to use when input CMYK image has no embedded profile + baton->iccProfileCmyk = *String::Utf8Value(options->Get(NanNew("iccProfileCmyk"))->ToString()); // Extract image options baton->topOffsetPre = options->Get(NanNew("topOffsetPre"))->Int32Value(); baton->leftOffsetPre = options->Get(NanNew("leftOffsetPre"))->Int32Value(); diff --git a/test/fixtures/Channel_digital_image_CMYK_color_no_profile.jpg b/test/fixtures/Channel_digital_image_CMYK_color_no_profile.jpg new file mode 100644 index 000000000..4115877fe Binary files /dev/null and b/test/fixtures/Channel_digital_image_CMYK_color_no_profile.jpg differ diff --git a/test/fixtures/index.js b/test/fixtures/index.js index 5fb1d13a7..35d6a76a1 100755 --- a/test/fixtures/index.js +++ b/test/fixtures/index.js @@ -13,6 +13,7 @@ module.exports = { inputJpgWithExifMirroring: getPath('Landscape_5.jpg'), // https://github.com/recurser/exif-orientation-examples/blob/master/Landscape_5.jpg inputJpgWithGammaHoliness: getPath('gamma_dalai_lama_gray.jpg'), // http://www.4p8.com/eric.brasseur/gamma.html inputJpgWithCmykProfile: getPath('Channel_digital_image_CMYK_color.jpg'), // http://en.wikipedia.org/wiki/File:Channel_digital_image_CMYK_color.jpg + inputJpgWithCmykNoProfile: getPath('Channel_digital_image_CMYK_color_no_profile.jpg'), inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png inputPngWithTransparency: getPath('blackbug.png'), // public domain diff --git a/test/unit/colourspace.js b/test/unit/colourspace.js index be3b67ffa..3f6e4ee7f 100755 --- a/test/unit/colourspace.js +++ b/test/unit/colourspace.js @@ -66,4 +66,16 @@ describe('Colour space conversion', function() { }); }); + it('From profile-less CMYK to sRGB', function(done) { + sharp(fixtures.inputJpgWithCmykNoProfile) + .resize(320) + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(true, data.length > 0); + assert.strictEqual('jpeg', info.format); + assert.strictEqual(320, info.width); + done(); + }); + }); + });