If images are printed with "print-scaling=none" one wants to have their original size. To get an original size in absolute dimensions (inches, cm) one needs the pixel count and resolution (dpi, ppi). The resolution is supposed to be in the image's own header but in practice it is often not there.
Images from cameras, and also from some scanners, have additional metadata, the so-called EXIF data which usually contains a resolution. So we read out this with the help of libexif.
In Linux distributions there we usually have libexif and so it is no big effort to let libcupsfilters use it. But it is not required, so for low-resource systems libcupsfilters can be built without (and then not having this feature).
This completes the fix of issue #362
Pull request #466
gstoraster
imagetopdf
imagetoraster
-mupdftoraster
+mupdftopwg
pclmtoraster
pdftoijs
pdftoopvp
$(LCMS_LIBS) \
$(LIBQPDF_LIBS) \
$(LIBJPEG_LIBS) \
+ $(EXIF_LIBS) \
$(LIBPNG_LIBS) \
$(TIFF_LIBS) \
$(POPPLER_LIBS) \
$(LCMS_CFLAGS) \
$(LIBQPDF_CFLAGS) \
$(LIBJPEG_CFLAGS) \
+ $(EXIF_CFLAGS) \
$(LIBPNG_CFLAGS) \
$(TIFF_CFLAGS)
libcupsfilters_la_LDFLAGS = \
# Check for modules needed by utils/
# ==================================
+dnl including EXIF library
+EXIF_LIBS=""
+EXIF_CFLAGS=""
+
+AC_ARG_ENABLE([exif],
+ [AS_HELP_STRING([--disable-exif], [Disable exif metadata readout from images])],
+ [enable_exif="$enableval"],
+ [enable_exif=yes]
+)
+AM_CONDITIONAL([ENABLE_EXIF], [test "x$enable_exif" != "xno"])
+
+AC_ARG_WITH(exif-libs,
+ [AS_HELP_STRING([--with-exif-libs], [Set directory for exif library.])],
+ EXIF_LIBS="-L$withval $EXIF_LIBS",)
+AC_ARG_WITH(exif-includes,
+ [AS_HELP_STRING([--with-exif-includes], [Set directory for exif includes.])],
+ EXIF_CFLAGS="-I$withval $EXIF_CFLAGS",)
+
+if test "x$enable_exif" != xno; then
+ PKG_CHECK_MODULES(EXIF, libexif,
+ [AC_DEFINE(HAVE_EXIF, [], [Define if you have the libexif library])])
+fi
+
+AC_SUBST(EXIF_LIBS)
+AC_SUBST(EXIF_CFLAGS)
+
dnl Avahi for cups-browsed
AVAHI_LIBS=""
AVAHI_CFLAGS=""
ippfind-path: ${with_ippfind_path}
imagefilters: ${enable_imagefilters}
jpeg: ${with_jpeg}
+ exif: ${enable_exif}
pdftocairo-path: ${with_pdftocairo_path}
pdftops: ${with_pdftops}
pdftops-path: ${with_pdftops_path}
#ifdef HAVE_LIBJPEG
# include <jpeglib.h> /* JPEG/JFIF image definitions */
+#define JPEG_APP0 0xE0 /* APP0 marker code */
/*
* '_cfImageReadJPEG()' - Read a JPEG image file.
img->xsize = cinfo.output_width;
img->ysize = cinfo.output_height;
+
+ int temp = -1;
- if (cinfo.X_density > 0 && cinfo.Y_density > 0 && cinfo.density_unit > 0)
+#ifdef HAVE_EXIF
+ /*
+ scan image file for exif data
+ */
+
+ temp = _cupsImageReadEXIF(img, fp);
+#endif
+ /*
+ check headers only if EXIF contains no info about ppi
+ */
+
+ if (temp != 1 && cinfo.X_density > 0 && cinfo.Y_density > 0 && cinfo.density_unit > 0)
{
if (cinfo.density_unit == 1)
{
img->xsize = width;
img->ysize = height;
- if ((xppm = png_get_x_pixels_per_meter(pp, info)) != 0 &&
+
+ int temp = -1;
+
+#ifdef HAVE_EXIF
+ /*
+ scan image file for exif data
+ */
+
+ temp = _cupsImageReadEXIF(img, fp);
+#endif
+ /*
+ check headers only if EXIF contains no info about ppi
+ */
+
+ if (temp != 1 && (xppm = png_get_x_pixels_per_meter(pp, info)) != 0 &&
(yppm = png_get_y_pixels_per_meter(pp, info)) != 0)
{
img->xppi = (int)((float)xppm * 0.0254);
# include <unistd.h>
# endif /* WIN32 */
# include <errno.h>
-# include <math.h>
+# include <math.h>
+#ifdef HAVE_EXIF
+# include <libexif/exif-data.h>
+#endif
/*
* Constants...
int ysize, int rotated,
cf_iztype_t type);
+#ifdef HAVE_EXIF
+int _cupsImageReadEXIF(cf_image_t *img, FILE *fp);
+#endif
+
#endif /* !_CUPS_IMAGE_PRIVATE_H_ */
/*
* Get the image resolution...
*/
+
+ int temp = -1;
- if (TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres) &&
+#ifdef HAVE_EXIF
+ /*
+ scan image file for exif data
+ */
+
+ temp = _cupsImageReadEXIF(img, fp);
+#endif
+ /*
+ check headers only if EXIF contains no info about ppi
+ */
+
+ if (temp != 1 && TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres) &&
TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres) &&
TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &resunit))
{
img->xppi, img->yppi));
}
+
/*
* See if the image has an alpha channel...
*/
* cfImageCrop() - Crop an image.
* flush_tile() - Flush the least-recently-used tile in the cache.
* get_tile() - Get a cached tile.
+ * _cupsImageReadEXIF() - to read exif metadata of images
+ * trim_spaces() - helper function to extract results from string returned by exif library functions
+ * find_bytes() - creates character array from image file, to make use in exif library functions
*/
/*
* Include necessary headers...
*/
+
#include "image-private.h"
static int flush_tile(cf_image_t *img);
static cf_ib_t *get_tile(cf_image_t *img, int x, int y);
-
+static void trim_spaces(char *buf);
+static char *find_bytes(FILE *fp, int *size);
/*
* 'cfImageClose()' - Close an image file.
return (ic->pixels + bpp * (y * CF_TILE_SIZE + x));
}
+
+#ifdef HAVE_EXIF
+/*
+ helper function required by EXIF read function
+ */
+
+static void trim_spaces(char *buf)
+{
+ char *s = buf - 1;
+ for (; *buf; ++buf)
+ {
+ if (*buf != ' ')
+ s = buf;
+ }
+ *++s = 0; /* null terminate the string on the first of the final spaces */
+}
+
+/*
+ implementation for EXIF read function
+ */
+
+/*
+ helper function to extract bytes from image files
+ */
+
+static char *find_bytes(FILE *fp, int *size)
+{
+ char *buf;
+
+ long int originalOffset = ftell(fp);
+ fseek(fp, 0L, SEEK_END);
+
+ // calculating the size of the file
+ long int res = ftell(fp);
+
+ buf = (char *)malloc(res * sizeof(char) + 1);
+ fseek(fp, 0, SEEK_SET);
+
+ fread(buf, res, 1, fp);
+
+ fseek(fp, originalOffset, SEEK_SET);
+ *size = res + 1;
+
+ return buf;
+}
+
+int _cupsImageReadEXIF(cf_image_t *img, FILE *fp)
+{
+
+ if (fp == NULL)
+ {
+ return -1;
+ }
+
+ int bufSize = 0;
+
+ char *buf = find_bytes(fp, &bufSize);
+
+ ExifData *ed = exif_data_new_from_data(buf, bufSize);
+
+ if (ed == NULL)
+ {
+ DEBUG_printf(("DEBUG: No EXIF data found"));
+ return 2;
+ }
+
+ ExifIfd ifd = EXIF_IFD_0;
+ ExifTag tagX = EXIF_TAG_X_RESOLUTION;
+ ExifTag tagY = EXIF_TAG_Y_RESOLUTION;
+
+ ExifEntry *entryX = exif_content_get_entry(ed->ifd[ifd], tagX);
+
+ ExifEntry *entryY = exif_content_get_entry(ed->ifd[ifd], tagY);
+
+ if (entryX == NULL || entryY == NULL)
+ {
+ DEBUG_printf(("DEBUG: No EXIF data found"));
+ return 2;
+ }
+
+ if (entryX)
+ {
+ char buf1[1024];
+
+ exif_entry_get_value(entryX, buf1, sizeof(buf1));
+
+ trim_spaces(buf1);
+
+ if (*buf1)
+ {
+ int xRes;
+ sscanf(buf1, "%d", &xRes);
+ img->xppi = xRes;
+ }
+ else{
+ return 2;
+ }
+ }
+
+ if (entryY)
+ {
+ char buf2[1024];
+
+ exif_entry_get_value(entryY, buf2, sizeof(buf2));
+ trim_spaces(buf2);
+
+ if (*buf2)
+ {
+ int yRes;
+ sscanf(buf2, "%d", &yRes);
+ img->yppi = yRes;
+ }
+ else{
+ return 2;
+ }
+ }
+
+ return 1;
+}
+#endif