@revizly/sharp 0.33.2-revizly3

Sign up to get free protection for your applications and to get access to all the features.
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
+ }