Thu, 10 Apr 2003 09:02:12 +0000
include tumble version in usage message.
1 /*
2 * tumble: build a PDF file from image files
3 *
4 * Main program
5 * $Id: tumble.c,v 1.43 2003/04/10 01:02:12 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>
34 #include "semantics.h"
35 #include "parser.tab.h"
36 #include "tumble.h"
37 #include "bitblt.h"
38 #include "pdf.h"
39 #include "tumble_input.h"
42 #define MAX_INPUT_FILES 5000
44 typedef struct output_file_t
45 {
46 struct output_file_t *next;
47 char *name;
48 pdf_file_handle pdf;
49 } output_file_t;
52 int verbose;
55 output_file_t *output_files;
56 output_file_t *out;
59 char *progname;
62 bool close_pdf_output_files (void);
65 #define QMAKESTR(x) #x
66 #define MAKESTR(x) QMAKESTR(x)
69 void usage (void)
70 {
71 fprintf (stderr, "\n");
72 fprintf (stderr, "tumble version " MAKESTR(TUMBLE_VERSION) " - Copyright 2001-2003 Eric Smith <eric@brouhaha.com>\n");
73 fprintf (stderr, "http://tumble.brouhaha.com/\n");
74 fprintf (stderr, "\n");
75 fprintf (stderr, "usage:\n");
76 fprintf (stderr, " %s [options] -c <control.tum>\n", progname);
77 fprintf (stderr, " %s [options] <input.tif>... -o <output.pdf>\n", progname);
78 fprintf (stderr, "options:\n");
79 fprintf (stderr, " -v verbose\n");
80 fprintf (stderr, " -b <fmt> create bookmarks\n");
81 fprintf (stderr, "bookmark format:\n");
82 fprintf (stderr, " %%F file name (sans suffix)\n");
83 fprintf (stderr, " %%p page number\n");
84 }
87 /* generate fatal error message to stderr, doesn't return */
88 void fatal (int ret, char *format, ...) __attribute__ ((noreturn));
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_input_file ();
107 close_pdf_output_files ();
108 exit (ret);
109 }
112 bool close_pdf_output_files (void)
113 {
114 output_file_t *o, *n;
116 for (o = output_files; o; o = n)
117 {
118 n = o->next;
119 pdf_close (o->pdf, PDF_PAGE_MODE_USE_OUTLINES);
120 free (o->name);
121 free (o);
122 }
123 out = NULL;
124 output_files = NULL;
125 return (1);
126 }
128 bool open_pdf_output_file (char *name,
129 pdf_file_attributes_t *attributes)
130 {
131 output_file_t *o;
133 if (out && (strcmp (name, out->name) == 0))
134 return (1);
135 for (o = output_files; o; o = o->next)
136 if (strcmp (name, o->name) == 0)
137 {
138 out = o;
139 return (1);
140 }
141 o = calloc (1, sizeof (output_file_t));
142 if (! o)
143 {
144 fprintf (stderr, "can't calloc output file struct for '%s'\n", name);
145 return (0);
146 }
148 o->name = strdup (name);
149 if (! o->name)
150 {
151 fprintf (stderr, "can't strdup output filename '%s'\n", name);
152 free (o);
153 return (0);
154 }
156 o->pdf = pdf_create (name);
157 if (! o->pdf)
158 {
159 fprintf (stderr, "can't open output file '%s'\n", name);
160 free (o->name);
161 free (o);
162 return (0);
163 }
165 if (attributes->author)
166 pdf_set_author (o->pdf, attributes->author);
167 if (attributes->creator)
168 pdf_set_creator (o->pdf, attributes->creator);
169 if (attributes->title)
170 pdf_set_title (o->pdf, attributes->title);
171 if (attributes->subject)
172 pdf_set_subject (o->pdf, attributes->subject);
173 if (attributes->keywords)
174 pdf_set_keywords (o->pdf, attributes->keywords);
176 /* prepend new output file onto list */
177 o->next = output_files;
178 output_files = o;
180 out = o;
181 return (1);
182 }
185 #define MAX_BOOKMARK_LEVEL 20
186 static pdf_bookmark_handle bookmark_vector [MAX_BOOKMARK_LEVEL + 1] = { NULL };
189 bool process_page (int image, /* range 1 .. n */
190 input_attributes_t input_attributes,
191 bookmark_t *bookmarks,
192 page_label_t *page_label)
193 {
194 pdf_page_handle page;
195 image_info_t image_info;
197 if (! get_image_info (image, input_attributes, & image_info))
198 return (0);
200 page = pdf_new_page (out->pdf,
201 image_info.width_points,
202 image_info.height_points);
204 if (! process_image (image, input_attributes, & image_info, page))
205 return (0);
207 while (bookmarks)
208 {
209 if (bookmarks->level <= MAX_BOOKMARK_LEVEL)
210 {
211 pdf_bookmark_handle parent = bookmark_vector [bookmarks->level - 1];
212 bookmark_vector [bookmarks->level] = pdf_new_bookmark (parent,
213 bookmarks->name,
214 0,
215 page);
216 }
217 else
218 {
219 (void) pdf_new_bookmark (bookmark_vector [MAX_BOOKMARK_LEVEL],
220 bookmarks->name,
221 0,
222 page);
223 }
224 bookmarks = bookmarks->next;
225 }
227 if (page_label)
228 pdf_new_page_label (out->pdf,
229 page_label->page_index,
230 page_label->base,
231 page_label->count,
232 page_label->style,
233 page_label->prefix);
235 return (page != NULL);
236 }
239 #define MAX_BOOKMARK_NAME_LEN 500
242 static int filename_length_without_suffix (char *in_fn)
243 {
244 char *p;
245 int len = strlen (in_fn);
247 p = strrchr (in_fn, '.');
248 if (p && match_input_suffix (p))
249 return (p - in_fn);
250 return (len);
251 }
254 /* $$$ this function should ensure that it doesn't overflow the name string! */
255 static void generate_bookmark_name (char *name,
256 char *bookmark_fmt,
257 char *in_fn,
258 int page)
259 {
260 bool meta = 0;
261 int len;
263 while (*bookmark_fmt)
264 {
265 if (meta)
266 {
267 meta = 0;
268 switch (*bookmark_fmt)
269 {
270 case '%':
271 *(name++) = '%';
272 break;
273 case 'F':
274 len = filename_length_without_suffix (in_fn);
275 strncpy (name, in_fn, len);
276 name += len;
277 break;
278 case 'p':
279 sprintf (name, "%d", page);
280 name += strlen (name);
281 break;
282 default:
283 break;
284 }
285 }
286 else
287 switch (*bookmark_fmt)
288 {
289 case '%':
290 meta = 1;
291 break;
292 default:
293 *(name++) = *bookmark_fmt;
294 }
295 bookmark_fmt++;
296 }
297 *name = '\0';
298 }
301 void main_args (char *out_fn,
302 int inf_count,
303 char **in_fn,
304 char *bookmark_fmt)
305 {
306 int i, ip;
307 input_attributes_t input_attributes;
308 pdf_file_attributes_t output_attributes;
309 bookmark_t bookmark;
310 char bookmark_name [MAX_BOOKMARK_NAME_LEN];
312 bookmark.next = NULL;
313 bookmark.level = 1;
314 bookmark.name = & bookmark_name [0];
316 memset (& input_attributes, 0, sizeof (input_attributes));
317 memset (& output_attributes, 0, sizeof (output_attributes));
319 if (! open_pdf_output_file (out_fn, & output_attributes))
320 fatal (3, "error opening output file \"%s\"\n", out_fn);
321 for (i = 0; i < inf_count; i++)
322 {
323 if (! open_input_file (in_fn [i]))
324 fatal (3, "error opening input file \"%s\"\n", in_fn [i]);
325 for (ip = 1;; ip++)
326 {
327 fprintf (stderr, "processing page %d of file \"%s\"\r", ip, in_fn [i]);
328 if (bookmark_fmt)
329 generate_bookmark_name (& bookmark_name [0],
330 bookmark_fmt,
331 in_fn [i],
332 ip);
333 if (! process_page (ip, input_attributes,
334 bookmark_fmt ? & bookmark : NULL,
335 NULL))
336 fatal (3, "error processing page %d of input file \"%s\"\n", ip, in_fn [i]);
337 if (last_input_page ())
338 break;
339 }
340 if (verbose)
341 fprintf (stderr, "processed %d pages of input file \"%s\"\n", ip, in_fn [i]);
342 if (! close_input_file ())
343 fatal (3, "error closing input file \"%s\"\n", in_fn [i]);
344 }
345 if (! close_pdf_output_files ())
346 fatal (3, "error closing output file \"%s\"\n", out_fn);
347 }
350 void main_control (char *control_fn)
351 {
352 if (! parse_control_file (control_fn))
353 fatal (2, "error parsing control file\n");
354 if (! process_controls ())
355 fatal (3, "error processing control file\n");
356 }
359 int main (int argc, char *argv[])
360 {
361 char *control_fn = NULL;
362 char *out_fn = NULL;
363 char *bookmark_fmt = NULL;
364 int inf_count = 0;
365 char *in_fn [MAX_INPUT_FILES];
367 progname = argv [0];
369 pdf_init ();
371 init_tiff_handler ();
372 init_jpeg_handler ();
373 init_pbm_handler ();
375 while (--argc)
376 {
377 if (argv [1][0] == '-')
378 {
379 if (strcmp (argv [1], "-v") == 0)
380 verbose++;
381 else if (strcmp (argv [1], "-o") == 0)
382 {
383 if (argc)
384 {
385 argc--;
386 argv++;
387 out_fn = argv [1];
388 }
389 else
390 fatal (1, "missing filename after \"-o\" option\n");
391 }
392 else if (strcmp (argv [1], "-c") == 0)
393 {
394 if (argc)
395 {
396 argc--;
397 argv++;
398 control_fn = argv [1];
399 }
400 else
401 fatal (1, "missing filename after \"-s\" option\n");
402 }
403 else if (strcmp (argv [1], "-b") == 0)
404 {
405 if (argc)
406 {
407 argc--;
408 argv++;
409 bookmark_fmt = argv [1];
410 }
411 else
412 fatal (1, "missing format string after \"-b\" option\n");
413 }
414 else
415 fatal (1, "unrecognized option \"%s\"\n", argv [1]);
416 }
417 else if (inf_count < MAX_INPUT_FILES)
418 in_fn [inf_count++] = argv [1];
419 else
420 fatal (1, "exceeded maximum of %d input files\n", MAX_INPUT_FILES);
421 argv++;
422 }
424 if (! ((! out_fn) ^ (! control_fn)))
425 fatal (1, "either a control file or an output file (but not both) must be specified\n");
427 if (out_fn && ! inf_count)
428 fatal (1, "no input files specified\n");
430 if (control_fn && inf_count)
431 fatal (1, "if control file is provided, input files can't be specified as arguments\n");
433 if (control_fn)
434 main_control (control_fn);
435 else
436 main_args (out_fn, inf_count, in_fn, bookmark_fmt);
438 close_input_file ();
439 close_pdf_output_files ();
440 exit (0);
441 }