diff --git a/docs/api-input.md b/docs/api-input.md index b8124f65..734744ea 100644 --- a/docs/api-input.md +++ b/docs/api-input.md @@ -44,6 +44,7 @@ A `Promise` is returned when `callback` is not provided. - `icc`: Buffer containing raw [ICC][3] profile data, if present - `iptc`: Buffer containing raw IPTC data, if present - `xmp`: Buffer containing raw XMP data, if present +- `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present ### Parameters diff --git a/docs/changelog.md b/docs/changelog.md index 2ad8aa1c..26ca5623 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -8,6 +8,9 @@ Requires libvips v8.8.1. * 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) diff --git a/lib/input.js b/lib/input.js index 943c01d5..caf4fe73 100644 --- a/lib/input.js +++ b/lib/input.js @@ -207,6 +207,7 @@ function clone () { * - `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present * - `iptc`: Buffer containing raw IPTC data, if present * - `xmp`: Buffer containing raw XMP data, if present + * - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present * * @example * const image = sharp(inputJpg); diff --git a/src/metadata.cc b/src/metadata.cc index c3e9ab08..bba44ed0 100644 --- a/src/metadata.cc +++ b/src/metadata.cc @@ -116,6 +116,14 @@ class MetadataWorker : public Nan::AsyncWorker { memcpy(baton->xmp, xmp, 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(g_malloc(tifftagPhotoshopLength)); + memcpy(baton->tifftagPhotoshop, tifftagPhotoshop, tifftagPhotoshopLength); + baton->tifftagPhotoshopLength = tifftagPhotoshopLength; + } } // Clean up @@ -189,6 +197,12 @@ class MetadataWorker : public Nan::AsyncWorker { New("xmp").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; } diff --git a/src/metadata.h b/src/metadata.h index d025a65c..e5a4df4b 100644 --- a/src/metadata.h +++ b/src/metadata.h @@ -48,6 +48,8 @@ struct MetadataBaton { size_t iptcLength; char *xmp; size_t xmpLength; + char *tifftagPhotoshop; + size_t tifftagPhotoshopLength; std::string err; MetadataBaton(): @@ -71,7 +73,9 @@ struct MetadataBaton { iptc(nullptr), iptcLength(0), xmp(nullptr), - xmpLength(0) {} + xmpLength(0), + tifftagPhotoshop(nullptr), + tifftagPhotoshopLength(0) {} }; NAN_METHOD(metadata); diff --git a/test/fixtures/index.js b/test/fixtures/index.js index d8a5fa3e..85c6f0a8 100644 --- a/test/fixtures/index.js +++ b/test/fixtures/index.js @@ -97,6 +97,7 @@ module.exports = { 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 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 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 diff --git a/test/fixtures/tifftag-photoshop.tiff b/test/fixtures/tifftag-photoshop.tiff new file mode 100644 index 00000000..939954cf Binary files /dev/null and b/test/fixtures/tifftag-photoshop.tiff differ diff --git a/test/unit/metadata.js b/test/unit/metadata.js index a67dd3e1..628cbe10 100644 --- a/test/unit/metadata.js +++ b/test/unit/metadata.js @@ -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) { sharp(fixtures.inputJpgWithCorruptHeader) .metadata(function (err) {