Tue, 09 Dec 2003 18:05:44 +0000
fix sed regexp in bin-dist-rh for Red Hat 9 (no minor version).
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"
36 #ifdef CTL_LANG
37 #include "parser.tab.h"
38 #endif
40 #include "tumble.h"
41 #include "bitblt.h"
42 #include "pdf.h"
43 #include "tumble_input.h"
46 #define MAX_INPUT_FILES 5000
48 typedef struct output_file_t
49 {
50 struct output_file_t *next;
51 char *name;
52 pdf_file_handle pdf;
53 } output_file_t;
56 int verbose;
59 output_file_t *output_files;
60 output_file_t *out;
63 char *progname;
66 bool close_pdf_output_files (void);
69 #define QMAKESTR(x) #x
70 #define MAKESTR(x) QMAKESTR(x)
73 void usage (void)
74 {
75 fprintf (stderr, "\n");
76 fprintf (stderr, "tumble version " MAKESTR(TUMBLE_VERSION) " - Copyright 2001-2003 Eric Smith <eric@brouhaha.com>\n");
77 fprintf (stderr, "http://tumble.brouhaha.com/\n");
78 fprintf (stderr, "\n");
79 fprintf (stderr, "usage:\n");
80 #ifdef CTL_LANG
81 fprintf (stderr, " %s [options] -c <control.tum>\n", progname);
82 #endif
83 fprintf (stderr, " %s [options] <input.tif>... -o <output.pdf>\n", progname);
84 fprintf (stderr, "options:\n");
85 fprintf (stderr, " -v verbose\n");
86 fprintf (stderr, " -b <fmt> create bookmarks\n");
87 fprintf (stderr, "bookmark format:\n");
88 fprintf (stderr, " %%F file name (sans suffix)\n");
89 fprintf (stderr, " %%p page number\n");
90 }
93 /* generate fatal error message to stderr, doesn't return */
94 void fatal (int ret, char *format, ...) __attribute__ ((noreturn));
96 void fatal (int ret, char *format, ...)
97 {
98 va_list ap;
100 fprintf (stderr, "fatal error");
101 if (format)
102 {
103 fprintf (stderr, ": ");
104 va_start (ap, format);
105 vfprintf (stderr, format, ap);
106 va_end (ap);
107 }
108 else
109 fprintf (stderr, "\n");
110 if (ret == 1)
111 usage ();
112 close_input_file ();
113 close_pdf_output_files ();
114 exit (ret);
115 }
118 bool close_pdf_output_files (void)
119 {
120 output_file_t *o, *n;
122 for (o = output_files; o; o = n)
123 {
124 n = o->next;
125 pdf_close (o->pdf, PDF_PAGE_MODE_USE_OUTLINES);
126 free (o->name);
127 free (o);
128 }
129 out = NULL;
130 output_files = NULL;
131 return (1);
132 }
134 bool open_pdf_output_file (char *name,
135 pdf_file_attributes_t *attributes)
136 {
137 output_file_t *o;
139 if (out && (strcmp (name, out->name) == 0))
140 return (1);
141 for (o = output_files; o; o = o->next)
142 if (strcmp (name, o->name) == 0)
143 {
144 out = o;
145 return (1);
146 }
147 o = calloc (1, sizeof (output_file_t));
148 if (! o)
149 {
150 fprintf (stderr, "can't calloc output file struct for '%s'\n", name);
151 return (0);
152 }
154 o->name = strdup (name);
155 if (! o->name)
156 {
157 fprintf (stderr, "can't strdup output filename '%s'\n", name);
158 free (o);
159 return (0);
160 }
162 o->pdf = pdf_create (name);
163 if (! o->pdf)
164 {
165 fprintf (stderr, "can't open output file '%s'\n", name);
166 free (o->name);
167 free (o);
168 return (0);
169 }
171 if (attributes->author)
172 pdf_set_author (o->pdf, attributes->author);
173 if (attributes->creator)
174 pdf_set_creator (o->pdf, attributes->creator);
175 if (attributes->title)
176 pdf_set_title (o->pdf, attributes->title);
177 if (attributes->subject)
178 pdf_set_subject (o->pdf, attributes->subject);
179 if (attributes->keywords)
180 pdf_set_keywords (o->pdf, attributes->keywords);
182 /* prepend new output file onto list */
183 o->next = output_files;
184 output_files = o;
186 out = o;
187 return (1);
188 }
191 #define MAX_BOOKMARK_LEVEL 20
192 static pdf_bookmark_handle bookmark_vector [MAX_BOOKMARK_LEVEL + 1] = { NULL };
195 bool process_page (int image, /* range 1 .. n */
196 input_attributes_t input_attributes,
197 bookmark_t *bookmarks,
198 page_label_t *page_label)
199 {
200 pdf_page_handle page;
201 image_info_t image_info;
203 if (! get_image_info (image, input_attributes, & image_info))
204 return (0);
206 page = pdf_new_page (out->pdf,
207 image_info.width_points,
208 image_info.height_points);
210 if (! process_image (image, input_attributes, & image_info, page))
211 return (0);
213 while (bookmarks)
214 {
215 if (bookmarks->level <= MAX_BOOKMARK_LEVEL)
216 {
217 pdf_bookmark_handle parent = bookmark_vector [bookmarks->level - 1];
218 bookmark_vector [bookmarks->level] = pdf_new_bookmark (parent,
219 bookmarks->name,
220 0,
221 page);
222 }
223 else
224 {
225 (void) pdf_new_bookmark (bookmark_vector [MAX_BOOKMARK_LEVEL],
226 bookmarks->name,
227 0,
228 page);
229 }
230 bookmarks = bookmarks->next;
231 }
233 if (page_label)
234 pdf_new_page_label (out->pdf,
235 page_label->page_index,
236 page_label->base,
237 page_label->count,
238 page_label->style,
239 page_label->prefix);
241 return (page != NULL);
242 }
245 #define MAX_BOOKMARK_NAME_LEN 500
248 static int filename_length_without_suffix (char *in_fn)
249 {
250 char *p;
251 int len = strlen (in_fn);
253 p = strrchr (in_fn, '.');
254 if (p && match_input_suffix (p))
255 return (p - in_fn);
256 return (len);
257 }
260 /* $$$ this function should ensure that it doesn't overflow the name string! */
261 static void generate_bookmark_name (char *name,
262 char *bookmark_fmt,
263 char *in_fn,
264 int page)
265 {
266 bool meta = 0;
267 int len;
269 while (*bookmark_fmt)
270 {
271 if (meta)
272 {
273 meta = 0;
274 switch (*bookmark_fmt)
275 {
276 case '%':
277 *(name++) = '%';
278 break;
279 case 'F':
280 len = filename_length_without_suffix (in_fn);
281 strncpy (name, in_fn, len);
282 name += len;
283 break;
284 case 'p':
285 sprintf (name, "%d", page);
286 name += strlen (name);
287 break;
288 default:
289 break;
290 }
291 }
292 else
293 switch (*bookmark_fmt)
294 {
295 case '%':
296 meta = 1;
297 break;
298 default:
299 *(name++) = *bookmark_fmt;
300 }
301 bookmark_fmt++;
302 }
303 *name = '\0';
304 }
307 void main_args (char *out_fn,
308 int inf_count,
309 char **in_fn,
310 char *bookmark_fmt)
311 {
312 int i, ip;
313 input_attributes_t input_attributes;
314 pdf_file_attributes_t output_attributes;
315 bookmark_t bookmark;
316 char bookmark_name [MAX_BOOKMARK_NAME_LEN];
318 bookmark.next = NULL;
319 bookmark.level = 1;
320 bookmark.name = & bookmark_name [0];
322 memset (& input_attributes, 0, sizeof (input_attributes));
323 memset (& output_attributes, 0, sizeof (output_attributes));
325 if (! open_pdf_output_file (out_fn, & output_attributes))
326 fatal (3, "error opening output file \"%s\"\n", out_fn);
327 for (i = 0; i < inf_count; i++)
328 {
329 if (! open_input_file (in_fn [i]))
330 fatal (3, "error opening input file \"%s\"\n", in_fn [i]);
331 for (ip = 1;; ip++)
332 {
333 fprintf (stderr, "processing page %d of file \"%s\"\r", ip, in_fn [i]);
334 if (bookmark_fmt)
335 generate_bookmark_name (& bookmark_name [0],
336 bookmark_fmt,
337 in_fn [i],
338 ip);
339 if (! process_page (ip, input_attributes,
340 bookmark_fmt ? & bookmark : NULL,
341 NULL))
342 fatal (3, "error processing page %d of input file \"%s\"\n", ip, in_fn [i]);
343 if (last_input_page ())
344 break;
345 }
346 if (verbose)
347 fprintf (stderr, "processed %d pages of input file \"%s\"\n", ip, in_fn [i]);
348 if (! close_input_file ())
349 fatal (3, "error closing input file \"%s\"\n", in_fn [i]);
350 }
351 if (! close_pdf_output_files ())
352 fatal (3, "error closing output file \"%s\"\n", out_fn);
353 }
356 #ifdef CTL_LANG
357 void main_control (char *control_fn)
358 {
359 if (! parse_control_file (control_fn))
360 fatal (2, "error parsing control file\n");
361 if (! process_controls ())
362 fatal (3, "error processing control file\n");
363 }
364 #endif
367 int main (int argc, char *argv[])
368 {
369 #ifdef CTL_LANG
370 char *control_fn = NULL;
371 #endif
372 char *out_fn = NULL;
373 char *bookmark_fmt = NULL;
374 int inf_count = 0;
375 char *in_fn [MAX_INPUT_FILES];
377 progname = argv [0];
379 pdf_init ();
381 init_tiff_handler ();
382 init_jpeg_handler ();
383 init_pbm_handler ();
385 while (--argc)
386 {
387 if (argv [1][0] == '-')
388 {
389 if (strcmp (argv [1], "-v") == 0)
390 verbose++;
391 else if (strcmp (argv [1], "-o") == 0)
392 {
393 if (argc)
394 {
395 argc--;
396 argv++;
397 out_fn = argv [1];
398 }
399 else
400 fatal (1, "missing filename after \"-o\" option\n");
401 }
402 #ifdef CTL_LANG
403 else if (strcmp (argv [1], "-c") == 0)
404 {
405 if (argc)
406 {
407 argc--;
408 argv++;
409 control_fn = argv [1];
410 }
411 else
412 fatal (1, "missing filename after \"-s\" option\n");
413 }
414 #endif
415 else if (strcmp (argv [1], "-b") == 0)
416 {
417 if (argc)
418 {
419 argc--;
420 argv++;
421 bookmark_fmt = argv [1];
422 }
423 else
424 fatal (1, "missing format string after \"-b\" option\n");
425 }
426 else
427 fatal (1, "unrecognized option \"%s\"\n", argv [1]);
428 }
429 else if (inf_count < MAX_INPUT_FILES)
430 in_fn [inf_count++] = argv [1];
431 else
432 fatal (1, "exceeded maximum of %d input files\n", MAX_INPUT_FILES);
433 argv++;
434 }
436 #ifdef CTL_LANG
437 if (! ((! out_fn) ^ (! control_fn)))
438 fatal (1, "either a control file or an output file (but not both) must be specified\n");
439 if (control_fn && inf_count)
440 fatal (1, "if control file is provided, input files can't be specified as arguments\n");
441 #else
442 if (! out_fn)
443 fatal (1, "an output file must be specified\n");
444 #endif
446 if (out_fn && ! inf_count)
447 fatal (1, "no input files specified\n");
449 #ifdef CTL_LANG
450 if (control_fn)
451 main_control (control_fn);
452 else
453 main_args (out_fn, inf_count, in_fn, bookmark_fmt);
454 #else
455 main_args (out_fn, inf_count, in_fn, bookmark_fmt);
456 #endif
458 close_input_file ();
459 close_pdf_output_files ();
460 exit (0);
461 }