tumble.c

Wed, 19 Mar 2003 15:39:55 +0000

author
eric
date
Wed, 19 Mar 2003 15:39:55 +0000
changeset 139
ec2a06d8a2a6
parent 134
313aba417199
child 141
752599b50ff3
permissions
-rw-r--r--

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 }