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