]> git.ipfire.org Git - thirdparty/cups-filters.git/commitdiff
libcupsfilters: Read image resolution from EXIF data of JPEG, PNG, TIFF
authorSachin Thakan <50351794+thakan25@users.noreply.github.com>
Sat, 14 May 2022 07:12:36 +0000 (12:42 +0530)
committerTill Kamppeter <till.kamppeter@gmail.com>
Wed, 24 Aug 2022 11:40:45 +0000 (13:40 +0200)
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

(cherry picked from commit 76a49f034b6670fe5ff2ef4c6660a8075c460aeb)

Makefile.am
configure.ac
cupsfilters/image-jpeg.c
cupsfilters/image-png.c
cupsfilters/image-private.h
cupsfilters/image-tiff.c
cupsfilters/image.c

index f1fd112a60eac041dc328c5f7cccaff790a78768..47a13bc9f359341bf620601a30260edae36fd135 100644 (file)
@@ -220,12 +220,14 @@ libcupsfilters_la_SOURCES = \
 libcupsfilters_la_LIBADD = \
        $(CUPS_LIBS) \
        $(LIBJPEG_LIBS) \
+       $(EXIF_LIBS) \
        $(LIBPNG_LIBS) \
        $(TIFF_LIBS) \
        -lm
 libcupsfilters_la_CFLAGS = \
        $(CUPS_CFLAGS) \
        $(LIBJPEG_CFLAGS) \
+       $(EXIF_CFLAGS) \
        $(LIBPNG_CFLAGS) \
        $(TIFF_CFLAGS)
 libcupsfilters_la_LDFLAGS = \
index 32f9a4eba69dfd0dba632d0b87cffec6688d627a..cff1aedc9f5722aa07640e3612a3c381b7ae54df 100644 (file)
@@ -310,6 +310,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=""
@@ -931,6 +957,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}
index caa13b2bad4d1297a5a6c3f501ec2d10d0224dc7..a93fe92d1206b5f4a26c722f64897622c80fe9a2 100644 (file)
@@ -23,6 +23,7 @@
 #ifdef HAVE_LIBJPEG
 #  include <jpeglib.h> /* JPEG/JFIF image definitions */
 
+#define JPEG_APP0 0xE0 /* APP0 marker code */
 
 /*
  * '_cupsImageReadJPEG()' - Read a JPEG image file.
@@ -133,8 +134,21 @@ _cupsImageReadJPEG(
 
   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)
     {
index 9eb5618188a05b185b49aec93c60c170e1abe90f..1c25b2c953922edf825add394ae4be34789e82fd 100644 (file)
@@ -115,7 +115,21 @@ _cupsImageReadPNG(
   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);
index 4975fffdfd162d7784a0fa337c555f8d689ea66d..18ea46d98c3ed2d7c871f3c9342dc2fa420181bd 100644 (file)
 #    include <unistd.h>
 #  endif /* WIN32 */
 #  include <errno.h>
-#  include <math.h>
+#  include <math.h>    
 
+#ifdef HAVE_EXIF
+#      include <libexif/exif-data.h>
+#endif
 
 /*
  * Constants...
@@ -210,5 +213,9 @@ extern int          _cupsRasterExecPS(cups_page_header2_t *h,
 extern void            _cupsRasterAddError(const char *f, ...);
 extern void            _cupsRasterClearError(void);
 
+#ifdef HAVE_EXIF
+int            _cupsImageReadEXIF(cf_image_t *img, FILE *fp);
+#endif
+
 #endif /* !_CUPS_IMAGE_PRIVATE_H_ */
 
index b65ce5d2a10a1343b672c0d7f5bd981258b25fba..5fe89071c331e05eb8bc53d67368842c26914ee4 100644 (file)
@@ -139,8 +139,21 @@ _cupsImageReadTIFF(
  /*
   * 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 @@ _cupsImageReadTIFF(
             img->xppi, img->yppi);
   }
 
+
  /*
   * See if the image has an alpha channel...
   */
index e97dafd78e4bdd9d34b646fd9ee2e15d54955235..7c20deb756826cee7ed380b792373442c8e6e820 100644 (file)
  *   cupsImageSetMaxTiles()   - Set the maximum number of tiles to cache.
  *   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"
 
 
@@ -41,6 +47,8 @@
 
 static int             flush_tile(cups_image_t *img);
 static cups_ib_t       *get_tile(cups_image_t *img, int x, int y);
+static void             trim_spaces(char *buf);
+static char             *find_bytes(FILE *fp, int *size);
 
 
 /*
@@ -843,3 +851,123 @@ cups_image_t* cupsImageCrop(cups_image_t* img,int posw,int posh,int width,int he
   free(pixels);
   return temp;
 }
+
+#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