Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
image-resolution.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Authors:
4 * Daniel Wagenaar <daw@caltech.edu>
5 *
6 * Copyright (C) 2012 Authors
7 *
8 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
9 */
10
11#ifdef HAVE_CONFIG_H
12# include "config.h" // only include where actually required!
13#endif
14
15#include "util/units.h"
16#include "image-resolution.h"
17
18#define IR_TRY_PNG 1
19#include <png.h>
20
21#ifdef HAVE_EXIF
22#include <math.h>
23#include <libexif/exif-data.h>
24#endif
25
26#define IR_TRY_EXIV 0
27
28#ifdef HAVE_JPEG
29#define IR_TRY_JFIF 1
30#include <jpeglib.h>
31#include <csetjmp>
32#endif
33
34#ifdef WITH_MAGICK
35#include <Magick++.h>
36#endif
37
38#define noIMAGE_RESOLUTION_DEBUG
39
40#ifdef IMAGE_RESOLUTION_DEBUG
41# define debug(f, a...) { g_print("%s(%d) %s:", \
42 __FILE__,__LINE__,__FUNCTION__); \
43 g_print(f, ## a); \
44 g_print("\n"); \
45 }
46#else
47# define debug(f, a...) /* */
48#endif
49
50namespace Inkscape {
51namespace Extension {
52namespace Internal {
53
55 ok_ = false;
56
57 readpng(fn);
58 if (!ok_) {
59 readexiv(fn);
60 }
61 if (!ok_) {
62 readjfif(fn);
63 }
64 if (!ok_) {
65 readexif(fn);
66 }
67 if (!ok_) {
69 }
70}
71
72#if IR_TRY_PNG
73
74static bool haspngheader(FILE *fp) {
75 unsigned char header[8];
76 if ( fread(header, 1, 8, fp) != 8 ) {
77 return false;
78 }
79
80 fseek(fp, 0, SEEK_SET);
81
82 if (png_sig_cmp(header, 0, 8)) {
83 return false;
84 }
85
86 return true;
87}
88
89// Implementation using libpng
90void ImageResolution::readpng(char const *fn) {
91 FILE *fp = fopen(fn, "rb");
92 if (!fp)
93 return;
94
95 if (!haspngheader(fp)) {
96 fclose(fp);
97 return;
98 }
99
101 if (!png_ptr)
102 return;
103
105 if (!info_ptr) {
106 png_destroy_read_struct(&png_ptr, nullptr, nullptr);
107 return;
108 }
109
110 if (setjmp(png_jmpbuf(png_ptr))) {
112 fclose(fp);
113 return;
114 }
115
116 png_init_io(png_ptr, fp);
118
120#ifdef PNG_INCH_CONVERSIONS_SUPPORTED
121 debug("PNG_INCH_CONVERSIONS_SUPPORTED");
124 if (res_x != 0 && res_y != 0) {
125 ok_ = true;
126 x_ = res_x * 1.0; // FIXME: implicit conversion of png_uint_32 to double ok?
127 y_ = res_y * 1.0; // FIXME: implicit conversion of png_uint_32 to double ok?
128 }
129#else
130 debug("PNG_RESOLUTION_METER");
131 int unit_type;
132 // FIXME: png_get_pHYs() fails to return expected values
133 // with clang (based on LLVM 3.2svn) from Xcode 4.6.3 (OS X 10.7.5)
135
137 ok_ = true;
138 x_ = res_x * 2.54 / 100;
139 y_ = res_y * 2.54 / 100;
140 }
141#endif
142
144 fclose(fp);
145
146 if (ok_) {
147 debug("xdpi: %f", x_);
148 debug("ydpi: %f", y_);
149 } else {
150 debug("FAILED");
151 }
152}
153#else
154
155// Dummy implementation
156void ImageResolution::readpng(char const *) {
157}
158
159#endif
160
161#if IR_TRY_EXIF
162
163static double exifDouble(ExifEntry *entry, ExifByteOrder byte_order) {
164 switch (entry->format) {
165 case EXIF_FORMAT_BYTE: {
166 return double(entry->data[0]);
167 }
168 case EXIF_FORMAT_SHORT: {
169 return double(exif_get_short(entry->data, byte_order));
170 }
171 case EXIF_FORMAT_LONG: {
172 return double(exif_get_long(entry->data, byte_order));
173 }
174 case EXIF_FORMAT_RATIONAL: {
175 ExifRational r = exif_get_rational(entry->data, byte_order);
176 return double(r.numerator) / double(r.denominator);
177 }
178 case EXIF_FORMAT_SBYTE: {
179 return double(*(signed char *)entry->data);
180 }
181 case EXIF_FORMAT_SSHORT: {
182 return double(exif_get_sshort(entry->data, byte_order));
183 }
184 case EXIF_FORMAT_SLONG: {
185 return double(exif_get_slong(entry->data, byte_order));
186 }
187 case EXIF_FORMAT_SRATIONAL: {
188 ExifSRational r = exif_get_srational(entry->data, byte_order);
189 return double(r.numerator) / double(r.denominator);
190 }
191 case EXIF_FORMAT_FLOAT: {
192 return double((reinterpret_cast<float *>(entry->data))[0]);
193 }
194 case EXIF_FORMAT_DOUBLE: {
195 return (reinterpret_cast<double *>(entry->data))[0];
196 }
197 default: {
198 return nan(0);
199 }
200 }
201}
202
203// Implementation using libexif
204void ImageResolution::readexif(char const *fn) {
205 ExifData *ed;
207 if (!ed)
208 return;
209
211
215
216 if ( xres && yres ) {
219 if (unit) {
220 double u = exifDouble(unit, byte_order);
221 if ( u == 3 ) {
222 x_ *= 2.54;
223 y_ *= 2.54;
224 }
225 }
226 ok_ = true;
227 }
229
230 if (ok_) {
231 debug("xdpi: %f", x_);
232 debug("ydpi: %f", y_);
233 } else {
234 debug("FAILED");
235 }
236}
237
238#else
239
240// Dummy implementation
241void ImageResolution::readexif(char const *) {
242}
243
244#endif
245
246#if IR_TRY_EXIV
247
248void ImageResolution::readexiv(char const *fn) {
249 Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(fn);
250 if (!image.get())
251 return;
252
253 image->readMetadata();
254 Exiv2::ExifData &exifData = image->exifData();
255 if (exifData.empty())
256 return;
257
258 Exiv2::ExifData::const_iterator end = exifData.end();
259 bool havex = false;
260 bool havey = false;
261 bool haveunit = false;
262 int unit;
263 for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
264 if (ok_)
265 break;
266 if ( i->tag() == 0x011a ) {
267 // X Resolution
268 x_ = i->toFloat();
269 havex = true;
270 } else if ( i->tag() == 0x011b ) {
271 // Y Resolution
272 y_ = i->toFloat();
273 havey = true;
274 } else if ( i->tag() == 0x0128 ) {
275 unit = i->toLong();
276 }
277 ok_ = havex && havey && haveunit;
278 }
279 if (haveunit) {
280 if ( unit == 3 ) {
281 x_ *= 2.54;
282 y_ *= 2.54;
283 }
284 }
285 ok_ = havex && havey;
286
287 if (ok_) {
288 debug("xdpi: %f", x_);
289 debug("ydpi: %f", y_);
290 } else {
291 debug("FAILED");
292 }
293}
294
295#else
296
297// Dummy implementation
298void ImageResolution::readexiv(char const *) {
299}
300
301#endif
302
303#if IR_TRY_JFIF
304
305static void irjfif_error_exit(j_common_ptr cinfo) {
306 longjmp(*(jmp_buf*)cinfo->client_data, 1);
307}
308
309static void irjfif_emit_message(j_common_ptr, int) {
310}
311
312static void irjfif_output_message(j_common_ptr) {
313}
314
315static void irjfif_format_message(j_common_ptr, char *) {
316}
317
318static void irjfif_reset(j_common_ptr) {
319}
320
321void ImageResolution::readjfif(char const *fn) {
322 FILE *ifd = fopen(fn, "rb");
323 if (!ifd) {
324 return;
325 }
326
329 struct jpeg_error_mgr jerr;
330
331 if (setjmp(jbuf)) {
332 fclose(ifd);
334 return;
335 }
336
337 cinfo.err = jpeg_std_error(&jerr);
339 jerr.error_exit = &irjfif_error_exit;
340 jerr.emit_message = &irjfif_emit_message;
341 jerr.output_message = &irjfif_output_message;
342 jerr.format_message = &irjfif_format_message;
343 jerr.reset_error_mgr = &irjfif_reset;
344 cinfo.client_data = (void*)&jbuf;
345
348
349 debug("cinfo.[XY]_density");
350 if (cinfo.saw_JFIF_marker) { // JFIF APP0 marker was seen
351 if ( cinfo.density_unit == 1 ) { // dots/inch
352 x_ = cinfo.X_density;
353 y_ = cinfo.Y_density;
354 ok_ = true;
355 } else if ( cinfo.density_unit == 2 ) { // dots/cm
356 x_ = cinfo.X_density * 2.54;
357 y_ = cinfo.Y_density * 2.54;
358 ok_ = true;
359 }
360 /* According to http://www.jpeg.org/public/jfif.pdf (page 7):
361 * "Xdensity and Ydensity should always be non-zero".
362 * but in some cases, they are (see LP bug #1275443) */
363 if (x_ == 0 or y_ == 0) {
364 ok_ = false;
365 }
366 }
368 fclose(ifd);
369
370 if (ok_) {
371 debug("xdpi: %f", x_);
372 debug("ydpi: %f", y_);
373 } else {
374 debug("FAILED");
375 }
376}
377
378#else
379
380// Dummy implementation
381void ImageResolution::readjfif(char const *) {
382}
383
384#endif
385
386#ifdef WITH_MAGICK
387void ImageResolution::readmagick(char const *fn) {
388 Magick::Image image;
389 debug("Trying image.read");
390 try {
391 image.read(fn);
392 } catch (Magick::Error & err) {
393 debug("ImageMagick error: %s", err.what());
394 return;
395 } catch (std::exception & err) {
396 debug("ImageResolution::readmagick: %s", err.what());
397 return;
398 }
399 debug("image.[xy]Resolution");
400 std::string const type = image.magick();
401 x_ = image.xResolution();
402 y_ = image.yResolution();
403
404// TODO: find out why the hell the following conversion is necessary
405 if (type == "BMP") {
408 }
409
410 if (x_ != 0 && y_ != 0) {
411 ok_ = true;
412 }
413
414 if (ok_) {
415 debug("xdpi: %f", x_);
416 debug("ydpi: %f", y_);
417 } else {
418 debug("FAILED");
419 debug("Using default Inkscape import resolution");
420 }
421}
422
423#else
424
425// Dummy implementation
426void ImageResolution::readmagick(char const *) {
427}
428
429#endif /* WITH_MAGICK */
430
431}
432}
433}
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Definition units.cpp:588
std::unique_ptr< Magick::Image > image
Geom::Point end
static bool haspngheader(FILE *fp)
static void err(const char *fmt,...)
Definition pov-out.cpp:57
static void irjfif_reset(j_common_ptr)
static void irjfif_format_message(j_common_ptr, char *)
static void irjfif_output_message(j_common_ptr)
static void irjfif_error_exit(j_common_ptr cinfo)
static void irjfif_emit_message(j_common_ptr, int)
static double exifDouble(ExifEntry *entry, ExifByteOrder byte_order)
Helper class to stream background task notifications as a series of messages.