From 76a49f034b6670fe5ff2ef4c6660a8075c460aeb Mon Sep 17 00:00:00 2001 From: Sachin Thakan <50351794+thakan25@users.noreply.github.com> Date: Sat, 14 May 2022 12:42:36 +0530 Subject: [PATCH] libcupsfilters: Read image resolution from EXIF data of JPEG, PNG, TIFF 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 --- .gitignore | 2 +- Makefile.am | 2 + configure.ac | 27 ++++++++ cupsfilters/image-jpeg.c | 16 ++++- cupsfilters/image-png.c | 16 ++++- cupsfilters/image-private.h | 9 ++- cupsfilters/image-tiff.c | 16 ++++- cupsfilters/image.c | 127 +++++++++++++++++++++++++++++++++++- 8 files changed, 209 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 6183006f4..9f4ddc51a 100644 --- a/.gitignore +++ b/.gitignore @@ -70,7 +70,7 @@ foomatic-rip gstoraster imagetopdf imagetoraster -mupdftoraster +mupdftopwg pclmtoraster pdftoijs pdftoopvp diff --git a/Makefile.am b/Makefile.am index 10e9161c5..c13c293b9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -517,6 +517,7 @@ libcupsfilters_la_LIBADD = \ $(LCMS_LIBS) \ $(LIBQPDF_LIBS) \ $(LIBJPEG_LIBS) \ + $(EXIF_LIBS) \ $(LIBPNG_LIBS) \ $(TIFF_LIBS) \ $(POPPLER_LIBS) \ @@ -529,6 +530,7 @@ libcupsfilters_la_CFLAGS = \ $(LCMS_CFLAGS) \ $(LIBQPDF_CFLAGS) \ $(LIBJPEG_CFLAGS) \ + $(EXIF_CFLAGS) \ $(LIBPNG_CFLAGS) \ $(TIFF_CFLAGS) libcupsfilters_la_LDFLAGS = \ diff --git a/configure.ac b/configure.ac index 333c6b6cf..d1940502d 100644 --- a/configure.ac +++ b/configure.ac @@ -309,6 +309,32 @@ AS_IF([test x"$with_tiff" != "xno"], [ # 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="" @@ -976,6 +1002,7 @@ Build configuration: 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} diff --git a/cupsfilters/image-jpeg.c b/cupsfilters/image-jpeg.c index f23019085..76ce4827e 100644 --- a/cupsfilters/image-jpeg.c +++ b/cupsfilters/image-jpeg.c @@ -23,6 +23,7 @@ #ifdef HAVE_LIBJPEG # include /* JPEG/JFIF image definitions */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ /* * '_cfImageReadJPEG()' - Read a JPEG image file. @@ -133,8 +134,21 @@ _cfImageReadJPEG( 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) { diff --git a/cupsfilters/image-png.c b/cupsfilters/image-png.c index 66f9edf77..5087e64c3 100644 --- a/cupsfilters/image-png.c +++ b/cupsfilters/image-png.c @@ -115,7 +115,21 @@ _cfImageReadPNG( 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); diff --git a/cupsfilters/image-private.h b/cupsfilters/image-private.h index b48657964..c2b6beb92 100644 --- a/cupsfilters/image-private.h +++ b/cupsfilters/image-private.h @@ -31,8 +31,11 @@ # include # endif /* WIN32 */ # include -# include +# include +#ifdef HAVE_EXIF +# include +#endif /* * Constants... @@ -209,5 +212,9 @@ extern cf_izoom_t *_cfImageZoomNew(cf_image_t *img, int xc0, int yc0, 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_ */ diff --git a/cupsfilters/image-tiff.c b/cupsfilters/image-tiff.c index 2ab477e2d..8fa853242 100644 --- a/cupsfilters/image-tiff.c +++ b/cupsfilters/image-tiff.c @@ -139,8 +139,21 @@ _cfImageReadTIFF( /* * 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)) { @@ -172,6 +185,7 @@ _cfImageReadTIFF( img->xppi, img->yppi)); } + /* * See if the image has an alpha channel... */ diff --git a/cupsfilters/image.c b/cupsfilters/image.c index 900009015..b4561c140 100644 --- a/cupsfilters/image.c +++ b/cupsfilters/image.c @@ -27,12 +27,16 @@ * 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" @@ -42,7 +46,8 @@ 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. @@ -871,3 +876,123 @@ get_tile(cf_image_t *img, /* I - Image */ 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 -- 2.47.3