tumble_tiff.c

Mon, 14 Dec 2009 16:15:04 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Mon, 14 Dec 2009 16:15:04 +0000
changeset 168
ad0b9a8990ac
parent 152
e69798068425
permissions
-rw-r--r--

added support for TIFF Photometric Tag

     1 /*
     2  * tumble: build a PDF file from image files
     3  *
     4  * $Id: tumble_tiff.c,v 1.5 2003/03/20 07:22:23 eric Exp $
     5  * Copyright 2001, 2002, 2003 Eric Smith <eric@brouhaha.com>
     6  *
     7  * This program is free software; you can redistribute it and/or modify
     8  * it under the terms of the GNU General Public License version 2 as
     9  * published by the Free Software Foundation.  Note that permission is
    10  * not granted to redistribute this program under the terms of any
    11  * other version of the General Public License.
    12  *
    13  * This program is distributed in the hope that it will be useful,
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16  * GNU General Public License for more details.
    17  *
    18  * You should have received a copy of the GNU General Public License
    19  * along with this program; if not, write to the Free Software
    20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
    21  */
    24 #include <stdbool.h>
    25 #include <stdint.h>
    26 #include <stdio.h>
    27 #include <stdlib.h>
    28 #include <strings.h>  /* strcasecmp() is a BSDism */
    30 #include <tiffio.h>
    31 /*
    32  * On the x86, libtiff defaults to bit-endian bit order for no good reason.
    33  * In theory, the '-L' (and maybe '-H') should give us little-endian bit
    34  * order, but it doesn't seem to work.  Thus we reverse the bits ourselves
    35  * after we read in the file.
    36  */
    37 #define TIFF_REVERSE_BITS
    39 /*
    40  * If we're reading in a TIFF file that doesn't have a
    41  * PhotometricInterpretation TIFF tag (TIFFTAG_PHOTOMETRIC), then this
    42  * value will be assumed.
    43  *
    44  * This is the default value used by tumble-0.33 before the Photometric
    45  * Tag read changes. ImageMagick often produces G4TIFF images which have
    46  * black and white reversed, and the photometric tag set accordingly; in
    47  * these cases, Tumble will create a PDF file with B+W swapped.
    48  *
    49  * Ideally this should be a command-line parameter.
    50  */
    51 #define TUMBLE_TIFF_PHOTOMETRIC_ASSUME 0
    53 #include "semantics.h"
    54 #include "tumble.h"
    55 #include "bitblt.h"
    56 #include "pdf.h"
    57 #include "tumble_input.h"
    60 TIFF *tiff_in;
    63 #define SWAP(type,a,b) do { type temp; temp = a; a = b; b = temp; } while (0)
    66 static bool match_tiff_suffix (char *suffix)
    67 {
    68   return ((strcasecmp (suffix, ".tif") == 0) ||
    69 	  (strcasecmp (suffix, ".tiff") == 0));
    70 }
    73 static bool close_tiff_input_file (void)
    74 {
    75   TIFFClose (tiff_in);
    76   return (1);
    77 }
    80 static bool open_tiff_input_file (FILE *f, char *name)
    81 {
    82   uint8_t buf [2];
    83   size_t l;
    85   l = fread (& buf [0], 1, sizeof (buf), f);
    86   if (l != sizeof (buf))
    87     return (0);
    89   rewind (f);
    91   if (! (((buf [0] == 0x49) && (buf [1] == 0x49)) ||
    92 	 ((buf [0] == 0x4d) && (buf [1] == 0x4d))))
    93     return (0);
    95   tiff_in = TIFFFdOpen (fileno (f), name, "r");
    96   if (! tiff_in)
    97     {
    98       fprintf (stderr, "can't open input file '%s'\n", name);
    99       return (0);
   100     }
   101   return (1);
   102 }
   105 static bool last_tiff_input_page (void)
   106 {
   107   return (TIFFLastDirectory (tiff_in));
   108 }
   111 static bool get_tiff_image_info (int image,
   112 				 input_attributes_t input_attributes,
   113 				 image_info_t *image_info)
   114 {
   115   uint32_t image_height, image_width;
   116   uint16_t samples_per_pixel;
   117   uint16_t bits_per_sample;
   118   uint16_t planar_config;
   119   uint16_t photometric;
   121   uint16_t resolution_unit;
   122   float x_resolution, y_resolution;
   124   double dest_x_resolution, dest_y_resolution;
   126 #ifdef CHECK_DEPTH
   127   uint32_t image_depth;
   128 #endif
   130   if (! TIFFSetDirectory (tiff_in, image - 1))
   131     {
   132       fprintf (stderr, "can't find page %d of input file\n", image);
   133       return (0);
   134     }
   135   if (1 != TIFFGetField (tiff_in, TIFFTAG_IMAGELENGTH, & image_height))
   136     {
   137       fprintf (stderr, "can't get image height\n");
   138       return (0);
   139     }
   140   if (1 != TIFFGetField (tiff_in, TIFFTAG_IMAGEWIDTH, & image_width))
   141     {
   142       fprintf (stderr, "can't get image width\n");
   143       return (0);
   144     }
   146   if (1 != TIFFGetField (tiff_in, TIFFTAG_SAMPLESPERPIXEL, & samples_per_pixel))
   147     {
   148       fprintf (stderr, "can't get samples per pixel\n");
   149       return (0);
   150     }
   152 #ifdef CHECK_DEPTH
   153   if (1 != TIFFGetField (tiff_in, TIFFTAG_IMAGEDEPTH, & image_depth))
   154     {
   155       fprintf (stderr, "can't get image depth\n");
   156       return (0);
   157     }
   158 #endif
   160   if (1 != TIFFGetField (tiff_in, TIFFTAG_BITSPERSAMPLE, & bits_per_sample))
   161     {
   162       fprintf (stderr, "can't get bits per sample\n");
   163       return (0);
   164     }
   166   if (1 != TIFFGetField (tiff_in, TIFFTAG_PHOTOMETRIC, & photometric))
   167     {
   168       fprintf(stderr, "warning: photometric tag not present, assuming %d\n", TUMBLE_TIFF_PHOTOMETRIC_ASSUME);
   169       photometric = TUMBLE_TIFF_PHOTOMETRIC_ASSUME;
   170     }
   172   if ((photometric != 0) && (photometric != 1))
   173     {
   174       fprintf(stderr, "TIFF photometric tag not valid; got %u, must be 0 or 1\n", photometric);
   175       return (0);
   176     }
   178   if (1 != TIFFGetField (tiff_in, TIFFTAG_PLANARCONFIG, & planar_config))
   179     planar_config = 1;
   181   if (1 != TIFFGetField (tiff_in, TIFFTAG_RESOLUTIONUNIT, & resolution_unit))
   182     resolution_unit = 2;
   183   if (1 != TIFFGetField (tiff_in, TIFFTAG_XRESOLUTION, & x_resolution))
   184     x_resolution = 300;
   185   if (1 != TIFFGetField (tiff_in, TIFFTAG_YRESOLUTION, & y_resolution))
   186     y_resolution = 300;
   188   if (samples_per_pixel != 1)
   189     {
   190       fprintf (stderr, "samples per pixel %u, must be 1\n", samples_per_pixel);
   191       return (0);
   192     }
   194 #ifdef CHECK_DEPTH
   195   if (image_depth != 1)
   196     {
   197       fprintf (stderr, "image depth %u, must be 1\n", image_depth);
   198       return (0);
   199     }
   200 #endif
   202   if (bits_per_sample != 1)
   203     {
   204       fprintf (stderr, "bits per sample %u, must be 1\n", bits_per_sample);
   205       return (0);
   206     }
   208   if (planar_config != 1)
   209     {
   210       fprintf (stderr, "planar config %u, must be 1\n", planar_config);
   211       return (0);
   212     }
   214   if (input_attributes.has_resolution)
   215     {
   216       x_resolution = input_attributes.x_resolution;
   217       y_resolution = input_attributes.y_resolution;
   218     }
   220   if ((input_attributes.rotation == 90) || (input_attributes.rotation == 270))
   221     {
   222       image_info->width_samples  = image_height;
   223       image_info->height_samples = image_width;
   224       dest_x_resolution = y_resolution;
   225       dest_y_resolution = x_resolution;
   226       SWAP (double, image_info->width_points, image_info->height_points);
   227     }
   228   else
   229     {
   230       image_info->width_samples = image_width;
   231       image_info->height_samples = image_height;
   232       dest_x_resolution = x_resolution;
   233       dest_y_resolution = y_resolution;
   234     }
   236   image_info->width_points = (image_info->width_samples / dest_x_resolution) * POINTS_PER_INCH;
   237   image_info->height_points = (image_info->height_samples / dest_y_resolution) * POINTS_PER_INCH;
   239   if ((image_info->height_points > PAGE_MAX_POINTS) || 
   240       (image_info->width_points > PAGE_MAX_POINTS))
   241     {
   242       fprintf (stdout, "image too large (max %d inches on a side\n", PAGE_MAX_INCHES);
   243       return (0);
   244     }
   246   image_info->tiff_photometric_tag = photometric;
   248   return (1);
   249 }
   252 /* frees original! */
   253 static Bitmap *resize_bitmap (Bitmap *src,
   254 			      double x_resolution,
   255 			      double y_resolution,
   256 			      input_attributes_t input_attributes)
   257 {
   258   Rect src_rect;
   259   Point dest_min;
   260   Bitmap *dest;
   262   int width_pixels = input_attributes.page_size.width * x_resolution;
   263   int height_pixels = input_attributes.page_size.height * y_resolution;
   265   src_rect.min.x = (rect_width (& src->rect) - width_pixels) / 2;
   266   src_rect.min.y = (rect_height (& src->rect) - height_pixels) / 2;
   267   src_rect.max.x = src_rect.min.x + width_pixels;
   268   src_rect.max.y = src_rect.min.y + height_pixels;
   270   dest_min.x = 0;
   271   dest_min.y = 0;
   273   dest = bitblt (src, & src_rect, NULL, & dest_min, TF_SRC, 0);
   274   free_bitmap (src);
   275   return (dest);
   276 }
   279 /* "in place" rotation */
   280 static void rotate_bitmap (Bitmap *src,
   281 			   input_attributes_t input_attributes)
   282 {
   283   switch (input_attributes.rotation)
   284     {
   285     case 0: break;
   286     case 90: rot_90 (src); break;
   287     case 180: rot_180 (src); break;
   288     case 270: rot_270 (src); break;
   289     default:
   290       fprintf (stderr, "rotation must be 0, 90, 180, or 270\n");
   291     }
   292 }
   295 static bool process_tiff_image (int image,  /* range 1 .. n */
   296 				input_attributes_t input_attributes,
   297 				image_info_t *image_info,
   298 				pdf_page_handle page)
   299 {
   300   bool result = 0;
   301   Rect rect;
   302   Bitmap *bitmap = NULL;
   304   int row;
   306   rect.min.x = 0;
   307   rect.min.y = 0;
   309   if ((input_attributes.rotation == 90) || (input_attributes.rotation == 270))
   310     {
   311       rect.max.x = image_info->height_samples;
   312       rect.max.y = image_info->width_samples;
   313     }
   314   else
   315     {
   316       rect.max.x = image_info->width_samples;
   317       rect.max.y = image_info->height_samples;
   318     }
   320   bitmap = create_bitmap (& rect);
   322   if (! bitmap)
   323     {
   324       fprintf (stderr, "can't allocate bitmap\n");
   325       fprintf (stderr, "width %d height %d\n", image_info->width_samples, image_info->height_samples);
   326       goto fail;
   327     }
   329   for (row = 0; row < rect.max.y; row++)
   330     if (1 != TIFFReadScanline (tiff_in,
   331 			       bitmap->bits + row * bitmap->row_words,
   332 			       row,
   333 			       0))
   334       {
   335 	fprintf (stderr, "can't read TIFF scanline\n");
   336 	goto fail;
   337       }
   339 #ifdef TIFF_REVERSE_BITS
   340   reverse_bits ((uint8_t *) bitmap->bits,
   341 		rect.max.y * bitmap->row_words * sizeof (word_t));
   342 #endif /* TIFF_REVERSE_BITS */
   344 #if 0
   345   if (input_attributes.has_page_size)
   346     bitmap = resize_bitmap (bitmap,
   347 			    x_resolution,
   348 			    y_resolution,
   349 			    input_attributes);
   350 #endif
   352   rotate_bitmap (bitmap,
   353 		 input_attributes);
   355 #if 0
   356   pdf_write_text (page);
   357 #else
   358   pdf_write_g4_fax_image (page,
   359 			  0, 0,  /* x, y */
   360 			  image_info->width_points, image_info->height_points,
   361 			  bitmap,
   362 			  0, /* ImageMask */
   363 			  0, 0, 0,  /* r, g, b */
   364 			  image_info->tiff_photometric_tag); /* 0=BlackIs1, 1=WhiteIs1 */
   365 #endif
   367   result = 1;
   369  fail:
   370   if (bitmap)
   371     free_bitmap (bitmap);
   372   return (result);
   373 }
   376 input_handler_t tiff_handler =
   377   {
   378     match_tiff_suffix,
   379     open_tiff_input_file,
   380     close_tiff_input_file,
   381     last_tiff_input_page,
   382     get_tiff_image_info,
   383     process_tiff_image
   384   };
   387 void init_tiff_handler (void)
   388 {
   389   install_input_handler (& tiff_handler);
   390 }