tumble.c

Mon, 14 Dec 2009 16:18:21 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Mon, 14 Dec 2009 16:18:21 +0000
changeset 172
2fae6df568f6
parent 166
301f6f17c364
permissions
-rw-r--r--

remove erroneous 0.33-philpem1 tag

eric@10 1 /*
eric@125 2 * tumble: build a PDF file from image files
eric@29 3 *
eric@10 4 * Main program
eric@158 5 * $Id: tumble.c,v 1.43 2003/04/10 01:02:12 eric Exp $
eric@49 6 * Copyright 2001, 2002, 2003 Eric Smith <eric@brouhaha.com>
eric@10 7 *
eric@10 8 * This program is free software; you can redistribute it and/or modify
eric@10 9 * it under the terms of the GNU General Public License version 2 as
eric@10 10 * published by the Free Software Foundation. Note that permission is
eric@10 11 * not granted to redistribute this program under the terms of any
eric@10 12 * other version of the General Public License.
eric@10 13 *
eric@10 14 * This program is distributed in the hope that it will be useful,
eric@10 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
eric@10 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
eric@10 17 * GNU General Public License for more details.
eric@10 18 *
eric@10 19 * You should have received a copy of the GNU General Public License
eric@10 20 * along with this program; if not, write to the Free Software
eric@62 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
eric@62 22 */
eric@10 23
eric@10 24
eric@49 25 #include <stdarg.h>
eric@48 26 #include <stdbool.h>
eric@48 27 #include <stdint.h>
eric@10 28 #include <stdio.h>
eric@28 29 #include <stdlib.h>
eric@62 30 #include <string.h>
eric@28 31 #include <unistd.h>
eric@47 32
eric@47 33
eric@18 34 #include "semantics.h"
eric@160 35
eric@160 36 #ifdef CTL_LANG
eric@10 37 #include "parser.tab.h"
eric@160 38 #endif
eric@160 39
eric@125 40 #include "tumble.h"
eric@141 41 #include "bitblt.h"
eric@62 42 #include "pdf.h"
eric@141 43 #include "tumble_input.h"
eric@10 44
eric@10 45
eric@49 46 #define MAX_INPUT_FILES 5000
eric@49 47
eric@26 48 typedef struct output_file_t
eric@26 49 {
eric@26 50 struct output_file_t *next;
eric@26 51 char *name;
eric@62 52 pdf_file_handle pdf;
eric@26 53 } output_file_t;
eric@26 54
eric@26 55
eric@49 56 int verbose;
eric@49 57
eric@49 58
eric@26 59 output_file_t *output_files;
eric@26 60 output_file_t *out;
eric@10 61
eric@10 62
eric@49 63 char *progname;
eric@49 64
eric@49 65
eric@49 66 bool close_pdf_output_files (void);
eric@49 67
eric@49 68
eric@158 69 #define QMAKESTR(x) #x
eric@158 70 #define MAKESTR(x) QMAKESTR(x)
eric@158 71
eric@158 72
eric@49 73 void usage (void)
eric@49 74 {
eric@50 75 fprintf (stderr, "\n");
eric@158 76 fprintf (stderr, "tumble version " MAKESTR(TUMBLE_VERSION) " - Copyright 2001-2003 Eric Smith <eric@brouhaha.com>\n");
eric@127 77 fprintf (stderr, "http://tumble.brouhaha.com/\n");
eric@50 78 fprintf (stderr, "\n");
eric@49 79 fprintf (stderr, "usage:\n");
eric@160 80 #ifdef CTL_LANG
eric@134 81 fprintf (stderr, " %s [options] -c <control.tum>\n", progname);
eric@160 82 #endif
eric@49 83 fprintf (stderr, " %s [options] <input.tif>... -o <output.pdf>\n", progname);
eric@49 84 fprintf (stderr, "options:\n");
eric@134 85 fprintf (stderr, " -v verbose\n");
eric@134 86 fprintf (stderr, " -b <fmt> create bookmarks\n");
eric@62 87 fprintf (stderr, "bookmark format:\n");
eric@74 88 fprintf (stderr, " %%F file name (sans suffix)\n");
eric@62 89 fprintf (stderr, " %%p page number\n");
eric@49 90 }
eric@49 91
eric@49 92
eric@49 93 /* generate fatal error message to stderr, doesn't return */
eric@139 94 void fatal (int ret, char *format, ...) __attribute__ ((noreturn));
eric@139 95
eric@49 96 void fatal (int ret, char *format, ...)
eric@49 97 {
eric@49 98 va_list ap;
eric@49 99
eric@49 100 fprintf (stderr, "fatal error");
eric@49 101 if (format)
eric@49 102 {
eric@49 103 fprintf (stderr, ": ");
eric@49 104 va_start (ap, format);
eric@49 105 vfprintf (stderr, format, ap);
eric@49 106 va_end (ap);
eric@49 107 }
eric@49 108 else
eric@49 109 fprintf (stderr, "\n");
eric@49 110 if (ret == 1)
eric@49 111 usage ();
eric@139 112 close_input_file ();
eric@49 113 close_pdf_output_files ();
eric@49 114 exit (ret);
eric@49 115 }
eric@49 116
eric@49 117
eric@48 118 bool close_pdf_output_files (void)
eric@10 119 {
eric@26 120 output_file_t *o, *n;
eric@26 121
eric@26 122 for (o = output_files; o; o = n)
eric@26 123 {
eric@26 124 n = o->next;
eric@133 125 pdf_close (o->pdf, PDF_PAGE_MODE_USE_OUTLINES);
eric@26 126 free (o->name);
eric@26 127 free (o);
eric@26 128 }
eric@10 129 out = NULL;
eric@26 130 output_files = NULL;
eric@10 131 return (1);
eric@10 132 }
eric@10 133
eric@48 134 bool open_pdf_output_file (char *name,
eric@48 135 pdf_file_attributes_t *attributes)
eric@10 136 {
eric@26 137 output_file_t *o;
eric@26 138
eric@26 139 if (out && (strcmp (name, out->name) == 0))
eric@26 140 return (1);
eric@26 141 for (o = output_files; o; o = o->next)
eric@26 142 if (strcmp (name, o->name) == 0)
eric@26 143 {
eric@26 144 out = o;
eric@26 145 return (1);
eric@26 146 }
eric@26 147 o = calloc (1, sizeof (output_file_t));
eric@29 148 if (! o)
eric@10 149 {
eric@26 150 fprintf (stderr, "can't calloc output file struct for '%s'\n", name);
eric@26 151 return (0);
eric@26 152 }
eric@26 153
eric@26 154 o->name = strdup (name);
eric@26 155 if (! o->name)
eric@26 156 {
eric@26 157 fprintf (stderr, "can't strdup output filename '%s'\n", name);
eric@26 158 free (o);
eric@10 159 return (0);
eric@10 160 }
eric@26 161
eric@133 162 o->pdf = pdf_create (name);
eric@26 163 if (! o->pdf)
eric@26 164 {
eric@26 165 fprintf (stderr, "can't open output file '%s'\n", name);
eric@26 166 free (o->name);
eric@26 167 free (o);
eric@26 168 return (0);
eric@26 169 }
eric@26 170
eric@30 171 if (attributes->author)
eric@62 172 pdf_set_author (o->pdf, attributes->author);
eric@30 173 if (attributes->creator)
eric@62 174 pdf_set_creator (o->pdf, attributes->creator);
eric@30 175 if (attributes->title)
eric@62 176 pdf_set_title (o->pdf, attributes->title);
eric@30 177 if (attributes->subject)
eric@62 178 pdf_set_subject (o->pdf, attributes->subject);
eric@30 179 if (attributes->keywords)
eric@62 180 pdf_set_keywords (o->pdf, attributes->keywords);
eric@30 181
eric@26 182 /* prepend new output file onto list */
eric@26 183 o->next = output_files;
eric@26 184 output_files = o;
eric@26 185
eric@26 186 out = o;
eric@10 187 return (1);
eric@10 188 }
eric@10 189
eric@10 190
eric@154 191 #define MAX_BOOKMARK_LEVEL 20
eric@154 192 static pdf_bookmark_handle bookmark_vector [MAX_BOOKMARK_LEVEL + 1] = { NULL };
eric@154 193
eric@154 194
eric@108 195 bool process_page (int image, /* range 1 .. n */
eric@108 196 input_attributes_t input_attributes,
eric@131 197 bookmark_t *bookmarks,
eric@131 198 page_label_t *page_label)
eric@108 199 {
eric@131 200 pdf_page_handle page;
eric@141 201 image_info_t image_info;
eric@141 202
eric@141 203 if (! get_image_info (image, input_attributes, & image_info))
eric@141 204 return (0);
eric@131 205
eric@141 206 page = pdf_new_page (out->pdf,
eric@141 207 image_info.width_points,
eric@141 208 image_info.height_points);
eric@141 209
eric@141 210 if (! process_image (image, input_attributes, & image_info, page))
eric@141 211 return (0);
eric@108 212
eric@131 213 while (bookmarks)
eric@131 214 {
eric@154 215 if (bookmarks->level <= MAX_BOOKMARK_LEVEL)
eric@154 216 {
eric@154 217 pdf_bookmark_handle parent = bookmark_vector [bookmarks->level - 1];
eric@154 218 bookmark_vector [bookmarks->level] = pdf_new_bookmark (parent,
eric@154 219 bookmarks->name,
eric@154 220 0,
eric@154 221 page);
eric@154 222 }
eric@154 223 else
eric@154 224 {
eric@154 225 (void) pdf_new_bookmark (bookmark_vector [MAX_BOOKMARK_LEVEL],
eric@154 226 bookmarks->name,
eric@154 227 0,
eric@154 228 page);
eric@154 229 }
eric@131 230 bookmarks = bookmarks->next;
eric@131 231 }
eric@108 232
eric@131 233 if (page_label)
eric@131 234 pdf_new_page_label (out->pdf,
eric@131 235 page_label->page_index,
eric@131 236 page_label->base,
eric@131 237 page_label->count,
eric@131 238 page_label->style,
eric@131 239 page_label->prefix);
eric@131 240
eric@131 241 return (page != NULL);
eric@108 242 }
eric@108 243
eric@108 244
eric@74 245 #define MAX_BOOKMARK_NAME_LEN 500
eric@74 246
eric@74 247
eric@74 248 static int filename_length_without_suffix (char *in_fn)
eric@74 249 {
eric@74 250 char *p;
eric@74 251 int len = strlen (in_fn);
eric@74 252
eric@74 253 p = strrchr (in_fn, '.');
eric@151 254 if (p && match_input_suffix (p))
eric@74 255 return (p - in_fn);
eric@74 256 return (len);
eric@74 257 }
eric@74 258
eric@74 259
eric@74 260 /* $$$ this function should ensure that it doesn't overflow the name string! */
eric@74 261 static void generate_bookmark_name (char *name,
eric@74 262 char *bookmark_fmt,
eric@74 263 char *in_fn,
eric@74 264 int page)
eric@74 265 {
eric@74 266 bool meta = 0;
eric@74 267 int len;
eric@74 268
eric@74 269 while (*bookmark_fmt)
eric@74 270 {
eric@74 271 if (meta)
eric@74 272 {
eric@74 273 meta = 0;
eric@74 274 switch (*bookmark_fmt)
eric@74 275 {
eric@74 276 case '%':
eric@74 277 *(name++) = '%';
eric@74 278 break;
eric@74 279 case 'F':
eric@74 280 len = filename_length_without_suffix (in_fn);
eric@74 281 strncpy (name, in_fn, len);
eric@74 282 name += len;
eric@74 283 break;
eric@74 284 case 'p':
eric@74 285 sprintf (name, "%d", page);
eric@74 286 name += strlen (name);
eric@74 287 break;
eric@74 288 default:
eric@74 289 break;
eric@74 290 }
eric@74 291 }
eric@74 292 else
eric@74 293 switch (*bookmark_fmt)
eric@74 294 {
eric@74 295 case '%':
eric@74 296 meta = 1;
eric@74 297 break;
eric@74 298 default:
eric@74 299 *(name++) = *bookmark_fmt;
eric@74 300 }
eric@74 301 bookmark_fmt++;
eric@74 302 }
eric@116 303 *name = '\0';
eric@74 304 }
eric@74 305
eric@74 306
eric@74 307 void main_args (char *out_fn,
eric@74 308 int inf_count,
eric@74 309 char **in_fn,
eric@74 310 char *bookmark_fmt)
eric@49 311 {
eric@49 312 int i, ip;
eric@49 313 input_attributes_t input_attributes;
eric@49 314 pdf_file_attributes_t output_attributes;
eric@74 315 bookmark_t bookmark;
eric@74 316 char bookmark_name [MAX_BOOKMARK_NAME_LEN];
eric@74 317
eric@74 318 bookmark.next = NULL;
eric@74 319 bookmark.level = 1;
eric@74 320 bookmark.name = & bookmark_name [0];
eric@49 321
eric@49 322 memset (& input_attributes, 0, sizeof (input_attributes));
eric@49 323 memset (& output_attributes, 0, sizeof (output_attributes));
eric@49 324
eric@49 325 if (! open_pdf_output_file (out_fn, & output_attributes))
eric@49 326 fatal (3, "error opening output file \"%s\"\n", out_fn);
eric@49 327 for (i = 0; i < inf_count; i++)
eric@49 328 {
eric@139 329 if (! open_input_file (in_fn [i]))
eric@49 330 fatal (3, "error opening input file \"%s\"\n", in_fn [i]);
eric@49 331 for (ip = 1;; ip++)
eric@49 332 {
eric@62 333 fprintf (stderr, "processing page %d of file \"%s\"\r", ip, in_fn [i]);
eric@74 334 if (bookmark_fmt)
eric@74 335 generate_bookmark_name (& bookmark_name [0],
eric@74 336 bookmark_fmt,
eric@74 337 in_fn [i],
eric@74 338 ip);
eric@74 339 if (! process_page (ip, input_attributes,
eric@131 340 bookmark_fmt ? & bookmark : NULL,
eric@131 341 NULL))
eric@49 342 fatal (3, "error processing page %d of input file \"%s\"\n", ip, in_fn [i]);
eric@139 343 if (last_input_page ())
eric@49 344 break;
eric@49 345 }
eric@49 346 if (verbose)
eric@49 347 fprintf (stderr, "processed %d pages of input file \"%s\"\n", ip, in_fn [i]);
eric@139 348 if (! close_input_file ())
eric@49 349 fatal (3, "error closing input file \"%s\"\n", in_fn [i]);
eric@49 350 }
eric@49 351 if (! close_pdf_output_files ())
eric@49 352 fatal (3, "error closing output file \"%s\"\n", out_fn);
eric@49 353 }
eric@49 354
eric@49 355
eric@160 356 #ifdef CTL_LANG
eric@134 357 void main_control (char *control_fn)
eric@49 358 {
eric@134 359 if (! parse_control_file (control_fn))
eric@134 360 fatal (2, "error parsing control file\n");
eric@134 361 if (! process_controls ())
eric@134 362 fatal (3, "error processing control file\n");
eric@49 363 }
eric@160 364 #endif
eric@49 365
eric@49 366
eric@10 367 int main (int argc, char *argv[])
eric@10 368 {
eric@160 369 #ifdef CTL_LANG
eric@134 370 char *control_fn = NULL;
eric@160 371 #endif
eric@49 372 char *out_fn = NULL;
eric@62 373 char *bookmark_fmt = NULL;
eric@49 374 int inf_count = 0;
eric@49 375 char *in_fn [MAX_INPUT_FILES];
eric@49 376
eric@49 377 progname = argv [0];
eric@10 378
eric@62 379 pdf_init ();
eric@10 380
eric@141 381 init_tiff_handler ();
eric@141 382 init_jpeg_handler ();
eric@157 383 init_pbm_handler ();
philpem@166 384 init_png_handler ();
philpem@166 385 init_jp2_handler ();
eric@141 386
eric@49 387 while (--argc)
eric@10 388 {
eric@49 389 if (argv [1][0] == '-')
eric@49 390 {
eric@49 391 if (strcmp (argv [1], "-v") == 0)
eric@49 392 verbose++;
eric@49 393 else if (strcmp (argv [1], "-o") == 0)
eric@49 394 {
eric@49 395 if (argc)
eric@49 396 {
eric@49 397 argc--;
eric@49 398 argv++;
eric@49 399 out_fn = argv [1];
eric@49 400 }
eric@49 401 else
eric@49 402 fatal (1, "missing filename after \"-o\" option\n");
eric@49 403 }
eric@160 404 #ifdef CTL_LANG
eric@134 405 else if (strcmp (argv [1], "-c") == 0)
eric@49 406 {
eric@49 407 if (argc)
eric@49 408 {
eric@49 409 argc--;
eric@49 410 argv++;
eric@134 411 control_fn = argv [1];
eric@49 412 }
eric@49 413 else
eric@49 414 fatal (1, "missing filename after \"-s\" option\n");
eric@49 415 }
eric@160 416 #endif
eric@62 417 else if (strcmp (argv [1], "-b") == 0)
eric@62 418 {
eric@62 419 if (argc)
eric@62 420 {
eric@62 421 argc--;
eric@62 422 argv++;
eric@62 423 bookmark_fmt = argv [1];
eric@62 424 }
eric@62 425 else
eric@62 426 fatal (1, "missing format string after \"-b\" option\n");
eric@62 427 }
eric@49 428 else
eric@49 429 fatal (1, "unrecognized option \"%s\"\n", argv [1]);
eric@49 430 }
eric@49 431 else if (inf_count < MAX_INPUT_FILES)
eric@49 432 in_fn [inf_count++] = argv [1];
eric@49 433 else
eric@49 434 fatal (1, "exceeded maximum of %d input files\n", MAX_INPUT_FILES);
eric@49 435 argv++;
eric@10 436 }
eric@10 437
eric@160 438 #ifdef CTL_LANG
eric@134 439 if (! ((! out_fn) ^ (! control_fn)))
eric@134 440 fatal (1, "either a control file or an output file (but not both) must be specified\n");
eric@160 441 if (control_fn && inf_count)
eric@160 442 fatal (1, "if control file is provided, input files can't be specified as arguments\n");
eric@160 443 #else
eric@160 444 if (! out_fn)
eric@160 445 fatal (1, "an output file must be specified\n");
eric@160 446 #endif
eric@49 447
eric@49 448 if (out_fn && ! inf_count)
eric@49 449 fatal (1, "no input files specified\n");
eric@26 450
eric@160 451 #ifdef CTL_LANG
eric@134 452 if (control_fn)
eric@134 453 main_control (control_fn);
eric@49 454 else
eric@74 455 main_args (out_fn, inf_count, in_fn, bookmark_fmt);
eric@160 456 #else
eric@160 457 main_args (out_fn, inf_count, in_fn, bookmark_fmt);
eric@160 458 #endif
eric@17 459
eric@139 460 close_input_file ();
eric@26 461 close_pdf_output_files ();
eric@49 462 exit (0);
eric@10 463 }