Thu, 13 Mar 2003 08:03:11 +0000
Acrobat 4.0 fails to optimize PDF files in which the Kids array in the Pages object is an indirect reference.
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.31 2003/03/12 23:59:10 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, (attributes->has_bookmarks ?
202 PDF_PAGE_MODE_USE_OUTLINES :
203 PDF_PAGE_MODE_USE_NONE));
204 if (! o->pdf)
205 {
206 fprintf (stderr, "can't open output file '%s'\n", name);
207 free (o->name);
208 free (o);
209 return (0);
210 }
212 if (attributes->author)
213 pdf_set_author (o->pdf, attributes->author);
214 if (attributes->creator)
215 pdf_set_creator (o->pdf, attributes->creator);
216 if (attributes->title)
217 pdf_set_title (o->pdf, attributes->title);
218 if (attributes->subject)
219 pdf_set_subject (o->pdf, attributes->subject);
220 if (attributes->keywords)
221 pdf_set_keywords (o->pdf, attributes->keywords);
223 /* prepend new output file onto list */
224 o->next = output_files;
225 output_files = o;
227 out = o;
228 return (1);
229 }
232 void process_page_numbers (int page_index,
233 int count,
234 int base,
235 page_label_t *page_label)
236 {
237 }
240 /* frees original! */
241 static Bitmap *resize_bitmap (Bitmap *src,
242 double x_resolution,
243 double y_resolution,
244 input_attributes_t input_attributes)
245 {
246 Rect src_rect;
247 Point dest_min;
248 Bitmap *dest;
250 int width_pixels = input_attributes.page_size.width * x_resolution;
251 int height_pixels = input_attributes.page_size.height * y_resolution;
253 src_rect.min.x = (rect_width (& src->rect) - width_pixels) / 2;
254 src_rect.min.y = (rect_height (& src->rect) - height_pixels) / 2;
255 src_rect.max.x = src_rect.min.x + width_pixels;
256 src_rect.max.y = src_rect.min.y + height_pixels;
258 dest_min.x = 0;
259 dest_min.y = 0;
261 dest = bitblt (src, & src_rect, NULL, & dest_min, TF_SRC, 0);
262 free_bitmap (src);
263 return (dest);
264 }
267 /* "in place" rotation */
268 static void rotate_bitmap (Bitmap *src,
269 input_attributes_t input_attributes)
270 {
271 switch (input_attributes.rotation)
272 {
273 case 0: break;
274 case 90: rot_90 (src); break;
275 case 180: rot_180 (src); break;
276 case 270: rot_270 (src); break;
277 default:
278 fprintf (stderr, "rotation must be 0, 90, 180, or 270\n");
279 }
280 }
283 #define SWAP(type,a,b) do { type temp; temp = a; a = b; b = temp; } while (0)
286 bool last_tiff_page (void)
287 {
288 return (TIFFLastDirectory (in));
289 }
292 bool process_tiff_page (int image, /* range 1 .. n */
293 input_attributes_t input_attributes,
294 bookmark_t *bookmarks)
295 {
296 int result = 0;
298 uint32_t image_length, image_width;
299 uint32_t dest_image_length, dest_image_width;
300 #ifdef CHECK_DEPTH
301 uint32_t image_depth;
302 #endif
304 uint16_t samples_per_pixel;
305 uint16_t bits_per_sample;
306 uint16_t planar_config;
308 uint16_t resolution_unit;
309 float x_resolution, y_resolution;
310 double dest_x_resolution, dest_y_resolution;
312 double width_points, height_points; /* really 1/72 inch units rather than
313 points */
315 Rect rect;
316 Bitmap *bitmap = NULL;
318 int row;
320 pdf_page_handle page;
322 if (! TIFFSetDirectory (in, image - 1))
323 {
324 fprintf (stderr, "can't find page %d of input file\n", image);
325 goto fail;
326 }
327 if (1 != TIFFGetField (in, TIFFTAG_IMAGELENGTH, & image_length))
328 {
329 fprintf (stderr, "can't get image length\n");
330 goto fail;
331 }
332 if (1 != TIFFGetField (in, TIFFTAG_IMAGEWIDTH, & image_width))
333 {
334 fprintf (stderr, "can't get image width\n");
335 goto fail;
336 }
338 if (1 != TIFFGetField (in, TIFFTAG_SAMPLESPERPIXEL, & samples_per_pixel))
339 {
340 fprintf (stderr, "can't get samples per pixel\n");
341 goto fail;
342 }
344 #ifdef CHECK_DEPTH
345 if (1 != TIFFGetField (in, TIFFTAG_IMAGEDEPTH, & image_depth))
346 {
347 fprintf (stderr, "can't get image depth\n");
348 goto fail;
349 }
350 #endif
352 if (1 != TIFFGetField (in, TIFFTAG_BITSPERSAMPLE, & bits_per_sample))
353 {
354 fprintf (stderr, "can't get bits per sample\n");
355 goto fail;
356 }
358 if (1 != TIFFGetField (in, TIFFTAG_PLANARCONFIG, & planar_config))
359 planar_config = 1;
361 if (1 != TIFFGetField (in, TIFFTAG_RESOLUTIONUNIT, & resolution_unit))
362 resolution_unit = 2;
363 if (1 != TIFFGetField (in, TIFFTAG_XRESOLUTION, & x_resolution))
364 x_resolution = 300;
365 if (1 != TIFFGetField (in, TIFFTAG_YRESOLUTION, & y_resolution))
366 y_resolution = 300;
368 if (samples_per_pixel != 1)
369 {
370 fprintf (stderr, "samples per pixel %u, must be 1\n", samples_per_pixel);
371 goto fail;
372 }
374 #ifdef CHECK_DEPTH
375 if (image_depth != 1)
376 {
377 fprintf (stderr, "image depth %u, must be 1\n", image_depth);
378 goto fail;
379 }
380 #endif
382 if (bits_per_sample != 1)
383 {
384 fprintf (stderr, "bits per sample %u, must be 1\n", bits_per_sample);
385 goto fail;
386 }
388 if (planar_config != 1)
389 {
390 fprintf (stderr, "planar config %u, must be 1\n", planar_config);
391 goto fail;
392 }
394 if (input_attributes.has_resolution)
395 {
396 x_resolution = input_attributes.x_resolution;
397 y_resolution = input_attributes.y_resolution;
398 }
400 if ((input_attributes.rotation == 90) || (input_attributes.rotation == 270))
401 {
402 dest_image_width = image_length;
403 dest_image_length = image_width;
404 dest_x_resolution = y_resolution;
405 dest_y_resolution = x_resolution;
406 SWAP (double, width_points, height_points); /* $$$ not yet set!!! */
407 }
408 else
409 {
410 dest_image_width = image_width;
411 dest_image_length = image_length;
412 dest_x_resolution = x_resolution;
413 dest_y_resolution = y_resolution;
414 }
416 rect.min.x = 0;
417 rect.min.y = 0;
418 rect.max.x = image_width;
419 rect.max.y = image_length;
421 bitmap = create_bitmap (& rect);
423 if (! bitmap)
424 {
425 fprintf (stderr, "can't allocate bitmap\n");
426 goto fail;
427 }
429 for (row = 0; row < image_length; row++)
430 if (1 != TIFFReadScanline (in,
431 bitmap->bits + row * bitmap->row_words,
432 row,
433 0))
434 {
435 fprintf (stderr, "can't read TIFF scanline\n");
436 goto fail;
437 }
439 #ifdef TIFF_REVERSE_BITS
440 reverse_bits ((uint8_t *) bitmap->bits,
441 image_length * bitmap->row_words * sizeof (word_t));
442 #endif /* TIFF_REVERSE_BITS */
444 #if 0
445 if (input_attributes.has_page_size)
446 bitmap = resize_bitmap (bitmap,
447 x_resolution,
448 y_resolution,
449 input_attributes);
450 #endif
452 rotate_bitmap (bitmap,
453 input_attributes);
455 width_points = (rect_width (& bitmap->rect) / dest_x_resolution) * POINTS_PER_INCH;
456 height_points = (rect_height (& bitmap->rect) / dest_y_resolution) * POINTS_PER_INCH;
458 if ((height_points > PAGE_MAX_POINTS) || (width_points > PAGE_MAX_POINTS))
459 {
460 fprintf (stdout, "image too large (max %d inches on a side\n", PAGE_MAX_INCHES);
461 goto fail;
462 }
464 page = pdf_new_page (out->pdf, width_points, height_points);
466 #if 0
467 pdf_write_text (page);
468 #else
469 pdf_write_g4_fax_image (page,
470 0, 0, /* x, y */
471 width_points, height_points,
472 bitmap,
473 0, /* ImageMask */
474 0, 0, 0, /* r, g, b */
475 0); /* BlackIs1 */
476 #endif
478 while (bookmarks)
479 {
480 /* $$$ need to handle level here */
481 pdf_new_bookmark (NULL, bookmarks->name, 0, page);
482 bookmarks = bookmarks->next;
483 }
485 result = 1;
487 fail:
488 if (bitmap)
489 free_bitmap (bitmap);
491 return (result);
492 }
495 #if 0
496 bool process_jpeg_page (int image, /* range 1 .. n */
497 input_attributes_t input_attributes,
498 bookmark_t *bookmarks)
499 {
500 int result = 0;
501 FILE *f;
502 pdf_page_handle page;
504 f = fopen (filename, "rb");
505 if (! f)
506 fatal ("error opening input file '%s'\n", filename);
508 page = pdf_new_page (out->pdf, width_points, height_points);
510 pdf_write_jpeg_image (page,
511 0, 0, /* x, y */
512 width_points, height_points,
513 f);
515 return (result);
516 }
517 #endif
520 bool process_page (int image, /* range 1 .. n */
521 input_attributes_t input_attributes,
522 bookmark_t *bookmarks)
523 {
524 int result = 0;
526 result = process_tiff_page (image, input_attributes, bookmarks);
528 return (result);
529 }
532 #define MAX_BOOKMARK_NAME_LEN 500
535 static int filename_length_without_suffix (char *in_fn)
536 {
537 char *p;
538 int len = strlen (in_fn);
540 p = strrchr (in_fn, '.');
541 if (p && ((strcasecmp (p, ".tif") == 0) ||
542 (strcasecmp (p, ".tiff") == 0)))
543 return (p - in_fn);
544 return (len);
545 }
548 /* $$$ this function should ensure that it doesn't overflow the name string! */
549 static void generate_bookmark_name (char *name,
550 char *bookmark_fmt,
551 char *in_fn,
552 int page)
553 {
554 bool meta = 0;
555 int len;
557 while (*bookmark_fmt)
558 {
559 if (meta)
560 {
561 meta = 0;
562 switch (*bookmark_fmt)
563 {
564 case '%':
565 *(name++) = '%';
566 break;
567 case 'F':
568 len = filename_length_without_suffix (in_fn);
569 strncpy (name, in_fn, len);
570 name += len;
571 break;
572 case 'p':
573 sprintf (name, "%d", page);
574 name += strlen (name);
575 break;
576 default:
577 break;
578 }
579 }
580 else
581 switch (*bookmark_fmt)
582 {
583 case '%':
584 meta = 1;
585 break;
586 default:
587 *(name++) = *bookmark_fmt;
588 }
589 bookmark_fmt++;
590 }
591 *name = '\0';
592 }
595 void main_args (char *out_fn,
596 int inf_count,
597 char **in_fn,
598 char *bookmark_fmt)
599 {
600 int i, ip;
601 input_attributes_t input_attributes;
602 pdf_file_attributes_t output_attributes;
603 bookmark_t bookmark;
604 char bookmark_name [MAX_BOOKMARK_NAME_LEN];
606 bookmark.next = NULL;
607 bookmark.level = 1;
608 bookmark.name = & bookmark_name [0];
610 memset (& input_attributes, 0, sizeof (input_attributes));
611 memset (& output_attributes, 0, sizeof (output_attributes));
613 output_attributes.has_bookmarks = (bookmark_fmt != NULL);
615 if (! open_pdf_output_file (out_fn, & output_attributes))
616 fatal (3, "error opening output file \"%s\"\n", out_fn);
617 for (i = 0; i < inf_count; i++)
618 {
619 if (! open_tiff_input_file (in_fn [i]))
620 fatal (3, "error opening input file \"%s\"\n", in_fn [i]);
621 for (ip = 1;; ip++)
622 {
623 fprintf (stderr, "processing page %d of file \"%s\"\r", ip, in_fn [i]);
624 if (bookmark_fmt)
625 generate_bookmark_name (& bookmark_name [0],
626 bookmark_fmt,
627 in_fn [i],
628 ip);
629 if (! process_page (ip, input_attributes,
630 bookmark_fmt ? & bookmark : NULL))
631 fatal (3, "error processing page %d of input file \"%s\"\n", ip, in_fn [i]);
632 if (last_tiff_page ())
633 break;
634 }
635 if (verbose)
636 fprintf (stderr, "processed %d pages of input file \"%s\"\n", ip, in_fn [i]);
637 if (! close_tiff_input_file ())
638 fatal (3, "error closing input file \"%s\"\n", in_fn [i]);
639 }
640 if (! close_pdf_output_files ())
641 fatal (3, "error closing output file \"%s\"\n", out_fn);
642 }
645 void main_spec (char *spec_fn)
646 {
647 if (! parse_spec_file (spec_fn))
648 fatal (2, "error parsing spec file\n");
649 if (! process_specs ())
650 fatal (3, "error processing spec file\n");
651 }
654 int main (int argc, char *argv[])
655 {
656 char *spec_fn = NULL;
657 char *out_fn = NULL;
658 char *bookmark_fmt = NULL;
659 int inf_count = 0;
660 char *in_fn [MAX_INPUT_FILES];
662 progname = argv [0];
664 pdf_init ();
666 while (--argc)
667 {
668 if (argv [1][0] == '-')
669 {
670 if (strcmp (argv [1], "-v") == 0)
671 verbose++;
672 else if (strcmp (argv [1], "-o") == 0)
673 {
674 if (argc)
675 {
676 argc--;
677 argv++;
678 out_fn = argv [1];
679 }
680 else
681 fatal (1, "missing filename after \"-o\" option\n");
682 }
683 else if (strcmp (argv [1], "-s") == 0)
684 {
685 if (argc)
686 {
687 argc--;
688 argv++;
689 spec_fn = argv [1];
690 }
691 else
692 fatal (1, "missing filename after \"-s\" option\n");
693 }
694 else if (strcmp (argv [1], "-b") == 0)
695 {
696 if (argc)
697 {
698 argc--;
699 argv++;
700 bookmark_fmt = argv [1];
701 }
702 else
703 fatal (1, "missing format string after \"-b\" option\n");
704 }
705 else
706 fatal (1, "unrecognized option \"%s\"\n", argv [1]);
707 }
708 else if (inf_count < MAX_INPUT_FILES)
709 in_fn [inf_count++] = argv [1];
710 else
711 fatal (1, "exceeded maximum of %d input files\n", MAX_INPUT_FILES);
712 argv++;
713 }
715 if (! ((! out_fn) ^ (! spec_fn)))
716 fatal (1, "either a spec file or an output file (but not both) must be specified\n");
718 if (out_fn && ! inf_count)
719 fatal (1, "no input files specified\n");
721 if (spec_fn && inf_count)
722 fatal (1, "if spec file is provided, input files can't be specified as arguments\n");
724 if (spec_fn)
725 main_spec (spec_fn);
726 else
727 main_args (out_fn, inf_count, in_fn, bookmark_fmt);
729 close_tiff_input_file ();
730 close_pdf_output_files ();
731 exit (0);
732 }