Wed, 19 Mar 2003 15:39:55 +0000
more JPEG support.
1 /*
2 * tumble: build a PDF file from image files
3 *
4 * Main program
5 * $Id: tumble.c,v 1.37 2003/03/19 07:39:55 eric Exp $
6 * Copyright 2001, 2002, 2003 Eric Smith <eric@brouhaha.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation. Note that permission is
11 * not granted to redistribute this program under the terms of any
12 * other version of the General Public License.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
22 */
25 #include <stdarg.h>
26 #include <stdbool.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
33 #include <tiffio.h>
34 #define TIFF_REVERSE_BITS
36 #include "bitblt.h"
37 #include "semantics.h"
38 #include "parser.tab.h"
39 #include "tumble.h"
40 #include "pdf.h"
43 #define MAX_INPUT_FILES 5000
45 #define POINTS_PER_INCH 72
47 /* page size limited by Acrobat Reader to 45 inches on a side */
48 #define PAGE_MAX_INCHES 45
49 #define PAGE_MAX_POINTS (PAGE_MAX_INCHES * POINTS_PER_INCH)
52 typedef struct output_file_t
53 {
54 struct output_file_t *next;
55 char *name;
56 pdf_file_handle pdf;
57 } output_file_t;
60 int verbose;
63 typedef enum
64 {
65 INPUT_FILE_TYPE_NONE,
66 INPUT_FILE_TYPE_TIFF,
67 INPUT_FILE_TYPE_JPEG
68 } input_file_type_t;
71 char *in_filename;
72 input_file_type_t in_type;
73 FILE *in;
74 TIFF *tiff_in;
75 output_file_t *output_files;
76 output_file_t *out;
79 char *progname;
82 bool close_tiff_input_file (void);
83 bool close_pdf_output_files (void);
86 void usage (void)
87 {
88 fprintf (stderr, "\n");
89 fprintf (stderr, "tumble - Copyright 2001-2003 Eric Smith <eric@brouhaha.com>\n");
90 fprintf (stderr, "http://tumble.brouhaha.com/\n");
91 fprintf (stderr, "\n");
92 fprintf (stderr, "usage:\n");
93 fprintf (stderr, " %s [options] -c <control.tum>\n", progname);
94 fprintf (stderr, " %s [options] <input.tif>... -o <output.pdf>\n", progname);
95 fprintf (stderr, "options:\n");
96 fprintf (stderr, " -v verbose\n");
97 fprintf (stderr, " -b <fmt> create bookmarks\n");
98 fprintf (stderr, "bookmark format:\n");
99 fprintf (stderr, " %%F file name (sans suffix)\n");
100 fprintf (stderr, " %%p page number\n");
101 }
104 /* generate fatal error message to stderr, doesn't return */
105 void fatal (int ret, char *format, ...) __attribute__ ((noreturn));
107 void fatal (int ret, char *format, ...)
108 {
109 va_list ap;
111 fprintf (stderr, "fatal error");
112 if (format)
113 {
114 fprintf (stderr, ": ");
115 va_start (ap, format);
116 vfprintf (stderr, format, ap);
117 va_end (ap);
118 }
119 else
120 fprintf (stderr, "\n");
121 if (ret == 1)
122 usage ();
123 close_input_file ();
124 close_pdf_output_files ();
125 exit (ret);
126 }
129 bool close_tiff_input_file (void)
130 {
131 TIFFClose (tiff_in);
132 return (1);
133 }
136 bool open_tiff_input_file (FILE *f, char *name)
137 {
138 tiff_in = TIFFFdOpen (fileno (f), name, "r");
139 if (! tiff_in)
140 {
141 fprintf (stderr, "can't open input file '%s'\n", name);
142 free (in_filename);
143 return (0);
144 }
145 in_type = INPUT_FILE_TYPE_TIFF;
146 return (1);
147 }
150 bool close_jpeg_input_file (void)
151 {
152 return (1);
153 }
156 bool open_jpeg_input_file (FILE *f, char *name)
157 {
158 in_type = INPUT_FILE_TYPE_JPEG;
159 return (1);
160 }
163 bool open_input_file (char *name)
164 {
165 bool result;
166 uint8_t buf [2];
167 size_t l;
169 if (in)
170 {
171 if (strcmp (name, in_filename) == 0)
172 return (1);
173 close_input_file ();
174 }
175 in_filename = strdup (name);
176 if (! in_filename)
177 {
178 fprintf (stderr, "can't strdup input filename '%s'\n", name);
179 return (0);
180 }
182 in = fopen (name, "rb");
183 if (! in)
184 return (0);
186 l = fread (& buf [0], 1, sizeof (buf), in);
187 if (l != sizeof (buf))
188 return (0);
190 rewind (in);
192 if ((buf [0] == 0x49) && (buf [1] == 0x49))
193 result = open_tiff_input_file (in, name);
194 else if ((buf [0] == 0xff) && (buf [1] == 0xd8))
195 result = open_jpeg_input_file (in, name);
196 else
197 {
198 fprintf (stderr, "unrecognized file header in file '%s'\n", name);
199 result = 0;
200 }
201 if (! result)
202 {
203 if (in)
204 fclose (in);
205 in = NULL;
206 in_type = INPUT_FILE_TYPE_NONE;
207 }
208 return (result);
209 }
212 bool close_input_file (void)
213 {
214 bool result;
216 switch (in_type)
217 {
218 case INPUT_FILE_TYPE_NONE:
219 return (1);
220 case INPUT_FILE_TYPE_TIFF:
221 result = close_tiff_input_file ();
222 break;
223 case INPUT_FILE_TYPE_JPEG:
224 result = close_jpeg_input_file ();
225 break;
226 default:
227 fatal (3, "internal error: bad input file type\n");
228 }
230 if (in_filename)
231 free (in_filename);
232 fclose (in);
233 in = NULL;
235 return (result);
236 }
239 bool last_tiff_input_page (void)
240 {
241 return (TIFFLastDirectory (tiff_in));
242 }
245 bool last_jpeg_input_page (void)
246 {
247 return (1);
248 }
251 bool last_input_page (void)
252 {
253 switch (in_type)
254 {
255 case INPUT_FILE_TYPE_TIFF:
256 return (last_tiff_input_page ());
257 case INPUT_FILE_TYPE_JPEG:
258 return (last_jpeg_input_page ());
259 default:
260 fatal (3, "internal error: bad input file type\n");
261 }
262 }
265 bool close_pdf_output_files (void)
266 {
267 output_file_t *o, *n;
269 for (o = output_files; o; o = n)
270 {
271 n = o->next;
272 pdf_close (o->pdf, PDF_PAGE_MODE_USE_OUTLINES);
273 free (o->name);
274 free (o);
275 }
276 out = NULL;
277 output_files = NULL;
278 return (1);
279 }
281 bool open_pdf_output_file (char *name,
282 pdf_file_attributes_t *attributes)
283 {
284 output_file_t *o;
286 if (out && (strcmp (name, out->name) == 0))
287 return (1);
288 for (o = output_files; o; o = o->next)
289 if (strcmp (name, o->name) == 0)
290 {
291 out = o;
292 return (1);
293 }
294 o = calloc (1, sizeof (output_file_t));
295 if (! o)
296 {
297 fprintf (stderr, "can't calloc output file struct for '%s'\n", name);
298 return (0);
299 }
301 o->name = strdup (name);
302 if (! o->name)
303 {
304 fprintf (stderr, "can't strdup output filename '%s'\n", name);
305 free (o);
306 return (0);
307 }
309 o->pdf = pdf_create (name);
310 if (! o->pdf)
311 {
312 fprintf (stderr, "can't open output file '%s'\n", name);
313 free (o->name);
314 free (o);
315 return (0);
316 }
318 if (attributes->author)
319 pdf_set_author (o->pdf, attributes->author);
320 if (attributes->creator)
321 pdf_set_creator (o->pdf, attributes->creator);
322 if (attributes->title)
323 pdf_set_title (o->pdf, attributes->title);
324 if (attributes->subject)
325 pdf_set_subject (o->pdf, attributes->subject);
326 if (attributes->keywords)
327 pdf_set_keywords (o->pdf, attributes->keywords);
329 /* prepend new output file onto list */
330 o->next = output_files;
331 output_files = o;
333 out = o;
334 return (1);
335 }
338 /* frees original! */
339 static Bitmap *resize_bitmap (Bitmap *src,
340 double x_resolution,
341 double y_resolution,
342 input_attributes_t input_attributes)
343 {
344 Rect src_rect;
345 Point dest_min;
346 Bitmap *dest;
348 int width_pixels = input_attributes.page_size.width * x_resolution;
349 int height_pixels = input_attributes.page_size.height * y_resolution;
351 src_rect.min.x = (rect_width (& src->rect) - width_pixels) / 2;
352 src_rect.min.y = (rect_height (& src->rect) - height_pixels) / 2;
353 src_rect.max.x = src_rect.min.x + width_pixels;
354 src_rect.max.y = src_rect.min.y + height_pixels;
356 dest_min.x = 0;
357 dest_min.y = 0;
359 dest = bitblt (src, & src_rect, NULL, & dest_min, TF_SRC, 0);
360 free_bitmap (src);
361 return (dest);
362 }
365 /* "in place" rotation */
366 static void rotate_bitmap (Bitmap *src,
367 input_attributes_t input_attributes)
368 {
369 switch (input_attributes.rotation)
370 {
371 case 0: break;
372 case 90: rot_90 (src); break;
373 case 180: rot_180 (src); break;
374 case 270: rot_270 (src); break;
375 default:
376 fprintf (stderr, "rotation must be 0, 90, 180, or 270\n");
377 }
378 }
381 #define SWAP(type,a,b) do { type temp; temp = a; a = b; b = temp; } while (0)
384 static pdf_page_handle process_tiff_page (int image, /* range 1 .. n */
385 input_attributes_t input_attributes)
386 {
387 uint32_t image_length, image_width;
388 uint32_t dest_image_length, dest_image_width;
389 #ifdef CHECK_DEPTH
390 uint32_t image_depth;
391 #endif
393 uint16_t samples_per_pixel;
394 uint16_t bits_per_sample;
395 uint16_t planar_config;
397 uint16_t resolution_unit;
398 float x_resolution, y_resolution;
399 double dest_x_resolution, dest_y_resolution;
401 double width_points, height_points; /* really 1/72 inch units rather than
402 points */
404 Rect rect;
405 Bitmap *bitmap = NULL;
407 int row;
409 pdf_page_handle page = NULL;
411 if (! TIFFSetDirectory (tiff_in, image - 1))
412 {
413 fprintf (stderr, "can't find page %d of input file\n", image);
414 goto fail;
415 }
416 if (1 != TIFFGetField (tiff_in, TIFFTAG_IMAGELENGTH, & image_length))
417 {
418 fprintf (stderr, "can't get image length\n");
419 goto fail;
420 }
421 if (1 != TIFFGetField (tiff_in, TIFFTAG_IMAGEWIDTH, & image_width))
422 {
423 fprintf (stderr, "can't get image width\n");
424 goto fail;
425 }
427 if (1 != TIFFGetField (tiff_in, TIFFTAG_SAMPLESPERPIXEL, & samples_per_pixel))
428 {
429 fprintf (stderr, "can't get samples per pixel\n");
430 goto fail;
431 }
433 #ifdef CHECK_DEPTH
434 if (1 != TIFFGetField (tiff_in, TIFFTAG_IMAGEDEPTH, & image_depth))
435 {
436 fprintf (stderr, "can't get image depth\n");
437 goto fail;
438 }
439 #endif
441 if (1 != TIFFGetField (tiff_in, TIFFTAG_BITSPERSAMPLE, & bits_per_sample))
442 {
443 fprintf (stderr, "can't get bits per sample\n");
444 goto fail;
445 }
447 if (1 != TIFFGetField (tiff_in, TIFFTAG_PLANARCONFIG, & planar_config))
448 planar_config = 1;
450 if (1 != TIFFGetField (tiff_in, TIFFTAG_RESOLUTIONUNIT, & resolution_unit))
451 resolution_unit = 2;
452 if (1 != TIFFGetField (tiff_in, TIFFTAG_XRESOLUTION, & x_resolution))
453 x_resolution = 300;
454 if (1 != TIFFGetField (tiff_in, TIFFTAG_YRESOLUTION, & y_resolution))
455 y_resolution = 300;
457 if (samples_per_pixel != 1)
458 {
459 fprintf (stderr, "samples per pixel %u, must be 1\n", samples_per_pixel);
460 goto fail;
461 }
463 #ifdef CHECK_DEPTH
464 if (image_depth != 1)
465 {
466 fprintf (stderr, "image depth %u, must be 1\n", image_depth);
467 goto fail;
468 }
469 #endif
471 if (bits_per_sample != 1)
472 {
473 fprintf (stderr, "bits per sample %u, must be 1\n", bits_per_sample);
474 goto fail;
475 }
477 if (planar_config != 1)
478 {
479 fprintf (stderr, "planar config %u, must be 1\n", planar_config);
480 goto fail;
481 }
483 if (input_attributes.has_resolution)
484 {
485 x_resolution = input_attributes.x_resolution;
486 y_resolution = input_attributes.y_resolution;
487 }
489 if ((input_attributes.rotation == 90) || (input_attributes.rotation == 270))
490 {
491 dest_image_width = image_length;
492 dest_image_length = image_width;
493 dest_x_resolution = y_resolution;
494 dest_y_resolution = x_resolution;
495 SWAP (double, width_points, height_points); /* $$$ not yet set!!! */
496 }
497 else
498 {
499 dest_image_width = image_width;
500 dest_image_length = image_length;
501 dest_x_resolution = x_resolution;
502 dest_y_resolution = y_resolution;
503 }
505 rect.min.x = 0;
506 rect.min.y = 0;
507 rect.max.x = image_width;
508 rect.max.y = image_length;
510 bitmap = create_bitmap (& rect);
512 if (! bitmap)
513 {
514 fprintf (stderr, "can't allocate bitmap\n");
515 goto fail;
516 }
518 for (row = 0; row < image_length; row++)
519 if (1 != TIFFReadScanline (tiff_in,
520 bitmap->bits + row * bitmap->row_words,
521 row,
522 0))
523 {
524 fprintf (stderr, "can't read TIFF scanline\n");
525 goto fail;
526 }
528 #ifdef TIFF_REVERSE_BITS
529 reverse_bits ((uint8_t *) bitmap->bits,
530 image_length * bitmap->row_words * sizeof (word_t));
531 #endif /* TIFF_REVERSE_BITS */
533 #if 0
534 if (input_attributes.has_page_size)
535 bitmap = resize_bitmap (bitmap,
536 x_resolution,
537 y_resolution,
538 input_attributes);
539 #endif
541 rotate_bitmap (bitmap,
542 input_attributes);
544 width_points = (rect_width (& bitmap->rect) / dest_x_resolution) * POINTS_PER_INCH;
545 height_points = (rect_height (& bitmap->rect) / dest_y_resolution) * POINTS_PER_INCH;
547 if ((height_points > PAGE_MAX_POINTS) || (width_points > PAGE_MAX_POINTS))
548 {
549 fprintf (stdout, "image too large (max %d inches on a side\n", PAGE_MAX_INCHES);
550 goto fail;
551 }
553 page = pdf_new_page (out->pdf, width_points, height_points);
555 #if 0
556 pdf_write_text (page);
557 #else
558 pdf_write_g4_fax_image (page,
559 0, 0, /* x, y */
560 width_points, height_points,
561 bitmap,
562 0, /* ImageMask */
563 0, 0, 0, /* r, g, b */
564 0); /* BlackIs1 */
565 #endif
567 if (bitmap)
568 free_bitmap (bitmap);
569 return (page);
571 fail:
572 if (bitmap)
573 free_bitmap (bitmap);
575 return (NULL);
576 }
579 pdf_page_handle process_jpeg_page (int image, /* range 1 .. n */
580 input_attributes_t input_attributes)
581 {
582 pdf_page_handle page;
583 double width_points, height_points; /* really 1/72 inch units rather than
584 points */
586 /* $$$ need to get these from somewhere else, hardcoded for now */
587 width_points = 4 * 72.0;
588 height_points = 4 * 72.0;
590 page = pdf_new_page (out->pdf, width_points, height_points);
592 pdf_write_jpeg_image (page,
593 0, 0, /* x, y */
594 width_points, height_points,
595 in);
597 return (page);
598 }
601 bool process_page (int image, /* range 1 .. n */
602 input_attributes_t input_attributes,
603 bookmark_t *bookmarks,
604 page_label_t *page_label)
605 {
606 pdf_page_handle page;
608 switch (in_type)
609 {
610 case INPUT_FILE_TYPE_TIFF:
611 page = process_tiff_page (image, input_attributes);
612 break;
613 case INPUT_FILE_TYPE_JPEG:
614 page = process_jpeg_page (image, input_attributes);
615 break;
616 default:
617 fatal (3, "internal error: bad input file type\n");
618 }
620 while (bookmarks)
621 {
622 /* $$$ need to handle level here */
623 pdf_new_bookmark (NULL, bookmarks->name, 0, page);
624 bookmarks = bookmarks->next;
625 }
627 if (page_label)
628 pdf_new_page_label (out->pdf,
629 page_label->page_index,
630 page_label->base,
631 page_label->count,
632 page_label->style,
633 page_label->prefix);
635 return (page != NULL);
636 }
639 #define MAX_BOOKMARK_NAME_LEN 500
642 static int filename_length_without_suffix (char *in_fn)
643 {
644 char *p;
645 int len = strlen (in_fn);
647 p = strrchr (in_fn, '.');
648 if (p && ((strcasecmp (p, ".tif") == 0) ||
649 (strcasecmp (p, ".tiff") == 0)))
650 return (p - in_fn);
651 return (len);
652 }
655 /* $$$ this function should ensure that it doesn't overflow the name string! */
656 static void generate_bookmark_name (char *name,
657 char *bookmark_fmt,
658 char *in_fn,
659 int page)
660 {
661 bool meta = 0;
662 int len;
664 while (*bookmark_fmt)
665 {
666 if (meta)
667 {
668 meta = 0;
669 switch (*bookmark_fmt)
670 {
671 case '%':
672 *(name++) = '%';
673 break;
674 case 'F':
675 len = filename_length_without_suffix (in_fn);
676 strncpy (name, in_fn, len);
677 name += len;
678 break;
679 case 'p':
680 sprintf (name, "%d", page);
681 name += strlen (name);
682 break;
683 default:
684 break;
685 }
686 }
687 else
688 switch (*bookmark_fmt)
689 {
690 case '%':
691 meta = 1;
692 break;
693 default:
694 *(name++) = *bookmark_fmt;
695 }
696 bookmark_fmt++;
697 }
698 *name = '\0';
699 }
702 void main_args (char *out_fn,
703 int inf_count,
704 char **in_fn,
705 char *bookmark_fmt)
706 {
707 int i, ip;
708 input_attributes_t input_attributes;
709 pdf_file_attributes_t output_attributes;
710 bookmark_t bookmark;
711 char bookmark_name [MAX_BOOKMARK_NAME_LEN];
713 bookmark.next = NULL;
714 bookmark.level = 1;
715 bookmark.name = & bookmark_name [0];
717 memset (& input_attributes, 0, sizeof (input_attributes));
718 memset (& output_attributes, 0, sizeof (output_attributes));
720 if (! open_pdf_output_file (out_fn, & output_attributes))
721 fatal (3, "error opening output file \"%s\"\n", out_fn);
722 for (i = 0; i < inf_count; i++)
723 {
724 if (! open_input_file (in_fn [i]))
725 fatal (3, "error opening input file \"%s\"\n", in_fn [i]);
726 for (ip = 1;; ip++)
727 {
728 fprintf (stderr, "processing page %d of file \"%s\"\r", ip, in_fn [i]);
729 if (bookmark_fmt)
730 generate_bookmark_name (& bookmark_name [0],
731 bookmark_fmt,
732 in_fn [i],
733 ip);
734 if (! process_page (ip, input_attributes,
735 bookmark_fmt ? & bookmark : NULL,
736 NULL))
737 fatal (3, "error processing page %d of input file \"%s\"\n", ip, in_fn [i]);
738 if (last_input_page ())
739 break;
740 }
741 if (verbose)
742 fprintf (stderr, "processed %d pages of input file \"%s\"\n", ip, in_fn [i]);
743 if (! close_input_file ())
744 fatal (3, "error closing input file \"%s\"\n", in_fn [i]);
745 }
746 if (! close_pdf_output_files ())
747 fatal (3, "error closing output file \"%s\"\n", out_fn);
748 }
751 void main_control (char *control_fn)
752 {
753 if (! parse_control_file (control_fn))
754 fatal (2, "error parsing control file\n");
755 if (! process_controls ())
756 fatal (3, "error processing control file\n");
757 }
760 int main (int argc, char *argv[])
761 {
762 char *control_fn = NULL;
763 char *out_fn = NULL;
764 char *bookmark_fmt = NULL;
765 int inf_count = 0;
766 char *in_fn [MAX_INPUT_FILES];
768 progname = argv [0];
770 pdf_init ();
772 while (--argc)
773 {
774 if (argv [1][0] == '-')
775 {
776 if (strcmp (argv [1], "-v") == 0)
777 verbose++;
778 else if (strcmp (argv [1], "-o") == 0)
779 {
780 if (argc)
781 {
782 argc--;
783 argv++;
784 out_fn = argv [1];
785 }
786 else
787 fatal (1, "missing filename after \"-o\" option\n");
788 }
789 else if (strcmp (argv [1], "-c") == 0)
790 {
791 if (argc)
792 {
793 argc--;
794 argv++;
795 control_fn = argv [1];
796 }
797 else
798 fatal (1, "missing filename after \"-s\" option\n");
799 }
800 else if (strcmp (argv [1], "-b") == 0)
801 {
802 if (argc)
803 {
804 argc--;
805 argv++;
806 bookmark_fmt = argv [1];
807 }
808 else
809 fatal (1, "missing format string after \"-b\" option\n");
810 }
811 else
812 fatal (1, "unrecognized option \"%s\"\n", argv [1]);
813 }
814 else if (inf_count < MAX_INPUT_FILES)
815 in_fn [inf_count++] = argv [1];
816 else
817 fatal (1, "exceeded maximum of %d input files\n", MAX_INPUT_FILES);
818 argv++;
819 }
821 if (! ((! out_fn) ^ (! control_fn)))
822 fatal (1, "either a control file or an output file (but not both) must be specified\n");
824 if (out_fn && ! inf_count)
825 fatal (1, "no input files specified\n");
827 if (control_fn && inf_count)
828 fatal (1, "if control file is provided, input files can't be specified as arguments\n");
830 if (control_fn)
831 main_control (control_fn);
832 else
833 main_args (out_fn, inf_count, in_fn, bookmark_fmt);
835 close_input_file ();
836 close_pdf_output_files ();
837 exit (0);
838 }