@revizly/sharp 0.33.2-revizly3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/common.h ADDED
@@ -0,0 +1,393 @@
1
+ // Copyright 2013 Lovell Fuller and others.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ #ifndef SRC_COMMON_H_
5
+ #define SRC_COMMON_H_
6
+
7
+ #include <string>
8
+ #include <tuple>
9
+ #include <vector>
10
+ #include <atomic>
11
+
12
+ #include <napi.h>
13
+ #include <vips/vips8>
14
+
15
+ // Verify platform and compiler compatibility
16
+
17
+ #if (VIPS_MAJOR_VERSION < 8) || \
18
+ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 15) || \
19
+ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 15 && VIPS_MICRO_VERSION < 1)
20
+ #error "libvips version 8.15.1+ is required - please see https://sharp.pixelplumbing.com/install"
21
+ #endif
22
+
23
+ #if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
24
+ #error "GCC version 4.6+ is required for C++11 features - please see https://sharp.pixelplumbing.com/install"
25
+ #endif
26
+
27
+ #if (defined(__clang__) && defined(__has_feature))
28
+ #if (!__has_feature(cxx_range_for))
29
+ #error "clang version 3.0+ is required for C++11 features - please see https://sharp.pixelplumbing.com/install"
30
+ #endif
31
+ #endif
32
+
33
+ using vips::VImage;
34
+
35
+ namespace sharp {
36
+
37
+ struct InputDescriptor { // NOLINT(runtime/indentation_namespace)
38
+ std::string name;
39
+ std::string file;
40
+ char *buffer;
41
+ VipsFailOn failOn;
42
+ uint64_t limitInputPixels;
43
+ bool unlimited;
44
+ VipsAccess access;
45
+ size_t bufferLength;
46
+ bool isBuffer;
47
+ double density;
48
+ bool ignoreIcc;
49
+ VipsBandFormat rawDepth;
50
+ int rawChannels;
51
+ int rawWidth;
52
+ int rawHeight;
53
+ bool rawPremultiplied;
54
+ int pages;
55
+ int page;
56
+ int level;
57
+ int subifd;
58
+ int createChannels;
59
+ int createWidth;
60
+ int createHeight;
61
+ std::vector<double> createBackground;
62
+ std::string createNoiseType;
63
+ double createNoiseMean;
64
+ double createNoiseSigma;
65
+ std::string textValue;
66
+ std::string textFont;
67
+ std::string textFontfile;
68
+ int textWidth;
69
+ int textHeight;
70
+ VipsAlign textAlign;
71
+ bool textJustify;
72
+ int textDpi;
73
+ bool textRgba;
74
+ int textSpacing;
75
+ VipsTextWrap textWrap;
76
+ int textAutofitDpi;
77
+
78
+ InputDescriptor():
79
+ buffer(nullptr),
80
+ failOn(VIPS_FAIL_ON_WARNING),
81
+ limitInputPixels(0x3FFF * 0x3FFF),
82
+ unlimited(FALSE),
83
+ access(VIPS_ACCESS_RANDOM),
84
+ bufferLength(0),
85
+ isBuffer(FALSE),
86
+ density(72.0),
87
+ ignoreIcc(FALSE),
88
+ rawDepth(VIPS_FORMAT_UCHAR),
89
+ rawChannels(0),
90
+ rawWidth(0),
91
+ rawHeight(0),
92
+ rawPremultiplied(false),
93
+ pages(1),
94
+ page(0),
95
+ level(0),
96
+ subifd(-1),
97
+ createChannels(0),
98
+ createWidth(0),
99
+ createHeight(0),
100
+ createBackground{ 0.0, 0.0, 0.0, 255.0 },
101
+ createNoiseMean(0.0),
102
+ createNoiseSigma(0.0),
103
+ textWidth(0),
104
+ textHeight(0),
105
+ textAlign(VIPS_ALIGN_LOW),
106
+ textJustify(FALSE),
107
+ textDpi(72),
108
+ textRgba(FALSE),
109
+ textSpacing(0),
110
+ textWrap(VIPS_TEXT_WRAP_WORD),
111
+ textAutofitDpi(0) {}
112
+ };
113
+
114
+ // Convenience methods to access the attributes of a Napi::Object
115
+ bool HasAttr(Napi::Object obj, std::string attr);
116
+ std::string AttrAsStr(Napi::Object obj, std::string attr);
117
+ std::string AttrAsStr(Napi::Object obj, unsigned int const attr);
118
+ uint32_t AttrAsUint32(Napi::Object obj, std::string attr);
119
+ int32_t AttrAsInt32(Napi::Object obj, std::string attr);
120
+ int32_t AttrAsInt32(Napi::Object obj, unsigned int const attr);
121
+ double AttrAsDouble(Napi::Object obj, std::string attr);
122
+ double AttrAsDouble(Napi::Object obj, unsigned int const attr);
123
+ bool AttrAsBool(Napi::Object obj, std::string attr);
124
+ std::vector<double> AttrAsVectorOfDouble(Napi::Object obj, std::string attr);
125
+ std::vector<int32_t> AttrAsInt32Vector(Napi::Object obj, std::string attr);
126
+ template <class T> T AttrAsEnum(Napi::Object obj, std::string attr, GType type) {
127
+ return static_cast<T>(
128
+ vips_enum_from_nick(nullptr, type, AttrAsStr(obj, attr).data()));
129
+ }
130
+
131
+ // Create an InputDescriptor instance from a Napi::Object describing an input image
132
+ InputDescriptor* CreateInputDescriptor(Napi::Object input);
133
+
134
+ enum class ImageType {
135
+ JPEG,
136
+ PNG,
137
+ WEBP,
138
+ JP2,
139
+ TIFF,
140
+ GIF,
141
+ SVG,
142
+ HEIF,
143
+ PDF,
144
+ MAGICK,
145
+ OPENSLIDE,
146
+ PPM,
147
+ FITS,
148
+ EXR,
149
+ JXL,
150
+ VIPS,
151
+ RAW,
152
+ UNKNOWN,
153
+ MISSING
154
+ };
155
+
156
+ enum class Canvas {
157
+ CROP,
158
+ EMBED,
159
+ MAX,
160
+ MIN,
161
+ IGNORE_ASPECT
162
+ };
163
+
164
+ // How many tasks are in the queue?
165
+ extern std::atomic<int> counterQueue;
166
+
167
+ // How many tasks are being processed?
168
+ extern std::atomic<int> counterProcess;
169
+
170
+ // Filename extension checkers
171
+ bool IsJpeg(std::string const &str);
172
+ bool IsPng(std::string const &str);
173
+ bool IsWebp(std::string const &str);
174
+ bool IsJp2(std::string const &str);
175
+ bool IsGif(std::string const &str);
176
+ bool IsTiff(std::string const &str);
177
+ bool IsHeic(std::string const &str);
178
+ bool IsHeif(std::string const &str);
179
+ bool IsAvif(std::string const &str);
180
+ bool IsJxl(std::string const &str);
181
+ bool IsDz(std::string const &str);
182
+ bool IsDzZip(std::string const &str);
183
+ bool IsV(std::string const &str);
184
+
185
+ /*
186
+ Trim space from end of string.
187
+ */
188
+ std::string TrimEnd(std::string const &str);
189
+
190
+ /*
191
+ Provide a string identifier for the given image type.
192
+ */
193
+ std::string ImageTypeId(ImageType const imageType);
194
+
195
+ /*
196
+ Determine image format of a buffer.
197
+ */
198
+ ImageType DetermineImageType(void *buffer, size_t const length);
199
+
200
+ /*
201
+ Determine image format of a file.
202
+ */
203
+ ImageType DetermineImageType(char const *file);
204
+
205
+ /*
206
+ Does this image type support multiple pages?
207
+ */
208
+ bool ImageTypeSupportsPage(ImageType imageType);
209
+
210
+ /*
211
+ Does this image type support removal of safety limits?
212
+ */
213
+ bool ImageTypeSupportsUnlimited(ImageType imageType);
214
+
215
+ /*
216
+ Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
217
+ */
218
+ std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor);
219
+
220
+ /*
221
+ Does this image have an embedded profile?
222
+ */
223
+ bool HasProfile(VImage image);
224
+
225
+ /*
226
+ Get copy of embedded profile.
227
+ */
228
+ std::pair<char*, size_t> GetProfile(VImage image);
229
+
230
+ /*
231
+ Set embedded profile.
232
+ */
233
+ VImage SetProfile(VImage image, std::pair<char*, size_t> icc);
234
+
235
+ /*
236
+ Does this image have an alpha channel?
237
+ Uses colour space interpretation with number of channels to guess this.
238
+ */
239
+ bool HasAlpha(VImage image);
240
+
241
+ /*
242
+ Remove all EXIF-related image fields.
243
+ */
244
+ VImage RemoveExif(VImage image);
245
+
246
+ /*
247
+ Get EXIF Orientation of image, if any.
248
+ */
249
+ int ExifOrientation(VImage image);
250
+
251
+ /*
252
+ Set EXIF Orientation of image.
253
+ */
254
+ VImage SetExifOrientation(VImage image, int const orientation);
255
+
256
+ /*
257
+ Remove EXIF Orientation from image.
258
+ */
259
+ VImage RemoveExifOrientation(VImage image);
260
+
261
+ /*
262
+ Set animation properties if necessary.
263
+ */
264
+ VImage SetAnimationProperties(VImage image, int nPages, int pageHeight, std::vector<int> delay, int loop);
265
+
266
+ /*
267
+ Remove animation properties from image.
268
+ */
269
+ VImage RemoveAnimationProperties(VImage image);
270
+
271
+ /*
272
+ Remove GIF palette from image.
273
+ */
274
+ VImage RemoveGifPalette(VImage image);
275
+
276
+ /*
277
+ Does this image have a non-default density?
278
+ */
279
+ bool HasDensity(VImage image);
280
+
281
+ /*
282
+ Get pixels/mm resolution as pixels/inch density.
283
+ */
284
+ int GetDensity(VImage image);
285
+
286
+ /*
287
+ Set pixels/mm resolution based on a pixels/inch density.
288
+ */
289
+ VImage SetDensity(VImage image, const double density);
290
+
291
+ /*
292
+ Multi-page images can have a page height. Fetch it, and sanity check it.
293
+ If page-height is not set, it defaults to the image height
294
+ */
295
+ int GetPageHeight(VImage image);
296
+
297
+ /*
298
+ Check the proposed format supports the current dimensions.
299
+ */
300
+ void AssertImageTypeDimensions(VImage image, ImageType const imageType);
301
+
302
+ /*
303
+ Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
304
+ */
305
+ extern std::function<void(void*, char*)> FreeCallback;
306
+
307
+ /*
308
+ Called with warnings from the glib-registered "VIPS" domain
309
+ */
310
+ void VipsWarningCallback(char const* log_domain, GLogLevelFlags log_level, char const* message, void* ignore);
311
+
312
+ /*
313
+ Pop the oldest warning message from the queue
314
+ */
315
+ std::string VipsWarningPop();
316
+
317
+ /*
318
+ Attach an event listener for progress updates, used to detect timeout
319
+ */
320
+ void SetTimeout(VImage image, int const timeoutSeconds);
321
+
322
+ /*
323
+ Event listener for progress updates, used to detect timeout
324
+ */
325
+ void VipsProgressCallBack(VipsImage *image, VipsProgress *progress, int *timeoutSeconds);
326
+
327
+ /*
328
+ Calculate the (left, top) coordinates of the output image
329
+ within the input image, applying the given gravity during an embed.
330
+ */
331
+ std::tuple<int, int> CalculateEmbedPosition(int const inWidth, int const inHeight,
332
+ int const outWidth, int const outHeight, int const gravity);
333
+
334
+ /*
335
+ Calculate the (left, top) coordinates of the output image
336
+ within the input image, applying the given gravity.
337
+ */
338
+ std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,
339
+ int const outWidth, int const outHeight, int const gravity);
340
+
341
+ /*
342
+ Calculate the (left, top) coordinates of the output image
343
+ within the input image, applying the given x and y offsets of the output image.
344
+ */
345
+ std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,
346
+ int const outWidth, int const outHeight, int const x, int const y);
347
+
348
+ /*
349
+ Are pixel values in this image 16-bit integer?
350
+ */
351
+ bool Is16Bit(VipsInterpretation const interpretation);
352
+
353
+ /*
354
+ Return the image alpha maximum. Useful for combining alpha bands. scRGB
355
+ images are 0 - 1 for image data, but the alpha is 0 - 255.
356
+ */
357
+ double MaximumImageAlpha(VipsInterpretation const interpretation);
358
+
359
+ /*
360
+ Convert RGBA value to another colourspace
361
+ */
362
+ std::vector<double> GetRgbaAsColourspace(std::vector<double> const rgba,
363
+ VipsInterpretation const interpretation, bool premultiply);
364
+
365
+ /*
366
+ Apply the alpha channel to a given colour
367
+ */
368
+ std::tuple<VImage, std::vector<double>> ApplyAlpha(VImage image, std::vector<double> colour, bool premultiply);
369
+
370
+ /*
371
+ Removes alpha channel, if any.
372
+ */
373
+ VImage RemoveAlpha(VImage image);
374
+
375
+ /*
376
+ Ensures alpha channel, if missing.
377
+ */
378
+ VImage EnsureAlpha(VImage image, double const value);
379
+
380
+ /*
381
+ Calculate the horizontal and vertical shrink factors, taking the canvas mode into account.
382
+ */
383
+ std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
384
+ Canvas canvas, bool withoutEnlargement, bool withoutReduction);
385
+
386
+ /*
387
+ Ensure decoding remains sequential.
388
+ */
389
+ VImage StaySequential(VImage image, VipsAccess access, bool condition = TRUE);
390
+
391
+ } // namespace sharp
392
+
393
+ #endif // SRC_COMMON_H_
@@ -0,0 +1,287 @@
1
+ // Copyright 2013 Lovell Fuller and others.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ #include <numeric>
5
+ #include <vector>
6
+
7
+ #include <napi.h>
8
+ #include <vips/vips8>
9
+
10
+ #include "common.h"
11
+ #include "metadata.h"
12
+
13
+ class MetadataWorker : public Napi::AsyncWorker {
14
+ public:
15
+ MetadataWorker(Napi::Function callback, MetadataBaton *baton, Napi::Function debuglog) :
16
+ Napi::AsyncWorker(callback), baton(baton), debuglog(Napi::Persistent(debuglog)) {}
17
+ ~MetadataWorker() {}
18
+
19
+ void Execute() {
20
+ // Decrement queued task counter
21
+ sharp::counterQueue--;
22
+
23
+ vips::VImage image;
24
+ sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
25
+ try {
26
+ std::tie(image, imageType) = OpenInput(baton->input);
27
+ } catch (vips::VError const &err) {
28
+ (baton->err).append(err.what());
29
+ }
30
+ if (imageType != sharp::ImageType::UNKNOWN) {
31
+ // Image type
32
+ baton->format = sharp::ImageTypeId(imageType);
33
+ // VipsImage attributes
34
+ baton->width = image.width();
35
+ baton->height = image.height();
36
+ baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image.interpretation());
37
+ baton->channels = image.bands();
38
+ baton->depth = vips_enum_nick(VIPS_TYPE_BAND_FORMAT, image.format());
39
+ if (sharp::HasDensity(image)) {
40
+ baton->density = sharp::GetDensity(image);
41
+ }
42
+ if (image.get_typeof("jpeg-chroma-subsample") == VIPS_TYPE_REF_STRING) {
43
+ baton->chromaSubsampling = image.get_string("jpeg-chroma-subsample");
44
+ }
45
+ if (image.get_typeof("interlaced") == G_TYPE_INT) {
46
+ baton->isProgressive = image.get_int("interlaced") == 1;
47
+ }
48
+ if (image.get_typeof("palette-bit-depth") == G_TYPE_INT) {
49
+ baton->paletteBitDepth = image.get_int("palette-bit-depth");
50
+ }
51
+ if (image.get_typeof(VIPS_META_N_PAGES) == G_TYPE_INT) {
52
+ baton->pages = image.get_int(VIPS_META_N_PAGES);
53
+ }
54
+ if (image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT) {
55
+ baton->pageHeight = image.get_int(VIPS_META_PAGE_HEIGHT);
56
+ }
57
+ if (image.get_typeof("loop") == G_TYPE_INT) {
58
+ baton->loop = image.get_int("loop");
59
+ }
60
+ if (image.get_typeof("delay") == VIPS_TYPE_ARRAY_INT) {
61
+ baton->delay = image.get_array_int("delay");
62
+ }
63
+ if (image.get_typeof("heif-primary") == G_TYPE_INT) {
64
+ baton->pagePrimary = image.get_int("heif-primary");
65
+ }
66
+ if (image.get_typeof("heif-compression") == VIPS_TYPE_REF_STRING) {
67
+ baton->compression = image.get_string("heif-compression");
68
+ }
69
+ if (image.get_typeof(VIPS_META_RESOLUTION_UNIT) == VIPS_TYPE_REF_STRING) {
70
+ baton->resolutionUnit = image.get_string(VIPS_META_RESOLUTION_UNIT);
71
+ }
72
+ if (image.get_typeof("magick-format") == VIPS_TYPE_REF_STRING) {
73
+ baton->formatMagick = image.get_string("magick-format");
74
+ }
75
+ if (image.get_typeof("openslide.level-count") == VIPS_TYPE_REF_STRING) {
76
+ int const levels = std::stoi(image.get_string("openslide.level-count"));
77
+ for (int l = 0; l < levels; l++) {
78
+ std::string prefix = "openslide.level[" + std::to_string(l) + "].";
79
+ int const width = std::stoi(image.get_string((prefix + "width").data()));
80
+ int const height = std::stoi(image.get_string((prefix + "height").data()));
81
+ baton->levels.push_back(std::pair<int, int>(width, height));
82
+ }
83
+ }
84
+ if (image.get_typeof(VIPS_META_N_SUBIFDS) == G_TYPE_INT) {
85
+ baton->subifds = image.get_int(VIPS_META_N_SUBIFDS);
86
+ }
87
+ baton->hasProfile = sharp::HasProfile(image);
88
+ if (image.get_typeof("background") == VIPS_TYPE_ARRAY_DOUBLE) {
89
+ baton->background = image.get_array_double("background");
90
+ }
91
+ // Derived attributes
92
+ baton->hasAlpha = sharp::HasAlpha(image);
93
+ baton->orientation = sharp::ExifOrientation(image);
94
+ // EXIF
95
+ if (image.get_typeof(VIPS_META_EXIF_NAME) == VIPS_TYPE_BLOB) {
96
+ size_t exifLength;
97
+ void const *exif = image.get_blob(VIPS_META_EXIF_NAME, &exifLength);
98
+ baton->exif = static_cast<char*>(g_malloc(exifLength));
99
+ memcpy(baton->exif, exif, exifLength);
100
+ baton->exifLength = exifLength;
101
+ }
102
+ // ICC profile
103
+ if (image.get_typeof(VIPS_META_ICC_NAME) == VIPS_TYPE_BLOB) {
104
+ size_t iccLength;
105
+ void const *icc = image.get_blob(VIPS_META_ICC_NAME, &iccLength);
106
+ baton->icc = static_cast<char*>(g_malloc(iccLength));
107
+ memcpy(baton->icc, icc, iccLength);
108
+ baton->iccLength = iccLength;
109
+ }
110
+ // IPTC
111
+ if (image.get_typeof(VIPS_META_IPTC_NAME) == VIPS_TYPE_BLOB) {
112
+ size_t iptcLength;
113
+ void const *iptc = image.get_blob(VIPS_META_IPTC_NAME, &iptcLength);
114
+ baton->iptc = static_cast<char *>(g_malloc(iptcLength));
115
+ memcpy(baton->iptc, iptc, iptcLength);
116
+ baton->iptcLength = iptcLength;
117
+ }
118
+ // XMP
119
+ if (image.get_typeof(VIPS_META_XMP_NAME) == VIPS_TYPE_BLOB) {
120
+ size_t xmpLength;
121
+ void const *xmp = image.get_blob(VIPS_META_XMP_NAME, &xmpLength);
122
+ baton->xmp = static_cast<char *>(g_malloc(xmpLength));
123
+ memcpy(baton->xmp, xmp, xmpLength);
124
+ baton->xmpLength = xmpLength;
125
+ }
126
+ // TIFFTAG_PHOTOSHOP
127
+ if (image.get_typeof(VIPS_META_PHOTOSHOP_NAME) == VIPS_TYPE_BLOB) {
128
+ size_t tifftagPhotoshopLength;
129
+ void const *tifftagPhotoshop = image.get_blob(VIPS_META_PHOTOSHOP_NAME, &tifftagPhotoshopLength);
130
+ baton->tifftagPhotoshop = static_cast<char *>(g_malloc(tifftagPhotoshopLength));
131
+ memcpy(baton->tifftagPhotoshop, tifftagPhotoshop, tifftagPhotoshopLength);
132
+ baton->tifftagPhotoshopLength = tifftagPhotoshopLength;
133
+ }
134
+ }
135
+
136
+ // Clean up
137
+ vips_error_clear();
138
+ vips_thread_shutdown();
139
+ }
140
+
141
+ void OnOK() {
142
+ Napi::Env env = Env();
143
+ Napi::HandleScope scope(env);
144
+
145
+ // Handle warnings
146
+ std::string warning = sharp::VipsWarningPop();
147
+ while (!warning.empty()) {
148
+ debuglog.Call(Receiver().Value(), { Napi::String::New(env, warning) });
149
+ warning = sharp::VipsWarningPop();
150
+ }
151
+
152
+ if (baton->err.empty()) {
153
+ Napi::Object info = Napi::Object::New(env);
154
+ info.Set("format", baton->format);
155
+ if (baton->input->bufferLength > 0) {
156
+ info.Set("size", baton->input->bufferLength);
157
+ }
158
+ info.Set("width", baton->width);
159
+ info.Set("height", baton->height);
160
+ info.Set("space", baton->space);
161
+ info.Set("channels", baton->channels);
162
+ info.Set("depth", baton->depth);
163
+ if (baton->density > 0) {
164
+ info.Set("density", baton->density);
165
+ }
166
+ if (!baton->chromaSubsampling.empty()) {
167
+ info.Set("chromaSubsampling", baton->chromaSubsampling);
168
+ }
169
+ info.Set("isProgressive", baton->isProgressive);
170
+ if (baton->paletteBitDepth > 0) {
171
+ info.Set("paletteBitDepth", baton->paletteBitDepth);
172
+ }
173
+ if (baton->pages > 0) {
174
+ info.Set("pages", baton->pages);
175
+ }
176
+ if (baton->pageHeight > 0) {
177
+ info.Set("pageHeight", baton->pageHeight);
178
+ }
179
+ if (baton->loop >= 0) {
180
+ info.Set("loop", baton->loop);
181
+ }
182
+ if (!baton->delay.empty()) {
183
+ int i = 0;
184
+ Napi::Array delay = Napi::Array::New(env, static_cast<size_t>(baton->delay.size()));
185
+ for (int const d : baton->delay) {
186
+ delay.Set(i++, d);
187
+ }
188
+ info.Set("delay", delay);
189
+ }
190
+ if (baton->pagePrimary > -1) {
191
+ info.Set("pagePrimary", baton->pagePrimary);
192
+ }
193
+ if (!baton->compression.empty()) {
194
+ info.Set("compression", baton->compression);
195
+ }
196
+ if (!baton->resolutionUnit.empty()) {
197
+ info.Set("resolutionUnit", baton->resolutionUnit == "in" ? "inch" : baton->resolutionUnit);
198
+ }
199
+ if (!baton->formatMagick.empty()) {
200
+ info.Set("formatMagick", baton->formatMagick);
201
+ }
202
+ if (!baton->levels.empty()) {
203
+ int i = 0;
204
+ Napi::Array levels = Napi::Array::New(env, static_cast<size_t>(baton->levels.size()));
205
+ for (std::pair<int, int> const &l : baton->levels) {
206
+ Napi::Object level = Napi::Object::New(env);
207
+ level.Set("width", l.first);
208
+ level.Set("height", l.second);
209
+ levels.Set(i++, level);
210
+ }
211
+ info.Set("levels", levels);
212
+ }
213
+ if (baton->subifds > 0) {
214
+ info.Set("subifds", baton->subifds);
215
+ }
216
+ if (!baton->background.empty()) {
217
+ if (baton->background.size() == 3) {
218
+ Napi::Object background = Napi::Object::New(env);
219
+ background.Set("r", baton->background[0]);
220
+ background.Set("g", baton->background[1]);
221
+ background.Set("b", baton->background[2]);
222
+ info.Set("background", background);
223
+ } else {
224
+ info.Set("background", baton->background[0]);
225
+ }
226
+ }
227
+ info.Set("hasProfile", baton->hasProfile);
228
+ info.Set("hasAlpha", baton->hasAlpha);
229
+ if (baton->orientation > 0) {
230
+ info.Set("orientation", baton->orientation);
231
+ }
232
+ if (baton->exifLength > 0) {
233
+ info.Set("exif", Napi::Buffer<char>::NewOrCopy(env, baton->exif, baton->exifLength, sharp::FreeCallback));
234
+ }
235
+ if (baton->iccLength > 0) {
236
+ info.Set("icc", Napi::Buffer<char>::NewOrCopy(env, baton->icc, baton->iccLength, sharp::FreeCallback));
237
+ }
238
+ if (baton->iptcLength > 0) {
239
+ info.Set("iptc", Napi::Buffer<char>::NewOrCopy(env, baton->iptc, baton->iptcLength, sharp::FreeCallback));
240
+ }
241
+ if (baton->xmpLength > 0) {
242
+ info.Set("xmp", Napi::Buffer<char>::NewOrCopy(env, baton->xmp, baton->xmpLength, sharp::FreeCallback));
243
+ }
244
+ if (baton->tifftagPhotoshopLength > 0) {
245
+ info.Set("tifftagPhotoshop",
246
+ Napi::Buffer<char>::NewOrCopy(env, baton->tifftagPhotoshop,
247
+ baton->tifftagPhotoshopLength, sharp::FreeCallback));
248
+ }
249
+ Callback().Call(Receiver().Value(), { env.Null(), info });
250
+ } else {
251
+ Callback().Call(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() });
252
+ }
253
+
254
+ delete baton->input;
255
+ delete baton;
256
+ }
257
+
258
+ private:
259
+ MetadataBaton* baton;
260
+ Napi::FunctionReference debuglog;
261
+ };
262
+
263
+ /*
264
+ metadata(options, callback)
265
+ */
266
+ Napi::Value metadata(const Napi::CallbackInfo& info) {
267
+ // V8 objects are converted to non-V8 types held in the baton struct
268
+ MetadataBaton *baton = new MetadataBaton;
269
+ Napi::Object options = info[size_t(0)].As<Napi::Object>();
270
+
271
+ // Input
272
+ baton->input = sharp::CreateInputDescriptor(options.Get("input").As<Napi::Object>());
273
+
274
+ // Function to notify of libvips warnings
275
+ Napi::Function debuglog = options.Get("debuglog").As<Napi::Function>();
276
+
277
+ // Join queue for worker thread
278
+ Napi::Function callback = info[size_t(1)].As<Napi::Function>();
279
+ MetadataWorker *worker = new MetadataWorker(callback, baton, debuglog);
280
+ worker->Receiver().Set("options", options);
281
+ worker->Queue();
282
+
283
+ // Increment queued task counter
284
+ sharp::counterQueue++;
285
+
286
+ return info.Env().Undefined();
287
+ }