Thu, 13 Mar 2003 07:59:10 +0000
don't use page mode USE_OUTLINES if there are no outline entries.
1 /*
2 * t2p: Create a PDF file from the contents of one or more TIFF
3 * bilevel image files. The images in the resulting PDF file
4 * will be compressed using ITU-T T.6 (G4) fax encoding.
5 *
6 * Semantic routines for spec file parser
7 * $Id: semantics.c,v 1.18 2003/03/12 00:38:04 eric Exp $
8 * Copyright 2001, 2002, 2003 Eric Smith <eric@brouhaha.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation. Note that permission is
13 * not granted to redistribute this program under the terms of any
14 * other version of the General Public License.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
24 */
27 #include <stdbool.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdio.h>
33 #include "semantics.h"
34 #include "parser.tab.h"
35 #include "t2p.h"
38 typedef struct
39 {
40 bool has_page_size;
41 page_size_t page_size;
43 bool has_rotation;
44 int rotation;
46 bool has_crop;
47 crop_t crop;
48 } input_modifiers_t;
51 typedef struct input_context_t
52 {
53 struct input_context_t *parent;
54 struct input_context_t *next;
56 int image_count; /* how many pages reference this context,
57 including those from subcontexts */
59 char *input_file;
61 input_modifiers_t modifiers [INPUT_MODIFIER_TYPE_COUNT];
62 } input_context_t;
65 typedef struct input_image_t
66 {
67 struct input_image_t *next;
68 input_context_t *input_context;
69 range_t range;
70 } input_image_t;
73 typedef struct output_context_t
74 {
75 struct output_context_t *parent;
76 struct output_context_t *next;
78 int page_count; /* how many pages reference this context,
79 including those from subcontexts */
81 char *output_file;
82 pdf_file_attributes_t file_attributes;
84 bookmark_t *first_bookmark;
85 bookmark_t *last_bookmark;
87 bool has_page_label;
88 page_label_t page_label;
89 } output_context_t;
92 typedef struct output_page_t
93 {
94 struct output_page_t *next;
95 output_context_t *output_context;
96 range_t range;
97 bookmark_t *bookmark_list;
98 } output_page_t;
101 #undef SEMANTIC_DEBUG
103 #ifdef SEMANTIC_DEBUG
104 #define SDBG(x) printf x
105 #else
106 #define SDBG(x)
107 #endif
110 FILE *yyin;
111 int line; /* line number in spec file */
113 int bookmark_level;
115 input_context_t *first_input_context;
116 input_context_t *last_input_context;
118 input_modifier_type_t current_modifier_context;
120 input_image_t *first_input_image;
121 input_image_t *last_input_image;
123 output_context_t *first_output_context;
124 output_context_t *last_output_context;
126 output_page_t *first_output_page;
127 output_page_t *last_output_page;
130 void input_push_context (void)
131 {
132 input_context_t *new_input_context;
134 new_input_context = malloc (sizeof (input_context_t));
135 if (! new_input_context)
136 {
137 fprintf (stderr, "failed to malloc an input context\n");
138 return;
139 }
141 if (last_input_context)
142 {
143 memcpy (new_input_context, last_input_context, sizeof (input_context_t));
144 new_input_context->image_count = 0;
145 }
146 else
147 {
148 memset (new_input_context, 0, sizeof (input_context_t));
149 first_input_context = new_input_context;
150 }
152 new_input_context->parent = last_input_context;
153 last_input_context = new_input_context;
154 };
156 void input_pop_context (void)
157 {
158 if (! last_input_context)
159 {
160 fprintf (stderr, "failed to pop an input context\n");
161 return;
162 }
164 last_input_context = last_input_context->parent;
165 };
167 void input_set_modifier_context (input_modifier_type_t type)
168 {
169 current_modifier_context = type;
170 #ifdef SEMANTIC_DEBUG
171 SDBG(("modifier type "));
172 switch (type)
173 {
174 case INPUT_MODIFIER_ALL: SDBG(("all")); break;
175 case INPUT_MODIFIER_ODD: SDBG(("odd")); break;
176 case INPUT_MODIFIER_EVEN: SDBG(("even")); break;
177 default: SDBG(("unknown %d", type));
178 }
179 SDBG(("\n"));
180 #endif /* SEMANTIC_DEBUG */
181 }
183 static void input_clone (void)
184 {
185 input_context_t *new_input_context;
187 if (! last_input_context->image_count)
188 return;
190 new_input_context = malloc (sizeof (input_context_t));
191 if (! new_input_context)
192 {
193 fprintf (stderr, "failed to malloc an input context\n");
194 return;
195 }
197 memcpy (new_input_context, last_input_context, sizeof (input_context_t));
198 new_input_context->image_count = 0;
199 last_input_context->next = new_input_context;
200 last_input_context = new_input_context;
201 }
203 void input_set_file (char *name)
204 {
205 input_clone ();
206 last_input_context->input_file = name;
207 };
209 void input_set_rotation (int rotation)
210 {
211 last_input_context->modifiers [current_modifier_context].has_rotation = 1;
212 last_input_context->modifiers [current_modifier_context].rotation = rotation;
213 SDBG(("rotation %d\n", rotation));
214 }
216 void input_set_page_size (page_size_t size)
217 {
218 last_input_context->modifiers [current_modifier_context].has_page_size = 1;
219 last_input_context->modifiers [current_modifier_context].page_size = size;
220 SDBG(("page size %f, %f\n", size.width, size.height));
221 }
223 static void increment_input_image_count (int count)
224 {
225 input_context_t *context;
227 for (context = last_input_context; context; context = context->parent)
228 context->image_count += count;
229 }
231 void input_images (range_t range)
232 {
233 input_image_t *new_image;
234 int count = ((range.last - range.first) + 1);
236 #ifdef SEMANTIC_DEBUG
237 if (range.first == range.last)
238 SDBG(("image %d\n", range.first));
239 else
240 SDBG(("images %d..%d\n", range.first, range.last));
241 #endif /* SEMANTIC_DEBUG */
243 new_image = calloc (1, sizeof (input_image_t));
244 if (! new_image)
245 {
246 fprintf (stderr, "failed to malloc an input image struct\n");
247 return;
248 }
249 if (first_input_image)
250 {
251 last_input_image->next = new_image;
252 last_input_image = new_image;
253 }
254 else
255 {
256 first_input_image = last_input_image = new_image;
257 }
258 new_image->range = range;
259 new_image->input_context = last_input_context;
260 increment_input_image_count (count);
261 }
264 void output_push_context (void)
265 {
266 output_context_t *new_output_context;
268 new_output_context = malloc (sizeof (output_context_t));
269 if (! new_output_context)
270 {
271 fprintf (stderr, "failed to malloc an output context\n");
272 return;
273 }
275 if (last_output_context)
276 {
277 memcpy (new_output_context, last_output_context, sizeof (output_context_t));
278 new_output_context->page_count = 0;
279 new_output_context->first_bookmark = NULL;
280 new_output_context->last_bookmark = NULL;
281 }
282 else
283 {
284 memset (new_output_context, 0, sizeof (output_context_t));
285 first_output_context = new_output_context;
286 }
288 new_output_context->parent = last_output_context;
289 last_output_context = new_output_context;
290 };
292 void output_pop_context (void)
293 {
294 if (! last_output_context)
295 {
296 fprintf (stderr, "failed to pop an output context\n");
297 return;
298 }
300 last_output_context = last_output_context->parent;
301 };
303 static void output_clone (void)
304 {
305 output_context_t *new_output_context;
307 if (! last_output_context->page_count)
308 return;
310 new_output_context = malloc (sizeof (output_context_t));
311 if (! new_output_context)
312 {
313 fprintf (stderr, "failed to malloc an output context\n");
314 return;
315 }
317 memcpy (new_output_context, last_output_context, sizeof (output_context_t));
318 new_output_context->page_count = 0;
319 last_output_context->next = new_output_context;
320 }
322 void output_set_file (char *name)
323 {
324 output_clone ();
325 last_output_context->output_file = name;
326 last_output_context->file_attributes.author = NULL;
327 last_output_context->file_attributes.creator = NULL;
328 last_output_context->file_attributes.title = NULL;
329 last_output_context->file_attributes.subject = NULL;
330 last_output_context->file_attributes.keywords = NULL;
331 };
333 void output_set_author (char *author)
334 {
335 last_output_context->file_attributes.author = author;
336 }
338 void output_set_creator (char *creator)
339 {
340 last_output_context->file_attributes.creator = creator;
341 }
343 void output_set_title (char *title)
344 {
345 last_output_context->file_attributes.title = title;
346 }
348 void output_set_subject (char *subject)
349 {
350 last_output_context->file_attributes.subject = subject;
351 }
353 void output_set_keywords (char *keywords)
354 {
355 last_output_context->file_attributes.keywords = keywords;
356 }
358 void output_set_bookmark (char *name)
359 {
360 bookmark_t *new_bookmark;
362 /* As the language is defined (parser.y), a bookmark can only appear
363 at the beginning of a context! */
364 if (last_output_context->page_count)
365 {
366 fprintf (stderr, "internal error, bookmark not at beginning of context\n");
367 exit (2);
368 }
370 new_bookmark = calloc (1, sizeof (bookmark_t));
371 if (! new_bookmark)
372 {
373 fprintf (stderr, "failed to calloc a bookmark\n");
374 return;
375 }
377 new_bookmark->level = bookmark_level;
378 new_bookmark->name = name;
379 if (last_output_context->first_bookmark)
380 last_output_context->last_bookmark->next = new_bookmark;
381 else
382 last_output_context->first_bookmark = new_bookmark;
383 last_output_context->last_bookmark = new_bookmark;
384 }
386 void output_set_page_label (page_label_t label)
387 {
388 output_clone ();
389 last_output_context->has_page_label = 1;
390 last_output_context->page_label = label;
391 }
393 static void increment_output_page_count (int count)
394 {
395 output_context_t *context;
397 for (context = last_output_context; context; context = context->parent)
398 context->page_count += count;
399 }
402 void output_pages (range_t range)
403 {
404 output_page_t *new_page;
405 output_context_t *context;
406 int count = ((range.last - range.first) + 1);
408 #ifdef SEMANTIC_DEBUG
409 if (range.first == range.last)
410 SDBG(("page %d\n", range.first));
411 else
412 SDBG(("pages %d..%d\n", range.first, range.last));
413 #endif /* SEMANTIC_DEBUG */
415 new_page = calloc (1, sizeof (output_page_t));
416 if (! new_page)
417 {
418 fprintf (stderr, "failed to malloc an output page struct\n");
419 return;
420 }
421 if (first_output_page)
422 {
423 last_output_page->next = new_page;
424 last_output_page = new_page;
425 }
426 else
427 {
428 first_output_page = last_output_page = new_page;
429 }
430 new_page->range = range;
431 new_page->output_context = last_output_context;
433 /* transfer bookmarks from context(s) to page */
434 for (context = last_output_context; context; context = context->parent)
435 if (context->first_bookmark)
436 {
437 context->last_bookmark->next = new_page->bookmark_list;
438 new_page->bookmark_list = context->first_bookmark;
439 context->first_bookmark = NULL;
440 context->last_bookmark = NULL;
441 }
443 increment_output_page_count (count);
444 }
447 void yyerror (char *s)
448 {
449 fprintf (stderr, "%d: %s\n", line, s);
450 }
453 static char *get_input_filename (input_context_t *context)
454 {
455 for (; context; context = context->parent)
456 if (context->input_file)
457 return (context->input_file);
458 fprintf (stderr, "no input file name found\n");
459 exit (2);
460 }
462 static bool get_input_rotation (input_context_t *context,
463 input_modifier_type_t type,
464 int *rotation)
465 {
466 for (; context; context = context->parent)
467 {
468 if (context->modifiers [type].has_rotation)
469 {
470 * rotation = context->modifiers [type].rotation;
471 return (1);
472 }
473 if (context->modifiers [INPUT_MODIFIER_ALL].has_rotation)
474 {
475 * rotation = context->modifiers [INPUT_MODIFIER_ALL].rotation;
476 return (1);
477 }
478 }
479 return (0); /* default */
480 }
482 static bool get_input_page_size (input_context_t *context,
483 input_modifier_type_t type,
484 page_size_t *page_size)
485 {
486 for (; context; context = context->parent)
487 {
488 if (context->modifiers [type].has_page_size)
489 {
490 * page_size = context->modifiers [type].page_size;
491 return (1);
492 }
493 if (context->modifiers [INPUT_MODIFIER_ALL].has_page_size)
494 {
495 * page_size = context->modifiers [INPUT_MODIFIER_ALL].page_size;
496 return (1);
497 }
498 }
499 return (0); /* default */
500 }
502 static char *get_output_filename (output_context_t *context)
503 {
504 for (; context; context = context->parent)
505 if (context->output_file)
506 return (context->output_file);
507 fprintf (stderr, "no output file found\n");
508 exit (2);
509 }
511 static pdf_file_attributes_t *get_output_file_attributes (output_context_t *context)
512 {
513 for (; context; context = context->parent)
514 if (context->output_file)
515 return (& context->file_attributes);
516 fprintf (stderr, "no output file found\n");
517 exit (2);
518 }
520 static page_label_t *get_output_page_label (output_context_t *context)
521 {
522 for (; context; context = context->parent)
523 if (context->has_page_label)
524 return (& context->page_label);
525 return (NULL); /* default */
526 }
529 #ifdef SEMANTIC_DEBUG
530 void dump_input_tree (void)
531 {
532 input_image_t *image;
533 int i;
535 printf ("input images:\n");
536 for (image = first_input_image; image; image = image->next)
537 for (i = image->range.first; i <= image->range.last; i++)
538 {
539 input_modifier_type_t parity = (i % 2) ? INPUT_MODIFIER_ODD : INPUT_MODIFIER_EVEN;
540 bool has_rotation, has_page_size;
541 int rotation;
542 page_size_t page_size;
544 has_rotation = get_input_rotation (image->input_context,
545 parity,
546 & rotation);
547 has_page_size = get_input_page_size (image->input_context,
548 parity,
549 & page_size);
550 printf ("file '%s' image %d",
551 get_input_filename (image->input_context),
552 i);
553 if (has_rotation)
554 printf (" rotation %d", rotation);
555 if (has_page_size)
556 printf (" size %f, %f", page_size.width, page_size.height);
557 printf ("\n");
558 printf ("context: %08x\n", image->input_context);
559 }
560 }
562 void dump_output_tree (void)
563 {
564 int i;
565 output_page_t *page;
566 bookmark_t *bookmark;
568 printf ("output pages:\n");
569 for (page = first_output_page; page; page = page->next)
570 {
571 if (page->bookmark_list)
572 {
573 for (bookmark = page->bookmark_list; bookmark; bookmark = bookmark->next)
574 printf ("bookmark %d \"%s\"\n", bookmark->level, bookmark->name);
575 }
576 for (i = page->range.first; i <= page->range.last; i++)
577 {
578 page_label_t *label = get_output_page_label (page->output_context);
579 printf ("file \"%s\" ", get_output_filename (page->output_context));
580 if (label)
581 {
582 printf ("label ");
583 if (label->prefix)
584 printf ("\"%s\" ", label->prefix);
585 if (label->style)
586 printf ("'%c' ", label->style);
587 }
588 printf ("page %d\n", i);
589 }
590 }
591 }
592 #endif /* SEMANTIC_DEBUG */
595 static inline int range_count (range_t range)
596 {
597 return ((range.last - range.first) + 1);
598 }
601 bool parse_spec_file (char *fn)
602 {
603 bool result = 0;
605 yyin = fopen (fn, "r");
606 if (! yyin)
607 {
608 fprintf (stderr, "can't open spec file '%s'\n", fn);
609 goto fail;
610 }
612 line = 1;
614 input_push_context (); /* create root input context */
615 input_push_context (); /* create inital input context */
617 output_push_context (); /* create root output context */
618 output_push_context (); /* create initial output context */
620 yyparse ();
622 if (first_input_context->image_count != first_output_context->page_count)
623 {
624 fprintf (stderr, "input image count %d != output page count %d\n",
625 first_input_context->image_count,
626 first_output_context->page_count);
627 goto fail;
628 }
630 fprintf (stderr, "%d pages specified\n", first_input_context->image_count);
632 result = 1;
634 #ifdef SEMANTIC_DEBUG
635 dump_input_tree ();
636 dump_output_tree ();
637 #endif /* SEMANTIC_DEBUG */
639 fail:
640 if (yyin)
641 fclose (yyin);
643 return (result);
644 }
647 bool process_specs (void)
648 {
649 input_image_t *image = NULL;
650 output_page_t *page = NULL;
651 int i = 0;
652 int p = 0;
653 int page_index = 0;
654 input_attributes_t input_attributes;
655 input_modifier_type_t parity;
656 page_label_t *page_label;
658 for (;;)
659 {
660 if ((! image) || (i >= range_count (image->range)))
661 {
662 char *input_fn;
663 if (image)
664 image = image->next;
665 else
666 image = first_input_image;
667 if (! image)
668 return (1); /* done */
669 i = 0;
670 input_fn = get_input_filename (image->input_context);
671 if (verbose)
672 fprintf (stderr, "opening TIFF file '%s'\n", input_fn);
673 if (! open_tiff_input_file (input_fn))
674 {
675 fprintf (stderr, "error opening TIFF file '%s'\n", input_fn);
676 return (0);
677 }
678 }
680 if ((! page) || (p >= range_count (page->range)))
681 {
682 char *output_fn;
683 if (page)
684 page = page->next;
685 else
686 page = first_output_page;
687 p = 0;
688 output_fn = get_output_filename (page->output_context);
689 if (verbose)
690 fprintf (stderr, "opening PDF file '%s'\n", output_fn);
691 if (! open_pdf_output_file (output_fn,
692 get_output_file_attributes (page->output_context)))
693 {
694 fprintf (stderr, "error opening PDF file '%s'\n", output_fn);
695 return (0);
696 }
697 page_label = get_output_page_label (page->output_context);
698 process_page_numbers (page_index,
699 range_count (page->range),
700 page->range.first,
701 page_label);
702 }
704 parity = ((image->range.first + i) % 2) ? INPUT_MODIFIER_ODD : INPUT_MODIFIER_EVEN;
706 memset (& input_attributes, 0, sizeof (input_attributes));
708 input_attributes.rotation = 0;
709 input_attributes.has_rotation = get_input_rotation (image->input_context,
710 parity,
711 & input_attributes.rotation);
713 input_attributes.has_page_size = get_input_page_size (image->input_context,
714 parity,
715 & input_attributes.page_size);
717 if (verbose)
718 fprintf (stderr, "processing image %d\n", image->range.first + i);
719 if (! process_page (image->range.first + i,
720 input_attributes,
721 page->bookmark_list))
722 {
723 fprintf (stderr, "error processing image %d\n", image->range.first + i);
724 return (0);
725 }
726 i++;
727 p++;
728 page_index++;
729 }
730 }