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

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