]> git.imager.perl.org - imager.git/blobdiff - tiff.c
removed --noprobe option, since probing is now required to scan
[imager.git] / tiff.c
diff --git a/tiff.c b/tiff.c
index af4e44bcee17aafbcb218440f71004b0e82f414a..8406ca6b5dca1c5fb13065e275ceb97aa34b97bd 100644 (file)
--- a/tiff.c
+++ b/tiff.c
@@ -1,7 +1,7 @@
-#include "image.h"
+#include "imager.h"
 #include "tiffio.h"
 #include "iolayer.h"
-#include "imagei.h"
+#include "imageri.h"
 
 /*
 =head1 NAME
@@ -31,7 +31,6 @@ Some of these functions are internal.
 =cut
 */
 
-
 #define byteswap_macro(x) \
      ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) |     \
       (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
@@ -61,10 +60,33 @@ static void error_handler(char const *module, char const *fmt, va_list ap) {
   i_push_errorvf(0, fmt, ap);
 }
 
+#define WARN_BUFFER_LIMIT 10000
+static char *warn_buffer = NULL;
+static int warn_buffer_size = 0;
+
 static void warn_handler(char const *module, char const *fmt, va_list ap) {
-  /* for now do nothing, perhaps we could warn(), though that should be
-     done in the XS code, not in the code which isn't mean to know perl 
-     exists ;) */
+  char buf[1000];
+
+  buf[0] = '\0';
+#ifdef HAVE_SNPRINTF
+  vsnprintf(buf, sizeof(buf), fmt, ap);
+#else
+  vsprintf(buf, fmt, ap);
+#endif
+  if (!warn_buffer || strlen(warn_buffer)+strlen(buf)+2 > warn_buffer_size) {
+    int new_size = warn_buffer_size + strlen(buf) + 2;
+    char *old_buffer = warn_buffer;
+    if (new_size > WARN_BUFFER_LIMIT) {
+      new_size = WARN_BUFFER_LIMIT;
+    }
+    warn_buffer = myrealloc(warn_buffer, new_size);
+    if (!old_buffer) *warn_buffer = '\0';
+    warn_buffer_size = new_size;
+  }
+  if (strlen(warn_buffer)+strlen(buf)+2 <= warn_buffer_size) {
+    strcat(warn_buffer, buf);
+    strcat(warn_buffer, "\n");
+  }
 }
 
 static int save_tiff_tags(TIFF *tif, i_img *im);
@@ -98,7 +120,38 @@ comp_seek(thandle_t h, toff_t o, int w) {
   return (toff_t) ig->seekcb(ig, o, w);
 }
 
-static i_img *read_one_tiff(TIFF *tif) {
+/*
+=item comp_mmap(thandle_t, tdata_t*, toff_t*)
+
+Dummy mmap stub.
+
+This shouldn't ever be called but newer tifflibs want it anyway.
+
+=cut
+*/
+
+static 
+int
+comp_mmap(thandle_t h, tdata_t*p, toff_t*off) {
+  return -1;
+}
+
+/*
+=item comp_munmap(thandle_t h, tdata_t p, toff_t off)
+
+Dummy munmap stub.
+
+This shouldn't ever be called but newer tifflibs want it anyway.
+
+=cut
+*/
+
+static void
+comp_munmap(thandle_t h, tdata_t p, toff_t off) {
+  /* do nothing */
+}
+
+static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
   i_img *im;
   uint32 width, height;
   uint16 channels;
@@ -125,6 +178,26 @@ static i_img *read_one_tiff(TIFF *tif) {
   mm_log((1, "i_readtiff_wiol: %stiled\n", tiled?"":"not "));
   mm_log((1, "i_readtiff_wiol: %sbyte swapped\n", TIFFIsByteSwapped(tif)?"":"not "));
 
+  /* separated defaults to CMYK, but if the user is using some strange
+     ink system we can't work out the color anyway */
+  if (photometric == PHOTOMETRIC_SEPARATED && channels >= 4) {
+    /* TIFF can have more than one alpha channel on an image,
+       but Imager can't, only store the first one */
+    
+    channels = channels == 4 ? 3 : 4;
+
+    /* unfortunately the RGBA functions don't try to deal with the alpha
+       channel on CMYK images, at some point I'm planning on expanding
+       TIFF support to handle 16-bit/sample images and I'll deal with
+       it then */
+  }
+
+  /* TIFF images can have more than one alpha channel, but Imager can't
+     this ignores the possibility of 2 channel images with 2 alpha,
+     but there's not much I can do about that */
+  if (channels > 4)
+    channels = 4;
+
   if (photometric == PHOTOMETRIC_PALETTE && bits_per_sample <= 8) {
     channels = 3;
     im = i_img_pal_new(width, height, channels, 256);
@@ -132,6 +205,13 @@ static i_img *read_one_tiff(TIFF *tif) {
   else {
     im = i_img_empty_ch(NULL, width, height, channels);
   }
+
+  if (!im)
+    return NULL;
+
+  /* general metadata */
+  i_tags_addn(&im->tags, "tiff_bitspersample", 0, bits_per_sample);
+  i_tags_addn(&im->tags, "tiff_photometric", 0, photometric);
     
   /* resolution tags */
   TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &resunit);
@@ -142,16 +222,28 @@ static i_img *read_one_tiff(TIFF *tif) {
       xres = yres;
     else if (!gotYres)
       yres = xres;
+    i_tags_addn(&im->tags, "tiff_resolutionunit", 0, resunit);
     if (resunit == RESUNIT_CENTIMETER) {
       /* from dots per cm to dpi */
       xres *= 2.54;
       yres *= 2.54;
+      i_tags_add(&im->tags, "tiff_resolutionunit_name", 0, "centimeter", -1, 0);
     }
-    i_tags_addn(&im->tags, "tiff_resolutionunit", 0, resunit);
-    if (resunit == RESUNIT_NONE)
+    else if (resunit == RESUNIT_NONE) {
       i_tags_addn(&im->tags, "i_aspect_only", 0, 1);
-    i_tags_set_float(&im->tags, "i_xres", 0, xres);
-    i_tags_set_float(&im->tags, "i_yres", 0, yres);
+      i_tags_add(&im->tags, "tiff_resolutionunit_name", 0, "none", -1, 0);
+    }
+    else if (resunit == RESUNIT_INCH) {
+      i_tags_add(&im->tags, "tiff_resolutionunit_name", 0, "inch", -1, 0);
+    }
+    else {
+      i_tags_add(&im->tags, "tiff_resolutionunit_name", 0, "unknown", -1, 0);
+    }
+    /* tifflib doesn't seem to provide a way to get to the original rational
+       value of these, which would let me provide a more reasonable
+       precision. So make up a number. */
+    i_tags_set_float2(&im->tags, "i_xres", 0, xres, 6);
+    i_tags_set_float2(&im->tags, "i_yres", 0, yres, 6);
   }
 
   /* Text tags */
@@ -164,6 +256,12 @@ static i_img *read_one_tiff(TIFF *tif) {
                 strlen(data), 0);
     }
   }
+
+  i_tags_add(&im->tags, "i_format", 0, "tiff", -1, 0);
+  if (warn_buffer && *warn_buffer) {
+    i_tags_add(&im->tags, "i_warning", 0, warn_buffer, -1, 0);
+    *warn_buffer = '\0';
+  }
   
   /*   TIFFPrintDirectory(tif, stdout, 0); good for debugging */
 
@@ -198,6 +296,14 @@ static i_img *read_one_tiff(TIFF *tif) {
       ++row;
     }
     if (row < height) {
+      if (allow_incomplete) {
+        i_tags_setn(&im->tags, "i_lines_read", row);
+      }
+      else {
+        i_img_destroy(im);
+        _TIFFfree(buffer);
+        return NULL;
+      }
       error = 1;
     }
     /* Ideally we'd optimize the palette, but that could be expensive
@@ -284,8 +390,17 @@ static i_img *read_one_tiff(TIFF *tif) {
         uint32 newrows, i_row;
         
         if (!TIFFReadRGBAStrip(tif, row, raster)) {
-          error++;
-          break;
+          if (allow_incomplete) {
+            i_tags_setn(&im->tags, "i_lines_read", row);
+            error++;
+            break;
+          }
+          else {
+            i_push_error(0, "could not read TIFF image strip");
+            _TIFFfree(raster);
+            i_img_destroy(im);
+            return NULL;
+          }
         }
         
         newrows = (row+rowsperstrip > height) ? height-row : rowsperstrip;
@@ -308,7 +423,7 @@ static i_img *read_one_tiff(TIFF *tif) {
   }
   if (error) {
     mm_log((1, "i_readtiff_wiol: error during reading\n"));
-    i_tags_addn(&im->tags, "i_incomplete", 0, 1);
+    i_tags_setn(&im->tags, "i_incomplete", 1);
   }
   if (raster)
     _TIFFfree( raster );
@@ -322,7 +437,7 @@ static i_img *read_one_tiff(TIFF *tif) {
 =cut
 */
 i_img*
-i_readtiff_wiol(io_glue *ig, int length) {
+i_readtiff_wiol(io_glue *ig, int allow_incomplete, int page) {
   TIFF* tif;
   TIFFErrorHandler old_handler;
   TIFFErrorHandler old_warn_handler;
@@ -331,12 +446,14 @@ i_readtiff_wiol(io_glue *ig, int length) {
   i_clear_error();
   old_handler = TIFFSetErrorHandler(error_handler);
   old_warn_handler = TIFFSetWarningHandler(warn_handler);
+  if (warn_buffer)
+    *warn_buffer = '\0';
 
   /* Add code to get the filename info from the iolayer */
   /* Also add code to check for mmapped code */
 
   io_glue_commit_types(ig);
-  mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig, length));
+  mm_log((1, "i_readtiff_wiol(ig %p, allow_incomplete %d, page %d)\n", ig, allow_incomplete, page));
   
   tif = TIFFClientOpen("(Iolayer)", 
                       "rm", 
@@ -346,18 +463,29 @@ i_readtiff_wiol(io_glue *ig, int length) {
                       (TIFFSeekProc) comp_seek,
                       (TIFFCloseProc) ig->closecb,
                       ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
-                      (TIFFMapFileProc) NULL,
-                      (TIFFUnmapFileProc) NULL);
+                      (TIFFMapFileProc) comp_mmap,
+                      (TIFFUnmapFileProc) comp_munmap);
   
   if (!tif) {
     mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
-    i_push_error(0, "opening file");
+    i_push_error(0, "Error opening file");
     TIFFSetErrorHandler(old_handler);
     TIFFSetWarningHandler(old_warn_handler);
     return NULL;
   }
 
-  im = read_one_tiff(tif);
+  if (page != 0) {
+    if (!TIFFSetDirectory(tif, page)) {
+      mm_log((1, "i_readtiff_wiol: Unable to switch to directory %d\n", page));
+      i_push_errorf(0, "could not switch to page %d", page);
+      TIFFSetErrorHandler(old_handler);
+      TIFFSetWarningHandler(old_warn_handler);
+      TIFFClose(tif);
+      return NULL;
+    }
+  }
+
+  im = read_one_tiff(tif, allow_incomplete);
 
   if (TIFFLastDirectory(tif)) mm_log((1, "Last directory of tiff file\n"));
   TIFFSetErrorHandler(old_handler);
@@ -385,6 +513,8 @@ i_readtiff_multi_wiol(io_glue *ig, int length, int *count) {
   i_clear_error();
   old_handler = TIFFSetErrorHandler(error_handler);
   old_warn_handler = TIFFSetWarningHandler(warn_handler);
+  if (warn_buffer)
+    *warn_buffer = '\0';
 
   /* Add code to get the filename info from the iolayer */
   /* Also add code to check for mmapped code */
@@ -399,13 +529,13 @@ i_readtiff_multi_wiol(io_glue *ig, int length, int *count) {
                       (TIFFReadWriteProc) ig->writecb,
                       (TIFFSeekProc) comp_seek,
                       (TIFFCloseProc) ig->closecb,
-                      (TIFFSizeProc) ig->sizecb,
-                      (TIFFMapFileProc) NULL,
-                      (TIFFUnmapFileProc) NULL);
+                      ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
+                      (TIFFMapFileProc) comp_mmap,
+                      (TIFFUnmapFileProc) comp_munmap);
   
   if (!tif) {
     mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
-    i_push_error(0, "opening file");
+    i_push_error(0, "Error opening file");
     TIFFSetErrorHandler(old_handler);
     TIFFSetWarningHandler(old_warn_handler);
     return NULL;
@@ -413,7 +543,7 @@ i_readtiff_multi_wiol(io_glue *ig, int length, int *count) {
 
   *count = 0;
   do {
-    i_img *im = read_one_tiff(tif);
+    i_img *im = read_one_tiff(tif, 0);
     if (!im)
       break;
     if (++*count > result_alloc) {
@@ -448,7 +578,6 @@ i_writetiff_low_faxable(TIFF *tif, i_img *im, int fine) {
   uint32 y;
   int rc;
   uint32 x;
-  int luma_mask;
   uint32 rowsperstrip;
   float vres = fine ? 196 : 98;
   int luma_chan;
@@ -489,7 +618,7 @@ i_writetiff_low_faxable(TIFF *tif, i_img *im, int fine) {
     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField bitpersample=1\n")); return 0; }
   if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG))
     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField planarconfig\n")); return 0; }
-  if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK))
+  if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE))
     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField photometric=%d\n", PHOTOMETRIC_MINISBLACK)); return 0; }
   if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, 3))
     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField compression=3\n")); return 0; }
@@ -529,7 +658,7 @@ i_writetiff_low_faxable(TIFF *tif, i_img *im, int fine) {
       bits = width-x; if(bits>8) bits=8;
       i_gsamp(im, x, x+8, y, luma, &luma_chan, 1);
       for(bitpos=0;bitpos<bits;bitpos++) {
-       linebuf[linebufpos] |= ((luma[bitpos]>=128)?bitval:0);
+       linebuf[linebufpos] |= ((luma[bitpos] < 128) ? bitval : 0);
        bitval >>= 1;
       }
       linebufpos++;
@@ -560,7 +689,7 @@ i_writetiff_low(TIFF *tif, i_img *im) {
   tsize_t linebytes;
   int ch, ci, rc;
   uint32 x;
-  int got_xres, got_yres, got_aspectonly, aspect_only, resunit;
+  int got_xres, got_yres, aspect_only, resunit;
   double xres, yres;
   uint16 bitspersample = 8;
   uint16 samplesperpixel;
@@ -625,7 +754,6 @@ i_writetiff_low(TIFF *tif, i_img *im) {
     i_color c;
     int count = i_colorcount(im);
     int size;
-    int bits;
     int ch, i;
     
     samplesperpixel = 1;
@@ -809,8 +937,11 @@ Stores an image in the iolayer object.
 undef_int
 i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
   TIFF* tif;
+  TIFFErrorHandler old_handler;
   int i;
 
+  old_handler = TIFFSetErrorHandler(error_handler);
+
   io_glue_commit_types(ig);
   i_clear_error();
   mm_log((1, "i_writetiff_multi_wiol(ig 0x%p, imgs 0x%p, count %d)\n", 
@@ -825,31 +956,37 @@ i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
                       (TIFFReadWriteProc) ig->writecb,
                       (TIFFSeekProc)      comp_seek,
                       (TIFFCloseProc)     ig->closecb, 
-                      (TIFFSizeProc)      ig->sizecb,
-                      (TIFFMapFileProc)   NULL,
-                      (TIFFUnmapFileProc) NULL);
+                      ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
+                      (TIFFMapFileProc)   comp_mmap,
+                      (TIFFUnmapFileProc) comp_munmap);
   
 
 
   if (!tif) {
-    mm_log((1, "i_writetiff_mulit_wiol: Unable to open tif file for writing\n"));
+    mm_log((1, "i_writetiff_multi_wiol: Unable to open tif file for writing\n"));
+    i_push_error(0, "Could not create TIFF object");
+    TIFFSetErrorHandler(old_handler);
     return 0;
   }
 
   for (i = 0; i < count; ++i) {
     if (!i_writetiff_low(tif, imgs[i])) {
       TIFFClose(tif);
+      TIFFSetErrorHandler(old_handler);
       return 0;
     }
 
     if (!TIFFWriteDirectory(tif)) {
       i_push_error(0, "Cannot write TIFF directory");
       TIFFClose(tif);
+      TIFFSetErrorHandler(old_handler);
       return 0;
     }
   }
 
+  TIFFSetErrorHandler(old_handler);
   (void) TIFFClose(tif);
+
   return 1;
 }
 
@@ -870,6 +1007,9 @@ undef_int
 i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
   TIFF* tif;
   int i;
+  TIFFErrorHandler old_handler;
+
+  old_handler = TIFFSetErrorHandler(error_handler);
 
   io_glue_commit_types(ig);
   i_clear_error();
@@ -885,31 +1025,37 @@ i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
                       (TIFFReadWriteProc) ig->writecb,
                       (TIFFSeekProc)      comp_seek,
                       (TIFFCloseProc)     ig->closecb, 
-                      (TIFFSizeProc)      ig->sizecb,
-                      (TIFFMapFileProc)   NULL,
-                      (TIFFUnmapFileProc) NULL);
+                      ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
+                      (TIFFMapFileProc)   comp_mmap,
+                      (TIFFUnmapFileProc) comp_munmap);
   
 
 
   if (!tif) {
     mm_log((1, "i_writetiff_mulit_wiol: Unable to open tif file for writing\n"));
+    i_push_error(0, "Could not create TIFF object");
+    TIFFSetErrorHandler(old_handler);
     return 0;
   }
 
   for (i = 0; i < count; ++i) {
     if (!i_writetiff_low_faxable(tif, imgs[i], fine)) {
       TIFFClose(tif);
+      TIFFSetErrorHandler(old_handler);
       return 0;
     }
 
     if (!TIFFWriteDirectory(tif)) {
       i_push_error(0, "Cannot write TIFF directory");
       TIFFClose(tif);
+      TIFFSetErrorHandler(old_handler);
       return 0;
     }
   }
 
   (void) TIFFClose(tif);
+  TIFFSetErrorHandler(old_handler);
+
   return 1;
 }
 
@@ -926,14 +1072,16 @@ Stores an image in the iolayer object.
 undef_int
 i_writetiff_wiol(i_img *img, io_glue *ig) {
   TIFF* tif;
-  int i;
+  TIFFErrorHandler old_handler;
+
+  old_handler = TIFFSetErrorHandler(error_handler);
 
   io_glue_commit_types(ig);
   i_clear_error();
   mm_log((1, "i_writetiff_wiol(img %p, ig 0x%p)\n", img, ig));
 
   /* FIXME: Enable the mmap interface */
-  
+
   tif = TIFFClientOpen("No name", 
                       "wm",
                       (thandle_t) ig, 
@@ -941,23 +1089,28 @@ i_writetiff_wiol(i_img *img, io_glue *ig) {
                       (TIFFReadWriteProc) ig->writecb,
                       (TIFFSeekProc)      comp_seek,
                       (TIFFCloseProc)     ig->closecb, 
-                      (TIFFSizeProc)      ig->sizecb,
-                      (TIFFMapFileProc)   NULL,
-                      (TIFFUnmapFileProc) NULL);
+                      ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
+                      (TIFFMapFileProc)   comp_mmap,
+                      (TIFFUnmapFileProc) comp_munmap);
   
 
 
   if (!tif) {
     mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
+    i_push_error(0, "Could not create TIFF object");
+    TIFFSetErrorHandler(old_handler);
     return 0;
   }
 
   if (!i_writetiff_low(tif, img)) {
     TIFFClose(tif);
+    TIFFSetErrorHandler(old_handler);
     return 0;
   }
 
   (void) TIFFClose(tif);
+  TIFFSetErrorHandler(old_handler);
+
   return 1;
 }
 
@@ -981,7 +1134,9 @@ point.
 undef_int
 i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
   TIFF* tif;
-  int i;
+  TIFFErrorHandler old_handler;
+
+  old_handler = TIFFSetErrorHandler(error_handler);
 
   io_glue_commit_types(ig);
   i_clear_error();
@@ -996,23 +1151,28 @@ i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
                       (TIFFReadWriteProc) ig->writecb,
                       (TIFFSeekProc)      comp_seek,
                       (TIFFCloseProc)     ig->closecb, 
-                      (TIFFSizeProc)      ig->sizecb,
-                      (TIFFMapFileProc)   NULL,
-                      (TIFFUnmapFileProc) NULL);
+                      ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
+                      (TIFFMapFileProc)   comp_mmap,
+                      (TIFFUnmapFileProc) comp_munmap);
   
 
 
   if (!tif) {
     mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
+    i_push_error(0, "Could not create TIFF object");
+    TIFFSetErrorHandler(old_handler);
     return 0;
   }
 
   if (!i_writetiff_low_faxable(tif, im, fine)) {
     TIFFClose(tif);
+    TIFFSetErrorHandler(old_handler);
     return 0;
   }
 
   (void) TIFFClose(tif);
+  TIFFSetErrorHandler(old_handler);
+
   return 1;
 }