tumble.c

Thu, 20 Mar 2003 01:08:05 +0000

author
eric
date
Thu, 20 Mar 2003 01:08:05 +0000
changeset 140
3fe049d83e22
parent 139
ec2a06d8a2a6
child 141
752599b50ff3
permissions
-rw-r--r--

*** empty log message ***

eric@10 1 /*
eric@125 2 * tumble: build a PDF file from image files
eric@29 3 *
eric@10 4 * Main program
eric@139 5 * $Id: tumble.c,v 1.37 2003/03/19 07:39:55 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@10 33 #include <tiffio.h>
eric@47 34 #define TIFF_REVERSE_BITS
eric@47 35
eric@10 36 #include "bitblt.h"
eric@18 37 #include "semantics.h"
eric@10 38 #include "parser.tab.h"
eric@125 39 #include "tumble.h"
eric@62 40 #include "pdf.h"
eric@10 41
eric@10 42
eric@49 43 #define MAX_INPUT_FILES 5000
eric@49 44
eric@28 45 #define POINTS_PER_INCH 72
eric@28 46
eric@28 47 /* page size limited by Acrobat Reader to 45 inches on a side */
eric@28 48 #define PAGE_MAX_INCHES 45
eric@28 49 #define PAGE_MAX_POINTS (PAGE_MAX_INCHES * POINTS_PER_INCH)
eric@28 50
eric@28 51
eric@26 52 typedef struct output_file_t
eric@26 53 {
eric@26 54 struct output_file_t *next;
eric@26 55 char *name;
eric@62 56 pdf_file_handle pdf;
eric@26 57 } output_file_t;
eric@26 58
eric@26 59
eric@49 60 int verbose;
eric@49 61
eric@49 62
eric@139 63 typedef enum
eric@139 64 {
eric@139 65 INPUT_FILE_TYPE_NONE,
eric@139 66 INPUT_FILE_TYPE_TIFF,
eric@139 67 INPUT_FILE_TYPE_JPEG
eric@139 68 } input_file_type_t;
eric@139 69
eric@139 70
eric@26 71 char *in_filename;
eric@139 72 input_file_type_t in_type;
eric@139 73 FILE *in;
eric@139 74 TIFF *tiff_in;
eric@26 75 output_file_t *output_files;
eric@26 76 output_file_t *out;
eric@10 77
eric@10 78
eric@49 79 char *progname;
eric@49 80
eric@49 81
eric@49 82 bool close_tiff_input_file (void);
eric@49 83 bool close_pdf_output_files (void);
eric@49 84
eric@49 85
eric@49 86 void usage (void)
eric@49 87 {
eric@50 88 fprintf (stderr, "\n");
eric@125 89 fprintf (stderr, "tumble - Copyright 2001-2003 Eric Smith <eric@brouhaha.com>\n");
eric@127 90 fprintf (stderr, "http://tumble.brouhaha.com/\n");
eric@50 91 fprintf (stderr, "\n");
eric@49 92 fprintf (stderr, "usage:\n");
eric@134 93 fprintf (stderr, " %s [options] -c <control.tum>\n", progname);
eric@49 94 fprintf (stderr, " %s [options] <input.tif>... -o <output.pdf>\n", progname);
eric@49 95 fprintf (stderr, "options:\n");
eric@134 96 fprintf (stderr, " -v verbose\n");
eric@134 97 fprintf (stderr, " -b <fmt> create bookmarks\n");
eric@62 98 fprintf (stderr, "bookmark format:\n");
eric@74 99 fprintf (stderr, " %%F file name (sans suffix)\n");
eric@62 100 fprintf (stderr, " %%p page number\n");
eric@49 101 }
eric@49 102
eric@49 103
eric@49 104 /* generate fatal error message to stderr, doesn't return */
eric@139 105 void fatal (int ret, char *format, ...) __attribute__ ((noreturn));
eric@139 106
eric@49 107 void fatal (int ret, char *format, ...)
eric@49 108 {
eric@49 109 va_list ap;
eric@49 110
eric@49 111 fprintf (stderr, "fatal error");
eric@49 112 if (format)
eric@49 113 {
eric@49 114 fprintf (stderr, ": ");
eric@49 115 va_start (ap, format);
eric@49 116 vfprintf (stderr, format, ap);
eric@49 117 va_end (ap);
eric@49 118 }
eric@49 119 else
eric@49 120 fprintf (stderr, "\n");
eric@49 121 if (ret == 1)
eric@49 122 usage ();
eric@139 123 close_input_file ();
eric@49 124 close_pdf_output_files ();
eric@49 125 exit (ret);
eric@49 126 }
eric@49 127
eric@49 128
eric@48 129 bool close_tiff_input_file (void)
eric@10 130 {
eric@139 131 TIFFClose (tiff_in);
eric@139 132 return (1);
eric@139 133 }
eric@139 134
eric@139 135
eric@139 136 bool open_tiff_input_file (FILE *f, char *name)
eric@139 137 {
eric@139 138 tiff_in = TIFFFdOpen (fileno (f), name, "r");
eric@139 139 if (! tiff_in)
eric@26 140 {
eric@139 141 fprintf (stderr, "can't open input file '%s'\n", name);
eric@26 142 free (in_filename);
eric@139 143 return (0);
eric@26 144 }
eric@139 145 in_type = INPUT_FILE_TYPE_TIFF;
eric@10 146 return (1);
eric@10 147 }
eric@10 148
eric@49 149
eric@139 150 bool close_jpeg_input_file (void)
eric@139 151 {
eric@139 152 return (1);
eric@139 153 }
eric@139 154
eric@139 155
eric@139 156 bool open_jpeg_input_file (FILE *f, char *name)
eric@10 157 {
eric@139 158 in_type = INPUT_FILE_TYPE_JPEG;
eric@139 159 return (1);
eric@139 160 }
eric@139 161
eric@139 162
eric@139 163 bool open_input_file (char *name)
eric@139 164 {
eric@139 165 bool result;
eric@139 166 uint8_t buf [2];
eric@139 167 size_t l;
eric@139 168
eric@10 169 if (in)
eric@26 170 {
eric@26 171 if (strcmp (name, in_filename) == 0)
eric@26 172 return (1);
eric@139 173 close_input_file ();
eric@26 174 }
eric@26 175 in_filename = strdup (name);
eric@26 176 if (! in_filename)
eric@26 177 {
eric@26 178 fprintf (stderr, "can't strdup input filename '%s'\n", name);
eric@26 179 return (0);
eric@26 180 }
eric@139 181
eric@139 182 in = fopen (name, "rb");
eric@10 183 if (! in)
eric@139 184 return (0);
eric@139 185
eric@139 186 l = fread (& buf [0], 1, sizeof (buf), in);
eric@139 187 if (l != sizeof (buf))
eric@139 188 return (0);
eric@139 189
eric@139 190 rewind (in);
eric@139 191
eric@139 192 if ((buf [0] == 0x49) && (buf [1] == 0x49))
eric@139 193 result = open_tiff_input_file (in, name);
eric@139 194 else if ((buf [0] == 0xff) && (buf [1] == 0xd8))
eric@139 195 result = open_jpeg_input_file (in, name);
eric@139 196 else
eric@139 197 {
eric@139 198 fprintf (stderr, "unrecognized file header in file '%s'\n", name);
eric@139 199 result = 0;
eric@139 200 }
eric@139 201 if (! result)
eric@139 202 {
eric@139 203 if (in)
eric@139 204 fclose (in);
eric@139 205 in = NULL;
eric@139 206 in_type = INPUT_FILE_TYPE_NONE;
eric@139 207 }
eric@139 208 return (result);
eric@139 209 }
eric@139 210
eric@139 211
eric@139 212 bool close_input_file (void)
eric@139 213 {
eric@139 214 bool result;
eric@139 215
eric@139 216 switch (in_type)
eric@10 217 {
eric@139 218 case INPUT_FILE_TYPE_NONE:
eric@139 219 return (1);
eric@139 220 case INPUT_FILE_TYPE_TIFF:
eric@139 221 result = close_tiff_input_file ();
eric@139 222 break;
eric@139 223 case INPUT_FILE_TYPE_JPEG:
eric@139 224 result = close_jpeg_input_file ();
eric@139 225 break;
eric@139 226 default:
eric@139 227 fatal (3, "internal error: bad input file type\n");
eric@10 228 }
eric@139 229
eric@139 230 if (in_filename)
eric@139 231 free (in_filename);
eric@139 232 fclose (in);
eric@139 233 in = NULL;
eric@139 234
eric@139 235 return (result);
eric@139 236 }
eric@139 237
eric@139 238
eric@139 239 bool last_tiff_input_page (void)
eric@139 240 {
eric@139 241 return (TIFFLastDirectory (tiff_in));
eric@139 242 }
eric@139 243
eric@139 244
eric@139 245 bool last_jpeg_input_page (void)
eric@139 246 {
eric@10 247 return (1);
eric@10 248 }
eric@10 249
eric@10 250
eric@139 251 bool last_input_page (void)
eric@139 252 {
eric@139 253 switch (in_type)
eric@139 254 {
eric@139 255 case INPUT_FILE_TYPE_TIFF:
eric@139 256 return (last_tiff_input_page ());
eric@139 257 case INPUT_FILE_TYPE_JPEG:
eric@139 258 return (last_jpeg_input_page ());
eric@139 259 default:
eric@139 260 fatal (3, "internal error: bad input file type\n");
eric@139 261 }
eric@139 262 }
eric@139 263
eric@139 264
eric@48 265 bool close_pdf_output_files (void)
eric@10 266 {
eric@26 267 output_file_t *o, *n;
eric@26 268
eric@26 269 for (o = output_files; o; o = n)
eric@26 270 {
eric@26 271 n = o->next;
eric@133 272 pdf_close (o->pdf, PDF_PAGE_MODE_USE_OUTLINES);
eric@26 273 free (o->name);
eric@26 274 free (o);
eric@26 275 }
eric@10 276 out = NULL;
eric@26 277 output_files = NULL;
eric@10 278 return (1);
eric@10 279 }
eric@10 280
eric@48 281 bool open_pdf_output_file (char *name,
eric@48 282 pdf_file_attributes_t *attributes)
eric@10 283 {
eric@26 284 output_file_t *o;
eric@26 285
eric@26 286 if (out && (strcmp (name, out->name) == 0))
eric@26 287 return (1);
eric@26 288 for (o = output_files; o; o = o->next)
eric@26 289 if (strcmp (name, o->name) == 0)
eric@26 290 {
eric@26 291 out = o;
eric@26 292 return (1);
eric@26 293 }
eric@26 294 o = calloc (1, sizeof (output_file_t));
eric@29 295 if (! o)
eric@10 296 {
eric@26 297 fprintf (stderr, "can't calloc output file struct for '%s'\n", name);
eric@26 298 return (0);
eric@26 299 }
eric@26 300
eric@26 301 o->name = strdup (name);
eric@26 302 if (! o->name)
eric@26 303 {
eric@26 304 fprintf (stderr, "can't strdup output filename '%s'\n", name);
eric@26 305 free (o);
eric@10 306 return (0);
eric@10 307 }
eric@26 308
eric@133 309 o->pdf = pdf_create (name);
eric@26 310 if (! o->pdf)
eric@26 311 {
eric@26 312 fprintf (stderr, "can't open output file '%s'\n", name);
eric@26 313 free (o->name);
eric@26 314 free (o);
eric@26 315 return (0);
eric@26 316 }
eric@26 317
eric@30 318 if (attributes->author)
eric@62 319 pdf_set_author (o->pdf, attributes->author);
eric@30 320 if (attributes->creator)
eric@62 321 pdf_set_creator (o->pdf, attributes->creator);
eric@30 322 if (attributes->title)
eric@62 323 pdf_set_title (o->pdf, attributes->title);
eric@30 324 if (attributes->subject)
eric@62 325 pdf_set_subject (o->pdf, attributes->subject);
eric@30 326 if (attributes->keywords)
eric@62 327 pdf_set_keywords (o->pdf, attributes->keywords);
eric@30 328
eric@26 329 /* prepend new output file onto list */
eric@26 330 o->next = output_files;
eric@26 331 output_files = o;
eric@26 332
eric@26 333 out = o;
eric@10 334 return (1);
eric@10 335 }
eric@10 336
eric@10 337
eric@42 338 /* frees original! */
eric@42 339 static Bitmap *resize_bitmap (Bitmap *src,
eric@92 340 double x_resolution,
eric@92 341 double y_resolution,
eric@36 342 input_attributes_t input_attributes)
eric@32 343 {
eric@32 344 Rect src_rect;
eric@42 345 Point dest_min;
eric@42 346 Bitmap *dest;
eric@32 347
eric@42 348 int width_pixels = input_attributes.page_size.width * x_resolution;
eric@42 349 int height_pixels = input_attributes.page_size.height * y_resolution;
eric@42 350
eric@42 351 src_rect.min.x = (rect_width (& src->rect) - width_pixels) / 2;
eric@42 352 src_rect.min.y = (rect_height (& src->rect) - height_pixels) / 2;
eric@42 353 src_rect.max.x = src_rect.min.x + width_pixels;
eric@42 354 src_rect.max.y = src_rect.min.y + height_pixels;
eric@36 355
eric@42 356 dest_min.x = 0;
eric@42 357 dest_min.y = 0;
eric@32 358
eric@43 359 dest = bitblt (src, & src_rect, NULL, & dest_min, TF_SRC, 0);
eric@42 360 free_bitmap (src);
eric@42 361 return (dest);
eric@42 362 }
eric@32 363
eric@42 364
eric@42 365 /* "in place" rotation */
eric@42 366 static void rotate_bitmap (Bitmap *src,
eric@42 367 input_attributes_t input_attributes)
eric@42 368 {
eric@36 369 switch (input_attributes.rotation)
eric@32 370 {
eric@42 371 case 0: break;
eric@42 372 case 90: rot_90 (src); break;
eric@42 373 case 180: rot_180 (src); break;
eric@42 374 case 270: rot_270 (src); break;
eric@32 375 default:
eric@32 376 fprintf (stderr, "rotation must be 0, 90, 180, or 270\n");
eric@32 377 }
eric@32 378 }
eric@32 379
eric@32 380
eric@32 381 #define SWAP(type,a,b) do { type temp; temp = a; a = b; b = temp; } while (0)
eric@32 382
eric@49 383
eric@131 384 static pdf_page_handle process_tiff_page (int image, /* range 1 .. n */
eric@131 385 input_attributes_t input_attributes)
eric@10 386 {
eric@48 387 uint32_t image_length, image_width;
eric@48 388 uint32_t dest_image_length, dest_image_width;
eric@10 389 #ifdef CHECK_DEPTH
eric@48 390 uint32_t image_depth;
eric@10 391 #endif
eric@29 392
eric@48 393 uint16_t samples_per_pixel;
eric@48 394 uint16_t bits_per_sample;
eric@48 395 uint16_t planar_config;
eric@32 396
eric@48 397 uint16_t resolution_unit;
eric@94 398 float x_resolution, y_resolution;
eric@92 399 double dest_x_resolution, dest_y_resolution;
eric@32 400
eric@62 401 double width_points, height_points; /* really 1/72 inch units rather than
eric@62 402 points */
eric@28 403
eric@42 404 Rect rect;
eric@74 405 Bitmap *bitmap = NULL;
eric@42 406
eric@32 407 int row;
eric@10 408
eric@131 409 pdf_page_handle page = NULL;
eric@28 410
eric@139 411 if (! TIFFSetDirectory (tiff_in, image - 1))
eric@10 412 {
eric@10 413 fprintf (stderr, "can't find page %d of input file\n", image);
eric@10 414 goto fail;
eric@10 415 }
eric@139 416 if (1 != TIFFGetField (tiff_in, TIFFTAG_IMAGELENGTH, & image_length))
eric@10 417 {
eric@10 418 fprintf (stderr, "can't get image length\n");
eric@10 419 goto fail;
eric@10 420 }
eric@139 421 if (1 != TIFFGetField (tiff_in, TIFFTAG_IMAGEWIDTH, & image_width))
eric@10 422 {
eric@10 423 fprintf (stderr, "can't get image width\n");
eric@10 424 goto fail;
eric@10 425 }
eric@29 426
eric@139 427 if (1 != TIFFGetField (tiff_in, TIFFTAG_SAMPLESPERPIXEL, & samples_per_pixel))
eric@29 428 {
eric@29 429 fprintf (stderr, "can't get samples per pixel\n");
eric@29 430 goto fail;
eric@29 431 }
eric@29 432
eric@10 433 #ifdef CHECK_DEPTH
eric@139 434 if (1 != TIFFGetField (tiff_in, TIFFTAG_IMAGEDEPTH, & image_depth))
eric@10 435 {
eric@10 436 fprintf (stderr, "can't get image depth\n");
eric@10 437 goto fail;
eric@10 438 }
eric@10 439 #endif
eric@10 440
eric@139 441 if (1 != TIFFGetField (tiff_in, TIFFTAG_BITSPERSAMPLE, & bits_per_sample))
eric@10 442 {
eric@10 443 fprintf (stderr, "can't get bits per sample\n");
eric@10 444 goto fail;
eric@10 445 }
eric@10 446
eric@139 447 if (1 != TIFFGetField (tiff_in, TIFFTAG_PLANARCONFIG, & planar_config))
eric@10 448 planar_config = 1;
eric@10 449
eric@139 450 if (1 != TIFFGetField (tiff_in, TIFFTAG_RESOLUTIONUNIT, & resolution_unit))
eric@10 451 resolution_unit = 2;
eric@139 452 if (1 != TIFFGetField (tiff_in, TIFFTAG_XRESOLUTION, & x_resolution))
eric@10 453 x_resolution = 300;
eric@139 454 if (1 != TIFFGetField (tiff_in, TIFFTAG_YRESOLUTION, & y_resolution))
eric@10 455 y_resolution = 300;
eric@10 456
eric@29 457 if (samples_per_pixel != 1)
eric@29 458 {
eric@29 459 fprintf (stderr, "samples per pixel %u, must be 1\n", samples_per_pixel);
eric@29 460 goto fail;
eric@29 461 }
eric@29 462
eric@10 463 #ifdef CHECK_DEPTH
eric@10 464 if (image_depth != 1)
eric@10 465 {
eric@10 466 fprintf (stderr, "image depth %u, must be 1\n", image_depth);
eric@10 467 goto fail;
eric@10 468 }
eric@10 469 #endif
eric@10 470
eric@10 471 if (bits_per_sample != 1)
eric@10 472 {
eric@10 473 fprintf (stderr, "bits per sample %u, must be 1\n", bits_per_sample);
eric@10 474 goto fail;
eric@10 475 }
eric@10 476
eric@10 477 if (planar_config != 1)
eric@10 478 {
eric@10 479 fprintf (stderr, "planar config %u, must be 1\n", planar_config);
eric@10 480 goto fail;
eric@10 481 }
eric@10 482
eric@36 483 if (input_attributes.has_resolution)
eric@32 484 {
eric@36 485 x_resolution = input_attributes.x_resolution;
eric@36 486 y_resolution = input_attributes.y_resolution;
eric@32 487 }
eric@32 488
eric@32 489 if ((input_attributes.rotation == 90) || (input_attributes.rotation == 270))
eric@32 490 {
eric@32 491 dest_image_width = image_length;
eric@32 492 dest_image_length = image_width;
eric@32 493 dest_x_resolution = y_resolution;
eric@32 494 dest_y_resolution = x_resolution;
eric@62 495 SWAP (double, width_points, height_points); /* $$$ not yet set!!! */
eric@32 496 }
eric@32 497 else
eric@32 498 {
eric@32 499 dest_image_width = image_width;
eric@32 500 dest_image_length = image_length;
eric@32 501 dest_x_resolution = x_resolution;
eric@32 502 dest_y_resolution = y_resolution;
eric@32 503 }
eric@32 504
eric@42 505 rect.min.x = 0;
eric@42 506 rect.min.y = 0;
eric@42 507 rect.max.x = image_width;
eric@42 508 rect.max.y = image_length;
eric@42 509
eric@42 510 bitmap = create_bitmap (& rect);
eric@42 511
eric@42 512 if (! bitmap)
eric@10 513 {
eric@32 514 fprintf (stderr, "can't allocate bitmap\n");
eric@10 515 goto fail;
eric@10 516 }
eric@10 517
eric@10 518 for (row = 0; row < image_length; row++)
eric@139 519 if (1 != TIFFReadScanline (tiff_in,
eric@43 520 bitmap->bits + row * bitmap->row_words,
eric@32 521 row,
eric@32 522 0))
eric@32 523 {
eric@32 524 fprintf (stderr, "can't read TIFF scanline\n");
eric@32 525 goto fail;
eric@32 526 }
eric@28 527
eric@47 528 #ifdef TIFF_REVERSE_BITS
eric@48 529 reverse_bits ((uint8_t *) bitmap->bits,
eric@108 530 image_length * bitmap->row_words * sizeof (word_t));
eric@47 531 #endif /* TIFF_REVERSE_BITS */
eric@47 532
eric@94 533 #if 0
eric@46 534 if (input_attributes.has_page_size)
eric@46 535 bitmap = resize_bitmap (bitmap,
eric@46 536 x_resolution,
eric@46 537 y_resolution,
eric@46 538 input_attributes);
eric@94 539 #endif
eric@42 540
eric@42 541 rotate_bitmap (bitmap,
eric@42 542 input_attributes);
eric@28 543
eric@42 544 width_points = (rect_width (& bitmap->rect) / dest_x_resolution) * POINTS_PER_INCH;
eric@42 545 height_points = (rect_height (& bitmap->rect) / dest_y_resolution) * POINTS_PER_INCH;
eric@36 546
eric@36 547 if ((height_points > PAGE_MAX_POINTS) || (width_points > PAGE_MAX_POINTS))
eric@36 548 {
eric@36 549 fprintf (stdout, "image too large (max %d inches on a side\n", PAGE_MAX_INCHES);
eric@36 550 goto fail;
eric@36 551 }
eric@36 552
eric@62 553 page = pdf_new_page (out->pdf, width_points, height_points);
eric@28 554
eric@121 555 #if 0
eric@121 556 pdf_write_text (page);
eric@121 557 #else
eric@62 558 pdf_write_g4_fax_image (page,
eric@66 559 0, 0, /* x, y */
eric@66 560 width_points, height_points,
eric@62 561 bitmap,
eric@62 562 0, /* ImageMask */
eric@66 563 0, 0, 0, /* r, g, b */
eric@62 564 0); /* BlackIs1 */
eric@121 565 #endif
eric@62 566
eric@131 567 if (bitmap)
eric@131 568 free_bitmap (bitmap);
eric@131 569 return (page);
eric@10 570
eric@10 571 fail:
eric@74 572 if (bitmap)
eric@74 573 free_bitmap (bitmap);
eric@74 574
eric@131 575 return (NULL);
eric@10 576 }
eric@10 577
eric@10 578
eric@131 579 pdf_page_handle process_jpeg_page (int image, /* range 1 .. n */
eric@131 580 input_attributes_t input_attributes)
eric@108 581 {
eric@108 582 pdf_page_handle page;
eric@139 583 double width_points, height_points; /* really 1/72 inch units rather than
eric@139 584 points */
eric@108 585
eric@139 586 /* $$$ need to get these from somewhere else, hardcoded for now */
eric@139 587 width_points = 4 * 72.0;
eric@139 588 height_points = 4 * 72.0;
eric@108 589
eric@108 590 page = pdf_new_page (out->pdf, width_points, height_points);
eric@108 591
eric@108 592 pdf_write_jpeg_image (page,
eric@108 593 0, 0, /* x, y */
eric@108 594 width_points, height_points,
eric@139 595 in);
eric@108 596
eric@131 597 return (page);
eric@108 598 }
eric@108 599
eric@108 600
eric@108 601 bool process_page (int image, /* range 1 .. n */
eric@108 602 input_attributes_t input_attributes,
eric@131 603 bookmark_t *bookmarks,
eric@131 604 page_label_t *page_label)
eric@108 605 {
eric@131 606 pdf_page_handle page;
eric@131 607
eric@139 608 switch (in_type)
eric@139 609 {
eric@139 610 case INPUT_FILE_TYPE_TIFF:
eric@139 611 page = process_tiff_page (image, input_attributes);
eric@139 612 break;
eric@139 613 case INPUT_FILE_TYPE_JPEG:
eric@139 614 page = process_jpeg_page (image, input_attributes);
eric@139 615 break;
eric@139 616 default:
eric@139 617 fatal (3, "internal error: bad input file type\n");
eric@139 618 }
eric@108 619
eric@131 620 while (bookmarks)
eric@131 621 {
eric@131 622 /* $$$ need to handle level here */
eric@131 623 pdf_new_bookmark (NULL, bookmarks->name, 0, page);
eric@131 624 bookmarks = bookmarks->next;
eric@131 625 }
eric@108 626
eric@131 627 if (page_label)
eric@131 628 pdf_new_page_label (out->pdf,
eric@131 629 page_label->page_index,
eric@131 630 page_label->base,
eric@131 631 page_label->count,
eric@131 632 page_label->style,
eric@131 633 page_label->prefix);
eric@131 634
eric@131 635 return (page != NULL);
eric@108 636 }
eric@108 637
eric@108 638
eric@74 639 #define MAX_BOOKMARK_NAME_LEN 500
eric@74 640
eric@74 641
eric@74 642 static int filename_length_without_suffix (char *in_fn)
eric@74 643 {
eric@74 644 char *p;
eric@74 645 int len = strlen (in_fn);
eric@74 646
eric@74 647 p = strrchr (in_fn, '.');
eric@74 648 if (p && ((strcasecmp (p, ".tif") == 0) ||
eric@74 649 (strcasecmp (p, ".tiff") == 0)))
eric@74 650 return (p - in_fn);
eric@74 651 return (len);
eric@74 652 }
eric@74 653
eric@74 654
eric@74 655 /* $$$ this function should ensure that it doesn't overflow the name string! */
eric@74 656 static void generate_bookmark_name (char *name,
eric@74 657 char *bookmark_fmt,
eric@74 658 char *in_fn,
eric@74 659 int page)
eric@74 660 {
eric@74 661 bool meta = 0;
eric@74 662 int len;
eric@74 663
eric@74 664 while (*bookmark_fmt)
eric@74 665 {
eric@74 666 if (meta)
eric@74 667 {
eric@74 668 meta = 0;
eric@74 669 switch (*bookmark_fmt)
eric@74 670 {
eric@74 671 case '%':
eric@74 672 *(name++) = '%';
eric@74 673 break;
eric@74 674 case 'F':
eric@74 675 len = filename_length_without_suffix (in_fn);
eric@74 676 strncpy (name, in_fn, len);
eric@74 677 name += len;
eric@74 678 break;
eric@74 679 case 'p':
eric@74 680 sprintf (name, "%d", page);
eric@74 681 name += strlen (name);
eric@74 682 break;
eric@74 683 default:
eric@74 684 break;
eric@74 685 }
eric@74 686 }
eric@74 687 else
eric@74 688 switch (*bookmark_fmt)
eric@74 689 {
eric@74 690 case '%':
eric@74 691 meta = 1;
eric@74 692 break;
eric@74 693 default:
eric@74 694 *(name++) = *bookmark_fmt;
eric@74 695 }
eric@74 696 bookmark_fmt++;
eric@74 697 }
eric@116 698 *name = '\0';
eric@74 699 }
eric@74 700
eric@74 701
eric@74 702 void main_args (char *out_fn,
eric@74 703 int inf_count,
eric@74 704 char **in_fn,
eric@74 705 char *bookmark_fmt)
eric@49 706 {
eric@49 707 int i, ip;
eric@49 708 input_attributes_t input_attributes;
eric@49 709 pdf_file_attributes_t output_attributes;
eric@74 710 bookmark_t bookmark;
eric@74 711 char bookmark_name [MAX_BOOKMARK_NAME_LEN];
eric@74 712
eric@74 713 bookmark.next = NULL;
eric@74 714 bookmark.level = 1;
eric@74 715 bookmark.name = & bookmark_name [0];
eric@49 716
eric@49 717 memset (& input_attributes, 0, sizeof (input_attributes));
eric@49 718 memset (& output_attributes, 0, sizeof (output_attributes));
eric@49 719
eric@49 720 if (! open_pdf_output_file (out_fn, & output_attributes))
eric@49 721 fatal (3, "error opening output file \"%s\"\n", out_fn);
eric@49 722 for (i = 0; i < inf_count; i++)
eric@49 723 {
eric@139 724 if (! open_input_file (in_fn [i]))
eric@49 725 fatal (3, "error opening input file \"%s\"\n", in_fn [i]);
eric@49 726 for (ip = 1;; ip++)
eric@49 727 {
eric@62 728 fprintf (stderr, "processing page %d of file \"%s\"\r", ip, in_fn [i]);
eric@74 729 if (bookmark_fmt)
eric@74 730 generate_bookmark_name (& bookmark_name [0],
eric@74 731 bookmark_fmt,
eric@74 732 in_fn [i],
eric@74 733 ip);
eric@74 734 if (! process_page (ip, input_attributes,
eric@131 735 bookmark_fmt ? & bookmark : NULL,
eric@131 736 NULL))
eric@49 737 fatal (3, "error processing page %d of input file \"%s\"\n", ip, in_fn [i]);
eric@139 738 if (last_input_page ())
eric@49 739 break;
eric@49 740 }
eric@49 741 if (verbose)
eric@49 742 fprintf (stderr, "processed %d pages of input file \"%s\"\n", ip, in_fn [i]);
eric@139 743 if (! close_input_file ())
eric@49 744 fatal (3, "error closing input file \"%s\"\n", in_fn [i]);
eric@49 745 }
eric@49 746 if (! close_pdf_output_files ())
eric@49 747 fatal (3, "error closing output file \"%s\"\n", out_fn);
eric@49 748 }
eric@49 749
eric@49 750
eric@134 751 void main_control (char *control_fn)
eric@49 752 {
eric@134 753 if (! parse_control_file (control_fn))
eric@134 754 fatal (2, "error parsing control file\n");
eric@134 755 if (! process_controls ())
eric@134 756 fatal (3, "error processing control file\n");
eric@49 757 }
eric@49 758
eric@49 759
eric@10 760 int main (int argc, char *argv[])
eric@10 761 {
eric@134 762 char *control_fn = NULL;
eric@49 763 char *out_fn = NULL;
eric@62 764 char *bookmark_fmt = NULL;
eric@49 765 int inf_count = 0;
eric@49 766 char *in_fn [MAX_INPUT_FILES];
eric@49 767
eric@49 768 progname = argv [0];
eric@10 769
eric@62 770 pdf_init ();
eric@10 771
eric@49 772 while (--argc)
eric@10 773 {
eric@49 774 if (argv [1][0] == '-')
eric@49 775 {
eric@49 776 if (strcmp (argv [1], "-v") == 0)
eric@49 777 verbose++;
eric@49 778 else if (strcmp (argv [1], "-o") == 0)
eric@49 779 {
eric@49 780 if (argc)
eric@49 781 {
eric@49 782 argc--;
eric@49 783 argv++;
eric@49 784 out_fn = argv [1];
eric@49 785 }
eric@49 786 else
eric@49 787 fatal (1, "missing filename after \"-o\" option\n");
eric@49 788 }
eric@134 789 else if (strcmp (argv [1], "-c") == 0)
eric@49 790 {
eric@49 791 if (argc)
eric@49 792 {
eric@49 793 argc--;
eric@49 794 argv++;
eric@134 795 control_fn = argv [1];
eric@49 796 }
eric@49 797 else
eric@49 798 fatal (1, "missing filename after \"-s\" option\n");
eric@49 799 }
eric@62 800 else if (strcmp (argv [1], "-b") == 0)
eric@62 801 {
eric@62 802 if (argc)
eric@62 803 {
eric@62 804 argc--;
eric@62 805 argv++;
eric@62 806 bookmark_fmt = argv [1];
eric@62 807 }
eric@62 808 else
eric@62 809 fatal (1, "missing format string after \"-b\" option\n");
eric@62 810 }
eric@49 811 else
eric@49 812 fatal (1, "unrecognized option \"%s\"\n", argv [1]);
eric@49 813 }
eric@49 814 else if (inf_count < MAX_INPUT_FILES)
eric@49 815 in_fn [inf_count++] = argv [1];
eric@49 816 else
eric@49 817 fatal (1, "exceeded maximum of %d input files\n", MAX_INPUT_FILES);
eric@49 818 argv++;
eric@10 819 }
eric@10 820
eric@134 821 if (! ((! out_fn) ^ (! control_fn)))
eric@134 822 fatal (1, "either a control file or an output file (but not both) must be specified\n");
eric@49 823
eric@49 824 if (out_fn && ! inf_count)
eric@49 825 fatal (1, "no input files specified\n");
eric@26 826
eric@134 827 if (control_fn && inf_count)
eric@134 828 fatal (1, "if control file is provided, input files can't be specified as arguments\n");
eric@49 829
eric@134 830 if (control_fn)
eric@134 831 main_control (control_fn);
eric@49 832 else
eric@74 833 main_args (out_fn, inf_count, in_fn, bookmark_fmt);
eric@17 834
eric@139 835 close_input_file ();
eric@26 836 close_pdf_output_files ();
eric@49 837 exit (0);
eric@10 838 }