]> 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)
committerGitHub <noreply@github.com>
Sat, 14 May 2022 07:12:36 +0000 (09:12 +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

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

index 6183006f45d112b56cfa9aa5d3b413d24f54e3f3..9f4ddc51a755b9ca417144078c9d953ae8f48df0 100644 (file)
@@ -70,7 +70,7 @@ foomatic-rip
 gstoraster
 imagetopdf
 imagetoraster
-mupdftoraster
+mupdftopwg
 pclmtoraster
 pdftoijs
 pdftoopvp
index 10e9161c52ce0d8a82ba846efb10a816ab4f8281..c13c293b992bab3909e84cf593f953c8ff16e689 100644 (file)
@@ -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 = \
index 333c6b6cf058a413adab584132cf8c773358dc95..d1940502d29217475699507981924de4c539afcb 100644 (file)
@@ -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}
index f23019085b18832c03d1bae741544727c45f09ce..76ce4827eadbd732f67ac3fce2afa7fbfb90191d 100644 (file)
@@ -23,6 +23,7 @@
 #ifdef HAVE_LIBJPEG
 #  include <jpeglib.h> /* 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)
     {
index 66f9edf77a821663b0832ee96e4d941cc65ad894..5087e64c3cd8501e82aadc7256ed6ac09cd5649f 100644 (file)
@@ -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);
index b4865796474aa77b109c500f5cc7226e780a1bee..c2b6beb9244d0e1c8fe88a9452f129ef63bd4bb6 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...
@@ -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_ */
 
index 2ab477e2dd9d98264fe2c154fa0cc4a855876c4e..8fa85324244c04f155ebb3d8ca112b0bda85530e 100644 (file)
@@ -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...
   */
index 90000901584c3a89f2e408975b380e1fc7a7c39d..b4561c140af0dcfdfb5ba43b4e4f3836704c977d 100644 (file)
  *   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