Skip to content

Commit

Permalink
Expose PNG metadata comments (#4157)
Browse files Browse the repository at this point in the history
  • Loading branch information
nkeynes authored Jul 18, 2024
1 parent c2a0241 commit d642108
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/api-input.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ A `Promise` is returned when `callback` is not provided.
- `xmp`: Buffer containing raw XMP data, if present
- `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
- `formatMagick`: String containing format for images loaded via *magick
- `comments`: Array of keyword/text pairs representing PNG text blocks, if present.



Expand Down
7 changes: 7 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1108,13 +1108,20 @@ declare namespace sharp {
resolutionUnit?: 'inch' | 'cm' | undefined;
/** String containing format for images loaded via *magick */
formatMagick?: string | undefined;
/** Array of keyword/text pairs representing PNG text blocks, if present. */
comments?: CommentsMetadata[] | undefined;
}

interface LevelMetadata {
width: number;
height: number;
}

interface CommentsMetadata {
keyword: string;
text: string;
}

interface Stats {
/** Array of channel statistics for each channel in the image. */
channels: ChannelStats[];
Expand Down
1 change: 1 addition & 0 deletions lib/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ function _isStreamInput () {
* - `xmp`: Buffer containing raw XMP data, if present
* - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
* - `formatMagick`: String containing format for images loaded via *magick
* - `comments` Array of keyword/text pairs representing PNG text blocks, if present.
*
* @example
* const metadata = await sharp(input).metadata();
Expand Down
33 changes: 33 additions & 0 deletions src/metadata.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "common.h"
#include "metadata.h"

static void* readPNGComment(VipsImage *image, const char *field, GValue *value, void *p);

class MetadataWorker : public Napi::AsyncWorker {
public:
MetadataWorker(Napi::Function callback, MetadataBaton *baton, Napi::Function debuglog) :
Expand Down Expand Up @@ -131,6 +133,8 @@ class MetadataWorker : public Napi::AsyncWorker {
memcpy(baton->tifftagPhotoshop, tifftagPhotoshop, tifftagPhotoshopLength);
baton->tifftagPhotoshopLength = tifftagPhotoshopLength;
}
// PNG comments
vips_image_map(image.get_image(), readPNGComment, &baton->comments);
}

// Clean up
Expand Down Expand Up @@ -246,6 +250,17 @@ class MetadataWorker : public Napi::AsyncWorker {
Napi::Buffer<char>::NewOrCopy(env, baton->tifftagPhotoshop,
baton->tifftagPhotoshopLength, sharp::FreeCallback));
}
if (baton->comments.size() > 0) {
int i = 0;
Napi::Array comments = Napi::Array::New(env, baton->comments.size());
for (auto &c : baton->comments) {
Napi::Object comment = Napi::Object::New(env);
comment.Set("keyword", c.first);
comment.Set("text", c.second);
comments.Set(i++, comment);
}
info.Set("comments", comments);
}
Callback().Call(Receiver().Value(), { env.Null(), info });
} else {
Callback().Call(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() });
Expand Down Expand Up @@ -285,3 +300,21 @@ Napi::Value metadata(const Napi::CallbackInfo& info) {

return info.Env().Undefined();
}

const char *PNG_COMMENT_START = "png-comment-";
const int PNG_COMMENT_START_LEN = strlen(PNG_COMMENT_START);

static void* readPNGComment(VipsImage *image, const char *field, GValue *value, void *p) {
MetadataComments *comments = static_cast<MetadataComments *>(p);

if (vips_isprefix(PNG_COMMENT_START, field)) {
const char *keyword = strchr(field + PNG_COMMENT_START_LEN, '-');
const char *str;
if (keyword != NULL && !vips_image_get_string(image, field, &str)) {
keyword++; // Skip the hyphen
comments->push_back(std::make_pair(keyword, str));
}
}

return NULL;
}
3 changes: 3 additions & 0 deletions src/metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include "./common.h"

typedef std::vector<std::pair<std::string, std::string>> MetadataComments;

struct MetadataBaton {
// Input
sharp::InputDescriptor *input;
Expand Down Expand Up @@ -47,6 +49,7 @@ struct MetadataBaton {
size_t xmpLength;
char *tifftagPhotoshop;
size_t tifftagPhotoshopLength;
MetadataComments comments;
std::string err;

MetadataBaton():
Expand Down
25 changes: 25 additions & 0 deletions test/unit/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,31 @@ describe('Image metadata', function () {
});
});

it('PNG with comment', function (done) {
sharp(fixtures.inputPngTestJoinChannel).metadata(function (err, metadata) {
if (err) throw err;
assert.strictEqual('png', metadata.format);
assert.strictEqual('undefined', typeof metadata.size);
assert.strictEqual(320, metadata.width);
assert.strictEqual(240, metadata.height);
assert.strictEqual('b-w', metadata.space);
assert.strictEqual(1, metadata.channels);
assert.strictEqual('uchar', metadata.depth);
assert.strictEqual(72, metadata.density);
assert.strictEqual('undefined', typeof metadata.chromaSubsampling);
assert.strictEqual(false, metadata.isProgressive);
assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha);
assert.strictEqual('undefined', typeof metadata.orientation);
assert.strictEqual('undefined', typeof metadata.exif);
assert.strictEqual('undefined', typeof metadata.icc);
assert.strictEqual(1, metadata.comments.length);
assert.strictEqual('Comment', metadata.comments[0].keyword);
assert.strictEqual('Created with GIMP', metadata.comments[0].text);
done();
});
});

it('Transparent PNG', function (done) {
sharp(fixtures.inputPngWithTransparency).metadata(function (err, metadata) {
if (err) throw err;
Expand Down

0 comments on commit d642108

Please sign in to comment.