Wed, 12 Mar 2003 10:58:33 +0000
start of JPEG support. changed word_type to word_t.
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: t2p.c,v 1.29 2003/03/12 02:58:33 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
24 */
27 #include <stdarg.h>
28 #include <stdbool.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
35 #include <tiffio.h>
36 #define TIFF_REVERSE_BITS
38 #include "bitblt.h"
39 #include "semantics.h"
40 #include "parser.tab.h"
41 #include "t2p.h"
42 #include "pdf.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 pdf_file_handle 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;
71 char *progname;
74 bool close_tiff_input_file (void);
75 bool close_pdf_output_files (void);
78 void usage (void)
79 {
80 fprintf (stderr, "\n");
81 fprintf (stderr, "t2p - Copyright 2001-2003 Eric Smith <eric@brouhaha.com>\n");
82 fprintf (stderr, "http://www.brouhaha.com/~eric/software/t2p/\n");
83 fprintf (stderr, "\n");
84 fprintf (stderr, "usage:\n");
85 fprintf (stderr, " %s [options] -s spec\n", progname);
86 fprintf (stderr, " %s [options] <input.tif>... -o <output.pdf>\n", progname);
87 fprintf (stderr, "options:\n");
88 fprintf (stderr, " -v verbose\n");
89 fprintf (stderr, " -b fmt create bookmarks\n");
90 fprintf (stderr, "bookmark format:\n");
91 fprintf (stderr, " %%F file name (sans suffix)\n");
92 fprintf (stderr, " %%p page number\n");
93 }
96 /* generate fatal error message to stderr, doesn't return */
97 void fatal (int ret, char *format, ...)
98 {
99 va_list ap;
101 fprintf (stderr, "fatal error");
102 if (format)
103 {
104 fprintf (stderr, ": ");
105 va_start (ap, format);
106 vfprintf (stderr, format, ap);
107 va_end (ap);
108 }
109 else
110 fprintf (stderr, "\n");
111 if (ret == 1)
112 usage ();
113 close_tiff_input_file ();
114 close_pdf_output_files ();
115 exit (ret);
116 }
119 bool close_tiff_input_file (void)
120 {
121 if (in)
122 {
123 free (in_filename);
124 TIFFClose (in);
125 }
126 in = NULL;
127 in_filename = NULL;
128 return (1);
129 }
132 bool open_tiff_input_file (char *name)
133 {
134 if (in)
135 {
136 if (strcmp (name, in_filename) == 0)
137 return (1);
138 close_tiff_input_file ();
139 }
140 in_filename = strdup (name);
141 if (! in_filename)
142 {
143 fprintf (stderr, "can't strdup input filename '%s'\n", name);
144 return (0);
145 }
146 in = TIFFOpen (name, "r");
147 if (! in)
148 {
149 fprintf (stderr, "can't open input file '%s'\n", name);
150 free (in_filename);
151 return (0);
152 }
153 return (1);
154 }
157 bool close_pdf_output_files (void)
158 {
159 output_file_t *o, *n;
161 for (o = output_files; o; o = n)
162 {
163 n = o->next;
164 pdf_close (o->pdf);
165 free (o->name);
166 free (o);
167 }
168 out = NULL;
169 output_files = NULL;
170 return (1);
171 }
173 bool open_pdf_output_file (char *name,
174 pdf_file_attributes_t *attributes)
175 {
176 output_file_t *o;
178 if (out && (strcmp (name, out->name) == 0))
179 return (1);
180 for (o = output_files; o; o = o->next)
181 if (strcmp (name, o->name) == 0)
182 {
183 out = o;
184 return (1);
185 }
186 o = calloc (1, sizeof (output_file_t));
187 if (! o)
188 {
189 fprintf (stderr, "can't calloc output file struct for '%s'\n", name);
190 return (0);
191 }
193 o->name = strdup (name);
194 if (! o->name)
195 {
196 fprintf (stderr, "can't strdup output filename '%s'\n", name);
197 free (o);
198 return (0);
199 }
201 o->pdf = pdf_create (name, PDF_PAGE_MODE_USE_OUTLINES);
202 if (! o->pdf)
203 {
204 fprintf (stderr, "can't open output file '%s'\n", name);
205 free (o->name);
206 free (o);
207 return (0);
208 }
210 if (attributes->author)
211 pdf_set_author (o->pdf, attributes->author);
212 if (attributes->creator)
213 pdf_set_creator (o->pdf, attributes->creator);
214 if (attributes->title)
215 pdf_set_title (o->pdf, attributes->title);
216 if (attributes->subject)
217 pdf_set_subject (o->pdf, attributes->subject);
218 if (attributes->keywords)
219 pdf_set_keywords (o->pdf, attributes->keywords);
221 /* prepend new output file onto list */
222 o->next = output_files;
223 output_files = o;
225 out = o;
226 return (1);
227 }
230 void process_page_numbers (int page_index,
231 int count,
232 int base,
233 page_label_t *page_label)
234 {
235 }
238 /* frees original! */
239 static Bitmap *resize_bitmap (Bitmap *src,
240 double x_resolution,
241 double y_resolution,
242 input_attributes_t input_attributes)
243 {
244 Rect src_rect;
245 Point dest_min;
246 Bitmap *dest;
248 int width_pixels = input_attributes.page_size.width * x_resolution;
249 int height_pixels = input_attributes.page_size.height * y_resolution;
251 src_rect.min.x = (rect_width (& src->rect) - width_pixels) / 2;
252 src_rect.min.y = (rect_height (& src->rect) - height_pixels) / 2;
253 src_rect.max.x = src_rect.min.x + width_pixels;
254 src_rect.max.y = src_rect.min.y + height_pixels;
256 dest_min.x = 0;
257 dest_min.y = 0;
259 dest = bitblt (src, & src_rect, NULL, & dest_min, TF_SRC, 0);
260 free_bitmap (src);
261 return (dest);
262 }
265 /* "in place" rotation */
266 static void rotate_bitmap (Bitmap *src,
267 input_attributes_t input_attributes)
268 {
269 switch (input_attributes.rotation)
270 {
271 case 0: break;
272 case 90: rot_90 (src); break;
273 case 180: rot_180 (src); break;
274 case 270: rot_270 (src); break;
275 default:
276 fprintf (stderr, "rotation must be 0, 90, 180, or 270\n");
277 }
278 }
281 #define SWAP(type,a,b) do { type temp; temp = a; a = b; b = temp; } while (0)
284 bool last_tiff_page (void)
285 {
286 return (TIFFLastDirectory (in));
287 }
290 bool process_tiff_page (int image, /* range 1 .. n */
291 input_attributes_t input_attributes,
292 bookmark_t *bookmarks)
293 {
294 int result = 0;
296 uint32_t image_length, image_width;
297 uint32_t dest_image_length, dest_image_width;
298 #ifdef CHECK_DEPTH
299 uint32_t image_depth;
300 #endif
302 uint16_t samples_per_pixel;
303 uint16_t bits_per_sample;
304 uint16_t planar_config;
306 uint16_t resolution_unit;
307 float x_resolution, y_resolution;
308 double dest_x_resolution, dest_y_resolution;
310 double width_points, height_points; /* really 1/72 inch units rather than
311 points */
313 Rect rect;
314 Bitmap *bitmap = NULL;
316 int row;
318 pdf_page_handle page;
320 if (! TIFFSetDirectory (in, image - 1))
321 {
322 fprintf (stderr, "can't find page %d of input file\n", image);
323 goto fail;
324 }
325 if (1 != TIFFGetField (in, TIFFTAG_IMAGELENGTH, & image_length))
326 {
327 fprintf (stderr, "can't get image length\n");
328 goto fail;
329 }
330 if (1 != TIFFGetField (in, TIFFTAG_IMAGEWIDTH, & image_width))
331 {
332 fprintf (stderr, "can't get image width\n");
333 goto fail;
334 }
336 if (1 != TIFFGetField (in, TIFFTAG_SAMPLESPERPIXEL, & samples_per_pixel))
337 {
338 fprintf (stderr, "can't get samples per pixel\n");
339 goto fail;
340 }
342 #ifdef CHECK_DEPTH
343 if (1 != TIFFGetField (in, TIFFTAG_IMAGEDEPTH, & image_depth))
344 {
345 fprintf (stderr, "can't get image depth\n");
346 goto fail;
347 }
348 #endif
350 if (1 != TIFFGetField (in, TIFFTAG_BITSPERSAMPLE, & bits_per_sample))
351 {
352 fprintf (stderr, "can't get bits per sample\n");
353 goto fail;
354 }
356 if (1 != TIFFGetField (in, TIFFTAG_PLANARCONFIG, & planar_config))
357 planar_config = 1;
359 if (1 != TIFFGetField (in, TIFFTAG_RESOLUTIONUNIT, & resolution_unit))
360 resolution_unit = 2;
361 if (1 != TIFFGetField (in, TIFFTAG_XRESOLUTION, & x_resolution))
362 x_resolution = 300;
363 if (1 != TIFFGetField (in, TIFFTAG_YRESOLUTION, & y_resolution))
364 y_resolution = 300;
366 if (samples_per_pixel != 1)
367 {
368 fprintf (stderr, "samples per pixel %u, must be 1\n", samples_per_pixel);
369 goto fail;
370 }
372 #ifdef CHECK_DEPTH
373 if (image_depth != 1)
374 {
375 fprintf (stderr, "image depth %u, must be 1\n", image_depth);
376 goto fail;
377 }
378 #endif
380 if (bits_per_sample != 1)
381 {
382 fprintf (stderr, "bits per sample %u, must be 1\n", bits_per_sample);
383 goto fail;
384 }
386 if (planar_config != 1)
387 {
388 fprintf (stderr, "planar config %u, must be 1\n", planar_config);
389 goto fail;
390 }
392 if (input_attributes.has_resolution)
393 {
394 x_resolution = input_attributes.x_resolution;
395 y_resolution = input_attributes.y_resolution;
396 }
398 if ((input_attributes.rotation == 90) || (input_attributes.rotation == 270))
399 {
400 dest_image_width = image_length;
401 dest_image_length = image_width;
402 dest_x_resolution = y_resolution;
403 dest_y_resolution = x_resolution;
404 SWAP (double, width_points, height_points); /* $$$ not yet set!!! */
405 }
406 else
407 {
408 dest_image_width = image_width;
409 dest_image_length = image_length;
410 dest_x_resolution = x_resolution;
411 dest_y_resolution = y_resolution;
412 }
414 rect.min.x = 0;
415 rect.min.y = 0;
416 rect.max.x = image_width;
417 rect.max.y = image_length;
419 bitmap = create_bitmap (& rect);
421 if (! bitmap)
422 {
423 fprintf (stderr, "can't allocate bitmap\n");
424 goto fail;
425 }
427 for (row = 0; row < image_length; row++)
428 if (1 != TIFFReadScanline (in,
429 bitmap->bits + row * bitmap->row_words,
430 row,
431 0))
432 {
433 fprintf (stderr, "can't read TIFF scanline\n");
434 goto fail;
435 }
437 #ifdef TIFF_REVERSE_BITS
438 reverse_bits ((uint8_t *) bitmap->bits,
439 image_length * bitmap->row_words * sizeof (word_t));
440 #endif /* TIFF_REVERSE_BITS */
442 #if 0
443 if (input_attributes.has_page_size)
444 bitmap = resize_bitmap (bitmap,
445 x_resolution,
446 y_resolution,
447 input_attributes);
448 #endif
450 rotate_bitmap (bitmap,
451 input_attributes);
453 width_points = (rect_width (& bitmap->rect) / dest_x_resolution) * POINTS_PER_INCH;
454 height_points = (rect_height (& bitmap->rect) / dest_y_resolution) * POINTS_PER_INCH;
456 if ((height_points > PAGE_MAX_POINTS) || (width_points > PAGE_MAX_POINTS))
457 {
458 fprintf (stdout, "image too large (max %d inches on a side\n", PAGE_MAX_INCHES);
459 goto fail;
460 }
462 page = pdf_new_page (out->pdf, width_points, height_points);
464 pdf_write_g4_fax_image (page,
465 0, 0, /* x, y */
466 width_points, height_points,
467 bitmap,
468 0, /* ImageMask */
469 0, 0, 0, /* r, g, b */
470 0); /* BlackIs1 */
472 while (bookmarks)
473 {
474 /* $$$ need to handle level here */
475 pdf_new_bookmark (NULL, bookmarks->name, 0, page);
476 bookmarks = bookmarks->next;
477 }
479 result = 1;
481 fail:
482 if (bitmap)
483 free_bitmap (bitmap);
485 return (result);
486 }
489 #if 0
490 bool process_jpeg_page (int image, /* range 1 .. n */
491 input_attributes_t input_attributes,
492 bookmark_t *bookmarks)
493 {
494 int result = 0;
495 FILE *f;
496 pdf_page_handle page;
498 f = fopen (filename, "rb");
499 if (! f)
500 fatal ("error opening input file '%s'\n", filename);
502 page = pdf_new_page (out->pdf, width_points, height_points);
504 pdf_write_jpeg_image (page,
505 0, 0, /* x, y */
506 width_points, height_points,
507 f);
509 return (result);
510 }
511 #endif
514 bool process_page (int image, /* range 1 .. n */
515 input_attributes_t input_attributes,
516 bookmark_t *bookmarks)
517 {
518 int result = 0;
520 result = process_tiff_page (image, input_attributes, bookmarks);
522 return (result);
523 }
526 #define MAX_BOOKMARK_NAME_LEN 500
529 static int filename_length_without_suffix (char *in_fn)
530 {
531 char *p;
532 int len = strlen (in_fn);
534 p = strrchr (in_fn, '.');
535 if (p && ((strcasecmp (p, ".tif") == 0) ||
536 (strcasecmp (p, ".tiff") == 0)))
537 return (p - in_fn);
538 return (len);
539 }
542 /* $$$ this function should ensure that it doesn't overflow the name string! */
543 static void generate_bookmark_name (char *name,
544 char *bookmark_fmt,
545 char *in_fn,
546 int page)
547 {
548 bool meta = 0;
549 int len;
551 while (*bookmark_fmt)
552 {
553 if (meta)
554 {
555 meta = 0;
556 switch (*bookmark_fmt)
557 {
558 case '%':
559 *(name++) = '%';
560 break;
561 case 'F':
562 len = filename_length_without_suffix (in_fn);
563 strncpy (name, in_fn, len);
564 name += len;
565 break;
566 case 'p':
567 sprintf (name, "%d", page);
568 name += strlen (name);
569 break;
570 default:
571 break;
572 }
573 }
574 else
575 switch (*bookmark_fmt)
576 {
577 case '%':
578 meta = 1;
579 break;
580 default:
581 *(name++) = *bookmark_fmt;
582 }
583 bookmark_fmt++;
584 }
585 *bookmark_fmt = '\0';
586 }
589 void main_args (char *out_fn,
590 int inf_count,
591 char **in_fn,
592 char *bookmark_fmt)
593 {
594 int i, ip;
595 input_attributes_t input_attributes;
596 pdf_file_attributes_t output_attributes;
597 bookmark_t bookmark;
598 char bookmark_name [MAX_BOOKMARK_NAME_LEN];
600 bookmark.next = NULL;
601 bookmark.level = 1;
602 bookmark.name = & bookmark_name [0];
604 memset (& input_attributes, 0, sizeof (input_attributes));
605 memset (& output_attributes, 0, sizeof (output_attributes));
607 if (! open_pdf_output_file (out_fn, & output_attributes))
608 fatal (3, "error opening output file \"%s\"\n", out_fn);
609 for (i = 0; i < inf_count; i++)
610 {
611 if (! open_tiff_input_file (in_fn [i]))
612 fatal (3, "error opening input file \"%s\"\n", in_fn [i]);
613 for (ip = 1;; ip++)
614 {
615 fprintf (stderr, "processing page %d of file \"%s\"\r", ip, in_fn [i]);
616 if (bookmark_fmt)
617 generate_bookmark_name (& bookmark_name [0],
618 bookmark_fmt,
619 in_fn [i],
620 ip);
621 if (! process_page (ip, input_attributes,
622 bookmark_fmt ? & bookmark : NULL))
623 fatal (3, "error processing page %d of input file \"%s\"\n", ip, in_fn [i]);
624 if (last_tiff_page ())
625 break;
626 }
627 if (verbose)
628 fprintf (stderr, "processed %d pages of input file \"%s\"\n", ip, in_fn [i]);
629 if (! close_tiff_input_file ())
630 fatal (3, "error closing input file \"%s\"\n", in_fn [i]);
631 }
632 if (! close_pdf_output_files ())
633 fatal (3, "error closing output file \"%s\"\n", out_fn);
634 }
637 void main_spec (char *spec_fn)
638 {
639 if (! parse_spec_file (spec_fn))
640 fatal (2, "error parsing spec file\n");
641 if (! process_specs ())
642 fatal (3, "error processing spec file\n");
643 }
646 int main (int argc, char *argv[])
647 {
648 char *spec_fn = NULL;
649 char *out_fn = NULL;
650 char *bookmark_fmt = NULL;
651 int inf_count = 0;
652 char *in_fn [MAX_INPUT_FILES];
654 progname = argv [0];
656 pdf_init ();
658 while (--argc)
659 {
660 if (argv [1][0] == '-')
661 {
662 if (strcmp (argv [1], "-v") == 0)
663 verbose++;
664 else if (strcmp (argv [1], "-o") == 0)
665 {
666 if (argc)
667 {
668 argc--;
669 argv++;
670 out_fn = argv [1];
671 }
672 else
673 fatal (1, "missing filename after \"-o\" option\n");
674 }
675 else if (strcmp (argv [1], "-s") == 0)
676 {
677 if (argc)
678 {
679 argc--;
680 argv++;
681 spec_fn = argv [1];
682 }
683 else
684 fatal (1, "missing filename after \"-s\" option\n");
685 }
686 else if (strcmp (argv [1], "-b") == 0)
687 {
688 if (argc)
689 {
690 argc--;
691 argv++;
692 bookmark_fmt = argv [1];
693 }
694 else
695 fatal (1, "missing format string after \"-b\" option\n");
696 }
697 else
698 fatal (1, "unrecognized option \"%s\"\n", argv [1]);
699 }
700 else if (inf_count < MAX_INPUT_FILES)
701 in_fn [inf_count++] = argv [1];
702 else
703 fatal (1, "exceeded maximum of %d input files\n", MAX_INPUT_FILES);
704 argv++;
705 }
707 if (! ((! out_fn) ^ (! spec_fn)))
708 fatal (1, "either a spec file or an output file (but not both) must be specified\n");
710 if (out_fn && ! inf_count)
711 fatal (1, "no input files specified\n");
713 if (spec_fn && inf_count)
714 fatal (1, "if spec file is provided, input files can't be specified as arguments\n");
716 if (spec_fn)
717 main_spec (spec_fn);
718 else
719 main_args (out_fn, inf_count, in_fn, bookmark_fmt);
721 close_tiff_input_file ();
722 close_pdf_output_files ();
723 exit (0);
724 }