Thu, 20 Mar 2003 07:06:35 +0000
*** empty log message ***
1 /*
2 * tumble: build a PDF file from image files
3 *
4 * Semantic routines for spec file parser
5 * $Id: semantics.c,v 1.23 2003/03/19 07:39:55 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 <stdbool.h>
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
31 #include "semantics.h"
32 #include "parser.tab.h"
33 #include "tumble.h"
36 typedef struct
37 {
38 bool has_page_size;
39 page_size_t page_size;
41 bool has_rotation;
42 int rotation;
44 bool has_crop;
45 crop_t crop;
46 } input_modifiers_t;
49 typedef struct input_context_t
50 {
51 struct input_context_t *parent;
52 struct input_context_t *next;
54 int image_count; /* how many pages reference this context,
55 including those from subcontexts */
57 char *input_file;
59 input_modifiers_t modifiers [INPUT_MODIFIER_TYPE_COUNT];
60 } input_context_t;
63 typedef struct input_image_t
64 {
65 struct input_image_t *next;
66 input_context_t *input_context;
67 range_t range;
68 } input_image_t;
71 typedef struct output_context_t
72 {
73 struct output_context_t *parent;
74 struct output_context_t *next;
76 int page_count; /* how many pages reference this context,
77 including those from subcontexts */
79 char *output_file;
80 pdf_file_attributes_t file_attributes;
82 bookmark_t *first_bookmark;
83 bookmark_t *last_bookmark;
85 bool has_page_label;
86 page_label_t page_label;
87 } output_context_t;
90 typedef struct output_page_t
91 {
92 struct output_page_t *next;
93 output_context_t *output_context;
94 range_t range;
95 bookmark_t *bookmark_list;
96 } output_page_t;
99 #undef SEMANTIC_DEBUG
101 #ifdef SEMANTIC_DEBUG
102 #define SDBG(x) printf x
103 #else
104 #define SDBG(x)
105 #endif
108 FILE *yyin;
109 int line; /* line number in spec file */
111 int bookmark_level;
113 input_context_t *first_input_context;
114 input_context_t *last_input_context;
116 input_modifier_type_t current_modifier_context;
118 input_image_t *first_input_image;
119 input_image_t *last_input_image;
121 output_context_t *first_output_context;
122 output_context_t *last_output_context;
124 output_page_t *first_output_page;
125 output_page_t *last_output_page;
128 void input_push_context (void)
129 {
130 input_context_t *new_input_context;
132 new_input_context = malloc (sizeof (input_context_t));
133 if (! new_input_context)
134 {
135 fprintf (stderr, "failed to malloc an input context\n");
136 return;
137 }
139 if (last_input_context)
140 {
141 memcpy (new_input_context, last_input_context, sizeof (input_context_t));
142 new_input_context->image_count = 0;
143 }
144 else
145 {
146 memset (new_input_context, 0, sizeof (input_context_t));
147 first_input_context = new_input_context;
148 }
150 new_input_context->parent = last_input_context;
151 last_input_context = new_input_context;
152 };
154 void input_pop_context (void)
155 {
156 if (! last_input_context)
157 {
158 fprintf (stderr, "failed to pop an input context\n");
159 return;
160 }
162 last_input_context = last_input_context->parent;
163 };
165 void input_set_modifier_context (input_modifier_type_t type)
166 {
167 current_modifier_context = type;
168 #ifdef SEMANTIC_DEBUG
169 SDBG(("modifier type "));
170 switch (type)
171 {
172 case INPUT_MODIFIER_ALL: SDBG(("all")); break;
173 case INPUT_MODIFIER_ODD: SDBG(("odd")); break;
174 case INPUT_MODIFIER_EVEN: SDBG(("even")); break;
175 default: SDBG(("unknown %d", type));
176 }
177 SDBG(("\n"));
178 #endif /* SEMANTIC_DEBUG */
179 }
181 static void input_clone (void)
182 {
183 input_context_t *new_input_context;
185 if (! last_input_context->image_count)
186 return;
188 new_input_context = malloc (sizeof (input_context_t));
189 if (! new_input_context)
190 {
191 fprintf (stderr, "failed to malloc an input context\n");
192 return;
193 }
195 memcpy (new_input_context, last_input_context, sizeof (input_context_t));
196 new_input_context->image_count = 0;
197 last_input_context->next = new_input_context;
198 last_input_context = new_input_context;
199 }
201 void input_set_file (char *name)
202 {
203 input_clone ();
204 last_input_context->input_file = name;
205 };
207 void input_set_rotation (int rotation)
208 {
209 last_input_context->modifiers [current_modifier_context].has_rotation = 1;
210 last_input_context->modifiers [current_modifier_context].rotation = rotation;
211 SDBG(("rotation %d\n", rotation));
212 }
214 void input_set_page_size (page_size_t size)
215 {
216 last_input_context->modifiers [current_modifier_context].has_page_size = 1;
217 last_input_context->modifiers [current_modifier_context].page_size = size;
218 SDBG(("page size %f, %f\n", size.width, size.height));
219 }
221 static void increment_input_image_count (int count)
222 {
223 input_context_t *context;
225 for (context = last_input_context; context; context = context->parent)
226 context->image_count += count;
227 }
229 void input_images (range_t range)
230 {
231 input_image_t *new_image;
232 int count = ((range.last - range.first) + 1);
234 #ifdef SEMANTIC_DEBUG
235 if (range.first == range.last)
236 SDBG(("image %d\n", range.first));
237 else
238 SDBG(("images %d..%d\n", range.first, range.last));
239 #endif /* SEMANTIC_DEBUG */
241 new_image = calloc (1, sizeof (input_image_t));
242 if (! new_image)
243 {
244 fprintf (stderr, "failed to malloc an input image struct\n");
245 return;
246 }
247 if (first_input_image)
248 {
249 last_input_image->next = new_image;
250 last_input_image = new_image;
251 }
252 else
253 {
254 first_input_image = last_input_image = new_image;
255 }
256 new_image->range = range;
257 new_image->input_context = last_input_context;
258 increment_input_image_count (count);
259 }
262 void output_push_context (void)
263 {
264 output_context_t *new_output_context;
266 new_output_context = malloc (sizeof (output_context_t));
267 if (! new_output_context)
268 {
269 fprintf (stderr, "failed to malloc an output context\n");
270 return;
271 }
273 if (last_output_context)
274 {
275 memcpy (new_output_context, last_output_context, sizeof (output_context_t));
276 new_output_context->page_count = 0;
277 new_output_context->first_bookmark = NULL;
278 new_output_context->last_bookmark = NULL;
279 }
280 else
281 {
282 memset (new_output_context, 0, sizeof (output_context_t));
283 first_output_context = new_output_context;
284 }
286 new_output_context->parent = last_output_context;
287 last_output_context = new_output_context;
288 };
290 void output_pop_context (void)
291 {
292 if (! last_output_context)
293 {
294 fprintf (stderr, "failed to pop an output context\n");
295 return;
296 }
298 last_output_context = last_output_context->parent;
299 };
301 static void output_clone (void)
302 {
303 output_context_t *new_output_context;
305 if (! last_output_context->page_count)
306 return;
308 new_output_context = malloc (sizeof (output_context_t));
309 if (! new_output_context)
310 {
311 fprintf (stderr, "failed to malloc an output context\n");
312 return;
313 }
315 memcpy (new_output_context, last_output_context, sizeof (output_context_t));
316 new_output_context->page_count = 0;
317 last_output_context->next = new_output_context;
318 }
320 void output_set_file (char *name)
321 {
322 output_clone ();
323 last_output_context->output_file = name;
324 last_output_context->file_attributes.author = NULL;
325 last_output_context->file_attributes.creator = NULL;
326 last_output_context->file_attributes.title = NULL;
327 last_output_context->file_attributes.subject = NULL;
328 last_output_context->file_attributes.keywords = NULL;
329 };
331 void output_set_author (char *author)
332 {
333 last_output_context->file_attributes.author = author;
334 }
336 void output_set_creator (char *creator)
337 {
338 last_output_context->file_attributes.creator = creator;
339 }
341 void output_set_title (char *title)
342 {
343 last_output_context->file_attributes.title = title;
344 }
346 void output_set_subject (char *subject)
347 {
348 last_output_context->file_attributes.subject = subject;
349 }
351 void output_set_keywords (char *keywords)
352 {
353 last_output_context->file_attributes.keywords = keywords;
354 }
356 void output_set_bookmark (char *name)
357 {
358 bookmark_t *new_bookmark;
360 /* As the language is defined (parser.y), a bookmark can only appear
361 at the beginning of a context! */
362 if (last_output_context->page_count)
363 {
364 fprintf (stderr, "internal error, bookmark not at beginning of context\n");
365 exit (2);
366 }
368 new_bookmark = calloc (1, sizeof (bookmark_t));
369 if (! new_bookmark)
370 {
371 fprintf (stderr, "failed to calloc a bookmark\n");
372 return;
373 }
375 new_bookmark->level = bookmark_level;
376 new_bookmark->name = name;
377 if (last_output_context->first_bookmark)
378 last_output_context->last_bookmark->next = new_bookmark;
379 else
380 last_output_context->first_bookmark = new_bookmark;
381 last_output_context->last_bookmark = new_bookmark;
382 }
384 void output_set_page_label (page_label_t label)
385 {
386 output_clone ();
387 last_output_context->has_page_label = 1;
388 last_output_context->page_label = label;
389 }
391 static void increment_output_page_count (int count)
392 {
393 output_context_t *context;
395 for (context = last_output_context; context; context = context->parent)
396 context->page_count += count;
397 }
400 void output_pages (range_t range)
401 {
402 output_page_t *new_page;
403 output_context_t *context;
404 int count = ((range.last - range.first) + 1);
406 #ifdef SEMANTIC_DEBUG
407 if (range.first == range.last)
408 SDBG(("page %d\n", range.first));
409 else
410 SDBG(("pages %d..%d\n", range.first, range.last));
411 #endif /* SEMANTIC_DEBUG */
413 new_page = calloc (1, sizeof (output_page_t));
414 if (! new_page)
415 {
416 fprintf (stderr, "failed to malloc an output page struct\n");
417 return;
418 }
419 if (first_output_page)
420 {
421 last_output_page->next = new_page;
422 last_output_page = new_page;
423 }
424 else
425 {
426 first_output_page = last_output_page = new_page;
427 }
428 new_page->range = range;
429 new_page->output_context = last_output_context;
431 /* transfer bookmarks from context(s) to page */
432 for (context = last_output_context; context; context = context->parent)
433 if (context->first_bookmark)
434 {
435 context->last_bookmark->next = new_page->bookmark_list;
436 new_page->bookmark_list = context->first_bookmark;
437 context->first_bookmark = NULL;
438 context->last_bookmark = NULL;
439 }
441 increment_output_page_count (count);
442 }
445 void yyerror (char *s)
446 {
447 fprintf (stderr, "%d: %s\n", line, s);
448 }
451 static char *get_input_filename (input_context_t *context)
452 {
453 for (; context; context = context->parent)
454 if (context->input_file)
455 return (context->input_file);
456 fprintf (stderr, "no input file name found\n");
457 exit (2);
458 }
460 static bool get_input_rotation (input_context_t *context,
461 input_modifier_type_t type,
462 int *rotation)
463 {
464 for (; context; context = context->parent)
465 {
466 if (context->modifiers [type].has_rotation)
467 {
468 * rotation = context->modifiers [type].rotation;
469 return (1);
470 }
471 if (context->modifiers [INPUT_MODIFIER_ALL].has_rotation)
472 {
473 * rotation = context->modifiers [INPUT_MODIFIER_ALL].rotation;
474 return (1);
475 }
476 }
477 return (0); /* default */
478 }
480 static bool get_input_page_size (input_context_t *context,
481 input_modifier_type_t type,
482 page_size_t *page_size)
483 {
484 for (; context; context = context->parent)
485 {
486 if (context->modifiers [type].has_page_size)
487 {
488 * page_size = context->modifiers [type].page_size;
489 return (1);
490 }
491 if (context->modifiers [INPUT_MODIFIER_ALL].has_page_size)
492 {
493 * page_size = context->modifiers [INPUT_MODIFIER_ALL].page_size;
494 return (1);
495 }
496 }
497 return (0); /* default */
498 }
500 static char *get_output_filename (output_context_t *context)
501 {
502 for (; context; context = context->parent)
503 if (context->output_file)
504 return (context->output_file);
505 fprintf (stderr, "no output file found\n");
506 exit (2);
507 }
509 static pdf_file_attributes_t *get_output_file_attributes (output_context_t *context)
510 {
511 for (; context; context = context->parent)
512 if (context->output_file)
513 return (& context->file_attributes);
514 fprintf (stderr, "no output file found\n");
515 exit (2);
516 }
518 static page_label_t *get_output_page_label (output_context_t *context)
519 {
520 for (; context; context = context->parent)
521 if (context->has_page_label)
522 return (& context->page_label);
523 return (NULL); /* default */
524 }
527 #ifdef SEMANTIC_DEBUG
528 void dump_input_tree (void)
529 {
530 input_image_t *image;
531 int i;
533 printf ("input images:\n");
534 for (image = first_input_image; image; image = image->next)
535 for (i = image->range.first; i <= image->range.last; i++)
536 {
537 input_modifier_type_t parity = (i % 2) ? INPUT_MODIFIER_ODD : INPUT_MODIFIER_EVEN;
538 bool has_rotation, has_page_size;
539 int rotation;
540 page_size_t page_size;
542 has_rotation = get_input_rotation (image->input_context,
543 parity,
544 & rotation);
545 has_page_size = get_input_page_size (image->input_context,
546 parity,
547 & page_size);
548 printf ("file '%s' image %d",
549 get_input_filename (image->input_context),
550 i);
551 if (has_rotation)
552 printf (" rotation %d", rotation);
553 if (has_page_size)
554 printf (" size %f, %f", page_size.width, page_size.height);
555 printf ("\n");
556 printf ("context: %08x\n", image->input_context);
557 }
558 }
560 void dump_output_tree (void)
561 {
562 int i;
563 output_page_t *page;
564 bookmark_t *bookmark;
566 printf ("output pages:\n");
567 for (page = first_output_page; page; page = page->next)
568 {
569 if (page->bookmark_list)
570 {
571 for (bookmark = page->bookmark_list; bookmark; bookmark = bookmark->next)
572 printf ("bookmark %d \"%s\"\n", bookmark->level, bookmark->name);
573 }
574 for (i = page->range.first; i <= page->range.last; i++)
575 {
576 page_label_t *label = get_output_page_label (page->output_context);
577 printf ("file \"%s\" ", get_output_filename (page->output_context));
578 if (label)
579 {
580 printf ("label ");
581 if (label->prefix)
582 printf ("\"%s\" ", label->prefix);
583 if (label->style)
584 printf ("'%c' ", label->style);
585 }
586 printf ("page %d\n", i);
587 }
588 }
589 }
590 #endif /* SEMANTIC_DEBUG */
593 static inline int range_count (range_t range)
594 {
595 return ((range.last - range.first) + 1);
596 }
599 bool parse_control_file (char *fn)
600 {
601 bool result = 0;
603 yyin = fopen (fn, "r");
604 if (! yyin)
605 {
606 fprintf (stderr, "can't open spec file '%s'\n", fn);
607 goto fail;
608 }
610 line = 1;
612 input_push_context (); /* create root input context */
613 input_push_context (); /* create inital input context */
615 output_push_context (); /* create root output context */
616 output_push_context (); /* create initial output context */
618 yyparse ();
620 if (first_input_context->image_count != first_output_context->page_count)
621 {
622 fprintf (stderr, "input image count %d != output page count %d\n",
623 first_input_context->image_count,
624 first_output_context->page_count);
625 goto fail;
626 }
628 fprintf (stderr, "%d pages specified\n", first_input_context->image_count);
630 result = 1;
632 #ifdef SEMANTIC_DEBUG
633 dump_input_tree ();
634 dump_output_tree ();
635 #endif /* SEMANTIC_DEBUG */
637 fail:
638 if (yyin)
639 fclose (yyin);
641 return (result);
642 }
645 bool process_controls (void)
646 {
647 input_image_t *image = NULL;
648 output_page_t *page = NULL;
649 int i = 0;
650 int p = 0;
651 int page_index = 0;
652 input_attributes_t input_attributes;
653 input_modifier_type_t parity;
654 page_label_t *page_label;
656 for (;;)
657 {
658 if ((! image) || (i >= range_count (image->range)))
659 {
660 char *input_fn;
661 if (image)
662 image = image->next;
663 else
664 image = first_input_image;
665 if (! image)
666 return (1); /* done */
667 i = 0;
668 input_fn = get_input_filename (image->input_context);
669 if (verbose)
670 fprintf (stderr, "opening input file '%s'\n", input_fn);
671 if (! open_input_file (input_fn))
672 {
673 fprintf (stderr, "error opening input file '%s'\n", input_fn);
674 return (0);
675 }
676 }
678 if ((! page) || (p >= range_count (page->range)))
679 {
680 char *output_fn;
681 if (page)
682 page = page->next;
683 else
684 page = first_output_page;
685 p = 0;
686 output_fn = get_output_filename (page->output_context);
687 if (verbose)
688 fprintf (stderr, "opening PDF file '%s'\n", output_fn);
689 if (! open_pdf_output_file (output_fn,
690 get_output_file_attributes (page->output_context)))
691 {
692 fprintf (stderr, "error opening PDF file '%s'\n", output_fn);
693 return (0);
694 }
695 }
697 parity = ((image->range.first + i) % 2) ? INPUT_MODIFIER_ODD : INPUT_MODIFIER_EVEN;
699 memset (& input_attributes, 0, sizeof (input_attributes));
701 input_attributes.rotation = 0;
702 input_attributes.has_rotation = get_input_rotation (image->input_context,
703 parity,
704 & input_attributes.rotation);
706 input_attributes.has_page_size = get_input_page_size (image->input_context,
707 parity,
708 & input_attributes.page_size);
710 if (verbose)
711 fprintf (stderr, "processing image %d\n", image->range.first + i);
713 if (p)
714 page_label = NULL;
715 else
716 {
717 page_label = get_output_page_label (page->output_context);
718 if (page_label)
719 {
720 page_label->page_index = page_index;
721 page_label->base = page->range.first;
722 page_label->count = range_count (page->range);
723 }
724 }
726 if (! process_page (image->range.first + i,
727 input_attributes,
728 p ? NULL : page->bookmark_list,
729 page_label))
730 {
731 fprintf (stderr, "error processing image %d\n", image->range.first + i);
732 return (0);
733 }
734 i++;
735 p++;
736 page_index++;
737 }
738 }