UNPKG

11.1 kBtext/x-cView Raw
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
13class 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 g_atomic_int_dec_and_test(&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.MakeCallback(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().MakeCallback(Receiver().Value(), { env.Null(), info });
250 } else {
251 Callback().MakeCallback(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*/
266Napi::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 g_atomic_int_inc(&sharp::counterQueue);
285
286 return info.Env().Undefined();
287}