Tue, 21 Jan 2003 18:30:49 +0000
added command-line mode
1 /*
2 * t2p: Create a PDF file from the contents of one or more TIFF
3 * bilevel image files. The images in the resulting PDF file
4 * will be compressed using ITU-T T.6 (G4) fax encoding.
5 *
6 * Main program
7 * $Id: tumble.c,v 1.20 2003/01/21 10:30:49 eric Exp $
8 * Copyright 2001, 2002, 2003 Eric Smith <eric@brouhaha.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation. Note that permission is
13 * not granted to redistribute this program under the terms of any
14 * other version of the General Public License.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA */
26 #include <stdarg.h>
27 #include <stdbool.h>
28 #include <stdint.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
33 #include <tiffio.h>
34 #define TIFF_REVERSE_BITS
36 #include <panda/functions.h>
37 #include <panda/constants.h>
39 #include "bitblt.h"
40 #include "semantics.h"
41 #include "parser.tab.h"
42 #include "t2p.h"
45 #define MAX_INPUT_FILES 5000
47 #define POINTS_PER_INCH 72
49 /* page size limited by Acrobat Reader to 45 inches on a side */
50 #define PAGE_MAX_INCHES 45
51 #define PAGE_MAX_POINTS (PAGE_MAX_INCHES * POINTS_PER_INCH)
54 typedef struct output_file_t
55 {
56 struct output_file_t *next;
57 char *name;
58 panda_pdf *pdf;
59 } output_file_t;
62 int verbose;
65 char *in_filename;
66 TIFF *in;
67 output_file_t *output_files;
68 output_file_t *out;
69 /* panda_pdf *out; */
72 char *progname;
75 bool close_tiff_input_file (void);
76 bool close_pdf_output_files (void);
79 void usage (void)
80 {
81 fprintf (stderr, "usage:\n");
82 fprintf (stderr, " %s [options] -s spec\n", progname);
83 fprintf (stderr, " %s [options] <input.tif>... -o <output.pdf>\n", progname);
84 fprintf (stderr, "options:\n");
85 fprintf (stderr, " -v verbose\n");
86 }
89 /* generate fatal error message to stderr, doesn't return */
90 void fatal (int ret, char *format, ...)
91 {
92 va_list ap;
94 fprintf (stderr, "fatal error");
95 if (format)
96 {
97 fprintf (stderr, ": ");
98 va_start (ap, format);
99 vfprintf (stderr, format, ap);
100 va_end (ap);
101 }
102 else
103 fprintf (stderr, "\n");
104 if (ret == 1)
105 usage ();
106 close_tiff_input_file ();
107 close_pdf_output_files ();
108 exit (ret);
109 }
112 bool close_tiff_input_file (void)
113 {
114 if (in)
115 {
116 free (in_filename);
117 TIFFClose (in);
118 }
119 in = NULL;
120 in_filename = NULL;
121 return (1);
122 }
125 bool open_tiff_input_file (char *name)
126 {
127 if (in)
128 {
129 if (strcmp (name, in_filename) == 0)
130 return (1);
131 close_tiff_input_file ();
132 }
133 in_filename = strdup (name);
134 if (! in_filename)
135 {
136 fprintf (stderr, "can't strdup input filename '%s'\n", name);
137 return (0);
138 }
139 in = TIFFOpen (name, "r");
140 if (! in)
141 {
142 fprintf (stderr, "can't open input file '%s'\n", name);
143 free (in_filename);
144 return (0);
145 }
146 return (1);
147 }
150 bool close_pdf_output_files (void)
151 {
152 output_file_t *o, *n;
154 for (o = output_files; o; o = n)
155 {
156 n = o->next;
157 panda_close (o->pdf);
158 free (o->name);
159 free (o);
160 }
161 out = NULL;
162 output_files = NULL;
163 return (1);
164 }
166 bool open_pdf_output_file (char *name,
167 pdf_file_attributes_t *attributes)
168 {
169 output_file_t *o;
171 if (out && (strcmp (name, out->name) == 0))
172 return (1);
173 for (o = output_files; o; o = o->next)
174 if (strcmp (name, o->name) == 0)
175 {
176 out = o;
177 return (1);
178 }
179 o = calloc (1, sizeof (output_file_t));
180 if (! o)
181 {
182 fprintf (stderr, "can't calloc output file struct for '%s'\n", name);
183 return (0);
184 }
186 o->name = strdup (name);
187 if (! o->name)
188 {
189 fprintf (stderr, "can't strdup output filename '%s'\n", name);
190 free (o);
191 return (0);
192 }
194 o->pdf = panda_open (name, "w");
195 if (! o->pdf)
196 {
197 fprintf (stderr, "can't open output file '%s'\n", name);
198 free (o->name);
199 free (o);
200 return (0);
201 }
203 if (attributes->author)
204 panda_setauthor (o->pdf, attributes->author);
205 if (attributes->creator)
206 panda_setcreator (o->pdf, attributes->creator);
207 if (attributes->title)
208 panda_settitle (o->pdf, attributes->title);
209 if (attributes->subject)
210 panda_setsubject (o->pdf, attributes->subject);
211 if (attributes->keywords)
212 panda_setkeywords (o->pdf, attributes->keywords);
214 /* prepend new output file onto list */
215 o->next = output_files;
216 output_files = o;
218 out = o;
219 return (1);
220 }
223 void process_page_numbers (int page_index,
224 int count,
225 int base,
226 page_label_t *page_label)
227 {
228 }
231 /* frees original! */
232 static Bitmap *resize_bitmap (Bitmap *src,
233 float x_resolution,
234 float y_resolution,
235 input_attributes_t input_attributes)
236 {
237 Rect src_rect;
238 Point dest_min;
239 Bitmap *dest;
241 int width_pixels = input_attributes.page_size.width * x_resolution;
242 int height_pixels = input_attributes.page_size.height * y_resolution;
244 src_rect.min.x = (rect_width (& src->rect) - width_pixels) / 2;
245 src_rect.min.y = (rect_height (& src->rect) - height_pixels) / 2;
246 src_rect.max.x = src_rect.min.x + width_pixels;
247 src_rect.max.y = src_rect.min.y + height_pixels;
249 dest_min.x = 0;
250 dest_min.y = 0;
252 dest = bitblt (src, & src_rect, NULL, & dest_min, TF_SRC, 0);
253 free_bitmap (src);
254 return (dest);
255 }
258 /* "in place" rotation */
259 static void rotate_bitmap (Bitmap *src,
260 input_attributes_t input_attributes)
261 {
262 switch (input_attributes.rotation)
263 {
264 case 0: break;
265 case 90: rot_90 (src); break;
266 case 180: rot_180 (src); break;
267 case 270: rot_270 (src); break;
268 default:
269 fprintf (stderr, "rotation must be 0, 90, 180, or 270\n");
270 }
271 }
274 #define SWAP(type,a,b) do { type temp; temp = a; a = b; b = temp; } while (0)
277 bool last_tiff_page (void)
278 {
279 return (TIFFLastDirectory (in));
280 }
283 bool process_page (int image, /* range 1 .. n */
284 input_attributes_t input_attributes,
285 bookmark_t *bookmarks)
286 {
287 int result = 0;
289 uint32_t image_length, image_width;
290 uint32_t dest_image_length, dest_image_width;
291 #ifdef CHECK_DEPTH
292 uint32_t image_depth;
293 #endif
295 uint16_t samples_per_pixel;
296 uint16_t bits_per_sample;
297 uint16_t planar_config;
299 uint16_t resolution_unit;
300 float x_resolution, y_resolution;
301 float dest_x_resolution, dest_y_resolution;
303 int width_points, height_points; /* really 1/72 inch units rather than
304 points */
306 Rect rect;
307 Bitmap *bitmap;
309 int row;
311 panda_page *page;
313 int tiff_temp_fd;
314 char tiff_temp_fn [] = "/var/tmp/t2p-XXXXXX\0";
315 TIFF *tiff_temp;
317 char pagesize [26]; /* Needs to hold two ints of four characters (0..3420),
318 two zeros, three spaces, two brackets, and a NULL.
319 Added an extra ten characters just in case. */
321 if (! TIFFSetDirectory (in, image - 1))
322 {
323 fprintf (stderr, "can't find page %d of input file\n", image);
324 goto fail;
325 }
326 if (1 != TIFFGetField (in, TIFFTAG_IMAGELENGTH, & image_length))
327 {
328 fprintf (stderr, "can't get image length\n");
329 goto fail;
330 }
331 if (1 != TIFFGetField (in, TIFFTAG_IMAGEWIDTH, & image_width))
332 {
333 fprintf (stderr, "can't get image width\n");
334 goto fail;
335 }
337 if (1 != TIFFGetField (in, TIFFTAG_SAMPLESPERPIXEL, & samples_per_pixel))
338 {
339 fprintf (stderr, "can't get samples per pixel\n");
340 goto fail;
341 }
343 #ifdef CHECK_DEPTH
344 if (1 != TIFFGetField (in, TIFFTAG_IMAGEDEPTH, & image_depth))
345 {
346 fprintf (stderr, "can't get image depth\n");
347 goto fail;
348 }
349 #endif
351 if (1 != TIFFGetField (in, TIFFTAG_BITSPERSAMPLE, & bits_per_sample))
352 {
353 fprintf (stderr, "can't get bits per sample\n");
354 goto fail;
355 }
357 if (1 != TIFFGetField (in, TIFFTAG_PLANARCONFIG, & planar_config))
358 planar_config = 1;
360 if (1 != TIFFGetField (in, TIFFTAG_RESOLUTIONUNIT, & resolution_unit))
361 resolution_unit = 2;
362 if (1 != TIFFGetField (in, TIFFTAG_XRESOLUTION, & x_resolution))
363 x_resolution = 300;
364 if (1 != TIFFGetField (in, TIFFTAG_YRESOLUTION, & y_resolution))
365 y_resolution = 300;
367 if (samples_per_pixel != 1)
368 {
369 fprintf (stderr, "samples per pixel %u, must be 1\n", samples_per_pixel);
370 goto fail;
371 }
373 #ifdef CHECK_DEPTH
374 if (image_depth != 1)
375 {
376 fprintf (stderr, "image depth %u, must be 1\n", image_depth);
377 goto fail;
378 }
379 #endif
381 if (bits_per_sample != 1)
382 {
383 fprintf (stderr, "bits per sample %u, must be 1\n", bits_per_sample);
384 goto fail;
385 }
387 if (planar_config != 1)
388 {
389 fprintf (stderr, "planar config %u, must be 1\n", planar_config);
390 goto fail;
391 }
393 if (input_attributes.has_resolution)
394 {
395 x_resolution = input_attributes.x_resolution;
396 y_resolution = input_attributes.y_resolution;
397 }
399 if ((input_attributes.rotation == 90) || (input_attributes.rotation == 270))
400 {
401 dest_image_width = image_length;
402 dest_image_length = image_width;
403 dest_x_resolution = y_resolution;
404 dest_y_resolution = x_resolution;
405 SWAP (int, width_points, height_points);
406 }
407 else
408 {
409 dest_image_width = image_width;
410 dest_image_length = image_length;
411 dest_x_resolution = x_resolution;
412 dest_y_resolution = y_resolution;
413 }
415 rect.min.x = 0;
416 rect.min.y = 0;
417 rect.max.x = image_width;
418 rect.max.y = image_length;
420 bitmap = create_bitmap (& rect);
422 if (! bitmap)
423 {
424 fprintf (stderr, "can't allocate bitmap\n");
425 goto fail;
426 }
428 for (row = 0; row < image_length; row++)
429 if (1 != TIFFReadScanline (in,
430 bitmap->bits + row * bitmap->row_words,
431 row,
432 0))
433 {
434 fprintf (stderr, "can't read TIFF scanline\n");
435 goto fail;
436 }
438 #ifdef TIFF_REVERSE_BITS
439 reverse_bits ((uint8_t *) bitmap->bits,
440 image_length * bitmap->row_words * sizeof (word_type));
441 #endif /* TIFF_REVERSE_BITS */
443 if (input_attributes.has_page_size)
444 bitmap = resize_bitmap (bitmap,
445 x_resolution,
446 y_resolution,
447 input_attributes);
449 rotate_bitmap (bitmap,
450 input_attributes);
452 tiff_temp_fd = mkstemp (tiff_temp_fn);
453 if (tiff_temp_fd < 0)
454 {
455 fprintf (stderr, "can't create temporary TIFF file\n");
456 goto fail;
457 }
459 tiff_temp = TIFFFdOpen (tiff_temp_fd, tiff_temp_fn, "w");
460 if (! out)
461 {
462 fprintf (stderr, "can't open temporary TIFF file '%s'\n", tiff_temp_fn);
463 goto fail;
464 }
466 TIFFSetField (tiff_temp, TIFFTAG_IMAGELENGTH, rect_height (& bitmap->rect));
467 TIFFSetField (tiff_temp, TIFFTAG_IMAGEWIDTH, rect_width (& bitmap->rect));
468 TIFFSetField (tiff_temp, TIFFTAG_PLANARCONFIG, planar_config);
470 TIFFSetField (tiff_temp, TIFFTAG_ROWSPERSTRIP, rect_height (& bitmap->rect));
472 TIFFSetField (tiff_temp, TIFFTAG_RESOLUTIONUNIT, resolution_unit);
473 TIFFSetField (tiff_temp, TIFFTAG_XRESOLUTION, dest_x_resolution);
474 TIFFSetField (tiff_temp, TIFFTAG_YRESOLUTION, dest_y_resolution);
476 TIFFSetField (tiff_temp, TIFFTAG_SAMPLESPERPIXEL, samples_per_pixel);
477 TIFFSetField (tiff_temp, TIFFTAG_BITSPERSAMPLE, bits_per_sample);
478 TIFFSetField (tiff_temp, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX4);
479 TIFFSetField (tiff_temp, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
481 #ifdef TIFF_REVERSE_BITS
482 reverse_bits ((uint8_t *) bitmap->bits,
483 image_length * bitmap->row_words * sizeof (word_type));
484 #endif /* TIFF_REVERSE_BITS */
486 for (row = 0; row < rect_height (& bitmap->rect); row++)
487 if (1 != TIFFWriteScanline (tiff_temp,
488 bitmap->bits + row * bitmap->row_words,
489 row,
490 0))
491 {
492 fprintf (stderr, "can't write TIFF scanline\n");
493 goto fail;
494 }
496 TIFFClose (tiff_temp);
498 width_points = (rect_width (& bitmap->rect) / dest_x_resolution) * POINTS_PER_INCH;
499 height_points = (rect_height (& bitmap->rect) / dest_y_resolution) * POINTS_PER_INCH;
501 free_bitmap (bitmap);
503 if ((height_points > PAGE_MAX_POINTS) || (width_points > PAGE_MAX_POINTS))
504 {
505 fprintf (stdout, "image too large (max %d inches on a side\n", PAGE_MAX_INCHES);
506 goto fail;
507 }
509 sprintf (pagesize, "[0 0 %d %d]", width_points, height_points);
511 page = panda_newpage (out->pdf, pagesize);
512 panda_imagebox (out->pdf,
513 page,
514 0, /* top */
515 0, /* left */
516 height_points, /* bottom */
517 width_points, /* right */
518 tiff_temp_fn,
519 panda_image_tiff);
521 result = 1;
523 fail:
524 if (tiff_temp_fd)
525 unlink (tiff_temp_fn);
526 return (result);
527 }
530 void main_args (char *out_fn, int inf_count, char **in_fn)
531 {
532 int i, ip;
533 input_attributes_t input_attributes;
534 pdf_file_attributes_t output_attributes;
536 memset (& input_attributes, 0, sizeof (input_attributes));
537 memset (& output_attributes, 0, sizeof (output_attributes));
539 if (! open_pdf_output_file (out_fn, & output_attributes))
540 fatal (3, "error opening output file \"%s\"\n", out_fn);
541 for (i = 0; i < inf_count; i++)
542 {
543 if (! open_tiff_input_file (in_fn [i]))
544 fatal (3, "error opening input file \"%s\"\n", in_fn [i]);
545 for (ip = 1;; ip++)
546 {
547 if (! process_page (ip, input_attributes, NULL))
548 fatal (3, "error processing page %d of input file \"%s\"\n", ip, in_fn [i]);
549 if (last_tiff_page ())
550 break;
551 }
552 if (verbose)
553 fprintf (stderr, "processed %d pages of input file \"%s\"\n", ip, in_fn [i]);
554 if (! close_tiff_input_file ())
555 fatal (3, "error closing input file \"%s\"\n", in_fn [i]);
556 }
557 if (! close_pdf_output_files ())
558 fatal (3, "error closing output file \"%s\"\n", out_fn);
559 }
562 void main_spec (char *spec_fn)
563 {
564 if (! parse_spec_file (spec_fn))
565 fatal (2, "error parsing spec file\n");
566 if (! process_specs ())
567 fatal (3, "error processing spec file\n");
568 }
571 int main (int argc, char *argv[])
572 {
573 char *spec_fn = NULL;
574 char *out_fn = NULL;
575 int inf_count = 0;
576 char *in_fn [MAX_INPUT_FILES];
578 progname = argv [0];
580 panda_init ();
582 while (--argc)
583 {
584 if (argv [1][0] == '-')
585 {
586 if (strcmp (argv [1], "-v") == 0)
587 verbose++;
588 else if (strcmp (argv [1], "-o") == 0)
589 {
590 if (argc)
591 {
592 argc--;
593 argv++;
594 out_fn = argv [1];
595 }
596 else
597 fatal (1, "missing filename after \"-o\" option\n");
598 }
599 else if (strcmp (argv [1], "-s") == 0)
600 {
601 if (argc)
602 {
603 argc--;
604 argv++;
605 spec_fn = argv [1];
606 }
607 else
608 fatal (1, "missing filename after \"-s\" option\n");
609 }
610 else
611 fatal (1, "unrecognized option \"%s\"\n", argv [1]);
612 }
613 else if (inf_count < MAX_INPUT_FILES)
614 in_fn [inf_count++] = argv [1];
615 else
616 fatal (1, "exceeded maximum of %d input files\n", MAX_INPUT_FILES);
617 argv++;
618 }
620 if (! ((! out_fn) ^ (! spec_fn)))
621 fatal (1, "either a spec file or an output file (but not both) must be specified\n");
623 if (out_fn && ! inf_count)
624 fatal (1, "no input files specified\n");
626 if (spec_fn && inf_count)
627 fatal (1, "if spec file is provided, input files can't be specified as arguments\n");
629 if (spec_fn)
630 main_spec (spec_fn);
631 else
632 main_args (out_fn, inf_count, in_fn);
634 close_tiff_input_file ();
635 close_pdf_output_files ();
636 exit (0);
637 }