Mon, 14 Dec 2009 16:18:21 +0000
remove erroneous 0.33-philpem1 tag
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"
33 #ifdef CTL_LANG
34 #include "parser.tab.h"
35 #endif
37 #include "tumble.h"
40 typedef struct
41 {
42 bool has_page_size;
43 page_size_t page_size;
45 bool has_rotation;
46 int rotation;
48 bool has_crop;
49 crop_t crop;
50 } input_modifiers_t;
53 typedef struct input_context_t
54 {
55 struct input_context_t *parent;
56 struct input_context_t *next;
58 int image_count; /* how many pages reference this context,
59 including those from subcontexts */
61 char *input_file;
63 input_modifiers_t modifiers [INPUT_MODIFIER_TYPE_COUNT];
64 } input_context_t;
67 typedef struct input_image_t
68 {
69 struct input_image_t *next;
70 input_context_t *input_context;
71 range_t range;
72 } input_image_t;
75 typedef struct output_context_t
76 {
77 struct output_context_t *parent;
78 struct output_context_t *next;
80 int page_count; /* how many pages reference this context,
81 including those from subcontexts */
83 char *output_file;
84 pdf_file_attributes_t file_attributes;
86 bookmark_t *first_bookmark;
87 bookmark_t *last_bookmark;
89 bool has_page_label;
90 page_label_t page_label;
91 } output_context_t;
94 typedef struct output_page_t
95 {
96 struct output_page_t *next;
97 output_context_t *output_context;
98 range_t range;
99 bookmark_t *bookmark_list;
100 } output_page_t;
103 #undef SEMANTIC_DEBUG
105 #ifdef SEMANTIC_DEBUG
106 #define SDBG(x) printf x
107 #else
108 #define SDBG(x)
109 #endif
112 FILE *yyin;
113 int line; /* line number in spec file */
115 int bookmark_level;
117 input_context_t *first_input_context;
118 input_context_t *last_input_context;
120 input_modifier_type_t current_modifier_context;
122 input_image_t *first_input_image;
123 input_image_t *last_input_image;
125 output_context_t *first_output_context;
126 output_context_t *last_output_context;
128 output_page_t *first_output_page;
129 output_page_t *last_output_page;
132 void input_push_context (void)
133 {
134 input_context_t *new_input_context;
136 new_input_context = malloc (sizeof (input_context_t));
137 if (! new_input_context)
138 {
139 fprintf (stderr, "failed to malloc an input context\n");
140 return;
141 }
143 if (last_input_context)
144 {
145 memcpy (new_input_context, last_input_context, sizeof (input_context_t));
146 new_input_context->image_count = 0;
147 }
148 else
149 {
150 memset (new_input_context, 0, sizeof (input_context_t));
151 first_input_context = new_input_context;
152 }
154 new_input_context->parent = last_input_context;
155 last_input_context = new_input_context;
156 };
158 void input_pop_context (void)
159 {
160 if (! last_input_context)
161 {
162 fprintf (stderr, "failed to pop an input context\n");
163 return;
164 }
166 last_input_context = last_input_context->parent;
167 };
169 void input_set_modifier_context (input_modifier_type_t type)
170 {
171 current_modifier_context = type;
172 #ifdef SEMANTIC_DEBUG
173 SDBG(("modifier type "));
174 switch (type)
175 {
176 case INPUT_MODIFIER_ALL: SDBG(("all")); break;
177 case INPUT_MODIFIER_ODD: SDBG(("odd")); break;
178 case INPUT_MODIFIER_EVEN: SDBG(("even")); break;
179 default: SDBG(("unknown %d", type));
180 }
181 SDBG(("\n"));
182 #endif /* SEMANTIC_DEBUG */
183 }
185 static void input_clone (void)
186 {
187 input_context_t *new_input_context;
189 if (! last_input_context->image_count)
190 return;
192 new_input_context = malloc (sizeof (input_context_t));
193 if (! new_input_context)
194 {
195 fprintf (stderr, "failed to malloc an input context\n");
196 return;
197 }
199 memcpy (new_input_context, last_input_context, sizeof (input_context_t));
200 new_input_context->image_count = 0;
201 last_input_context->next = new_input_context;
202 last_input_context = new_input_context;
203 }
205 void input_set_file (char *name)
206 {
207 input_clone ();
208 last_input_context->input_file = name;
209 };
211 void input_set_rotation (int rotation)
212 {
213 last_input_context->modifiers [current_modifier_context].has_rotation = 1;
214 last_input_context->modifiers [current_modifier_context].rotation = rotation;
215 SDBG(("rotation %d\n", rotation));
216 }
218 void input_set_page_size (page_size_t size)
219 {
220 last_input_context->modifiers [current_modifier_context].has_page_size = 1;
221 last_input_context->modifiers [current_modifier_context].page_size = size;
222 SDBG(("page size %f, %f\n", size.width, size.height));
223 }
225 static void increment_input_image_count (int count)
226 {
227 input_context_t *context;
229 for (context = last_input_context; context; context = context->parent)
230 context->image_count += count;
231 }
233 void input_images (range_t range)
234 {
235 input_image_t *new_image;
236 int count = ((range.last - range.first) + 1);
238 #ifdef SEMANTIC_DEBUG
239 if (range.first == range.last)
240 SDBG(("image %d\n", range.first));
241 else
242 SDBG(("images %d..%d\n", range.first, range.last));
243 #endif /* SEMANTIC_DEBUG */
245 new_image = calloc (1, sizeof (input_image_t));
246 if (! new_image)
247 {
248 fprintf (stderr, "failed to malloc an input image struct\n");
249 return;
250 }
251 if (first_input_image)
252 {
253 last_input_image->next = new_image;
254 last_input_image = new_image;
255 }
256 else
257 {
258 first_input_image = last_input_image = new_image;
259 }
260 new_image->range = range;
261 new_image->input_context = last_input_context;
262 increment_input_image_count (count);
263 }
266 void output_push_context (void)
267 {
268 output_context_t *new_output_context;
270 new_output_context = malloc (sizeof (output_context_t));
271 if (! new_output_context)
272 {
273 fprintf (stderr, "failed to malloc an output context\n");
274 return;
275 }
277 if (last_output_context)
278 {
279 memcpy (new_output_context, last_output_context, sizeof (output_context_t));
280 new_output_context->page_count = 0;
281 new_output_context->first_bookmark = NULL;
282 new_output_context->last_bookmark = NULL;
283 }
284 else
285 {
286 memset (new_output_context, 0, sizeof (output_context_t));
287 first_output_context = new_output_context;
288 }
290 new_output_context->parent = last_output_context;
291 last_output_context = new_output_context;
292 };
294 void output_pop_context (void)
295 {
296 if (! last_output_context)
297 {
298 fprintf (stderr, "failed to pop an output context\n");
299 return;
300 }
302 last_output_context = last_output_context->parent;
303 };
305 static void output_clone (void)
306 {
307 output_context_t *new_output_context;
309 if (! last_output_context->page_count)
310 return;
312 new_output_context = malloc (sizeof (output_context_t));
313 if (! new_output_context)
314 {
315 fprintf (stderr, "failed to malloc an output context\n");
316 return;
317 }
319 memcpy (new_output_context, last_output_context, sizeof (output_context_t));
320 new_output_context->page_count = 0;
321 last_output_context->next = new_output_context;
322 }
324 void output_set_file (char *name)
325 {
326 output_clone ();
327 last_output_context->output_file = name;
328 last_output_context->file_attributes.author = NULL;
329 last_output_context->file_attributes.creator = NULL;
330 last_output_context->file_attributes.title = NULL;
331 last_output_context->file_attributes.subject = NULL;
332 last_output_context->file_attributes.keywords = NULL;
333 };
335 void output_set_author (char *author)
336 {
337 last_output_context->file_attributes.author = author;
338 }
340 void output_set_creator (char *creator)
341 {
342 last_output_context->file_attributes.creator = creator;
343 }
345 void output_set_title (char *title)
346 {
347 last_output_context->file_attributes.title = title;
348 }
350 void output_set_subject (char *subject)
351 {
352 last_output_context->file_attributes.subject = subject;
353 }
355 void output_set_keywords (char *keywords)
356 {
357 last_output_context->file_attributes.keywords = keywords;
358 }
360 void output_set_bookmark (char *name)
361 {
362 bookmark_t *new_bookmark;
364 /* As the language is defined (parser.y), a bookmark can only appear
365 at the beginning of a context! */
366 if (last_output_context->page_count)
367 {
368 fprintf (stderr, "internal error, bookmark not at beginning of context\n");
369 exit (2);
370 }
372 new_bookmark = calloc (1, sizeof (bookmark_t));
373 if (! new_bookmark)
374 {
375 fprintf (stderr, "failed to calloc a bookmark\n");
376 return;
377 }
379 new_bookmark->level = bookmark_level;
380 new_bookmark->name = name;
381 if (last_output_context->first_bookmark)
382 last_output_context->last_bookmark->next = new_bookmark;
383 else
384 last_output_context->first_bookmark = new_bookmark;
385 last_output_context->last_bookmark = new_bookmark;
386 }
388 void output_set_page_label (page_label_t label)
389 {
390 output_clone ();
391 last_output_context->has_page_label = 1;
392 last_output_context->page_label = label;
393 }
395 static void increment_output_page_count (int count)
396 {
397 output_context_t *context;
399 for (context = last_output_context; context; context = context->parent)
400 context->page_count += count;
401 }
404 void output_pages (range_t range)
405 {
406 output_page_t *new_page;
407 output_context_t *context;
408 int count = ((range.last - range.first) + 1);
410 #ifdef SEMANTIC_DEBUG
411 if (range.first == range.last)
412 SDBG(("page %d\n", range.first));
413 else
414 SDBG(("pages %d..%d\n", range.first, range.last));
415 #endif /* SEMANTIC_DEBUG */
417 new_page = calloc (1, sizeof (output_page_t));
418 if (! new_page)
419 {
420 fprintf (stderr, "failed to malloc an output page struct\n");
421 return;
422 }
423 if (first_output_page)
424 {
425 last_output_page->next = new_page;
426 last_output_page = new_page;
427 }
428 else
429 {
430 first_output_page = last_output_page = new_page;
431 }
432 new_page->range = range;
433 new_page->output_context = last_output_context;
435 /* transfer bookmarks from context(s) to page */
436 for (context = last_output_context; context; context = context->parent)
437 if (context->first_bookmark)
438 {
439 context->last_bookmark->next = new_page->bookmark_list;
440 new_page->bookmark_list = context->first_bookmark;
441 context->first_bookmark = NULL;
442 context->last_bookmark = NULL;
443 }
445 increment_output_page_count (count);
446 }
449 void yyerror (char *s)
450 {
451 fprintf (stderr, "%d: %s\n", line, s);
452 }
455 static char *get_input_filename (input_context_t *context)
456 {
457 for (; context; context = context->parent)
458 if (context->input_file)
459 return (context->input_file);
460 fprintf (stderr, "no input file name found\n");
461 exit (2);
462 }
464 static bool get_input_rotation (input_context_t *context,
465 input_modifier_type_t type,
466 int *rotation)
467 {
468 for (; context; context = context->parent)
469 {
470 if (context->modifiers [type].has_rotation)
471 {
472 * rotation = context->modifiers [type].rotation;
473 return (1);
474 }
475 if (context->modifiers [INPUT_MODIFIER_ALL].has_rotation)
476 {
477 * rotation = context->modifiers [INPUT_MODIFIER_ALL].rotation;
478 return (1);
479 }
480 }
481 return (0); /* default */
482 }
484 static bool get_input_page_size (input_context_t *context,
485 input_modifier_type_t type,
486 page_size_t *page_size)
487 {
488 for (; context; context = context->parent)
489 {
490 if (context->modifiers [type].has_page_size)
491 {
492 * page_size = context->modifiers [type].page_size;
493 return (1);
494 }
495 if (context->modifiers [INPUT_MODIFIER_ALL].has_page_size)
496 {
497 * page_size = context->modifiers [INPUT_MODIFIER_ALL].page_size;
498 return (1);
499 }
500 }
501 return (0); /* default */
502 }
504 static char *get_output_filename (output_context_t *context)
505 {
506 for (; context; context = context->parent)
507 if (context->output_file)
508 return (context->output_file);
509 fprintf (stderr, "no output file found\n");
510 exit (2);
511 }
513 static pdf_file_attributes_t *get_output_file_attributes (output_context_t *context)
514 {
515 for (; context; context = context->parent)
516 if (context->output_file)
517 return (& context->file_attributes);
518 fprintf (stderr, "no output file found\n");
519 exit (2);
520 }
522 static page_label_t *get_output_page_label (output_context_t *context)
523 {
524 for (; context; context = context->parent)
525 if (context->has_page_label)
526 return (& context->page_label);
527 return (NULL); /* default */
528 }
531 #ifdef SEMANTIC_DEBUG
532 void dump_input_tree (void)
533 {
534 input_image_t *image;
535 int i;
537 printf ("input images:\n");
538 for (image = first_input_image; image; image = image->next)
539 for (i = image->range.first; i <= image->range.last; i++)
540 {
541 input_modifier_type_t parity = (i % 2) ? INPUT_MODIFIER_ODD : INPUT_MODIFIER_EVEN;
542 bool has_rotation, has_page_size;
543 int rotation;
544 page_size_t page_size;
546 has_rotation = get_input_rotation (image->input_context,
547 parity,
548 & rotation);
549 has_page_size = get_input_page_size (image->input_context,
550 parity,
551 & page_size);
552 printf ("file '%s' image %d",
553 get_input_filename (image->input_context),
554 i);
555 if (has_rotation)
556 printf (" rotation %d", rotation);
557 if (has_page_size)
558 printf (" size %f, %f", page_size.width, page_size.height);
559 printf ("\n");
560 printf ("context: %08x\n", image->input_context);
561 }
562 }
564 void dump_output_tree (void)
565 {
566 int i;
567 output_page_t *page;
568 bookmark_t *bookmark;
570 printf ("output pages:\n");
571 for (page = first_output_page; page; page = page->next)
572 {
573 if (page->bookmark_list)
574 {
575 for (bookmark = page->bookmark_list; bookmark; bookmark = bookmark->next)
576 printf ("bookmark %d \"%s\"\n", bookmark->level, bookmark->name);
577 }
578 for (i = page->range.first; i <= page->range.last; i++)
579 {
580 page_label_t *label = get_output_page_label (page->output_context);
581 printf ("file \"%s\" ", get_output_filename (page->output_context));
582 if (label)
583 {
584 printf ("label ");
585 if (label->prefix)
586 printf ("\"%s\" ", label->prefix);
587 if (label->style)
588 printf ("'%c' ", label->style);
589 }
590 printf ("page %d\n", i);
591 }
592 }
593 }
594 #endif /* SEMANTIC_DEBUG */
597 static inline int range_count (range_t range)
598 {
599 return ((range.last - range.first) + 1);
600 }
603 #ifdef CTL_LANG
604 bool parse_control_file (char *fn)
605 {
606 bool result = 0;
608 yyin = fopen (fn, "r");
609 if (! yyin)
610 {
611 fprintf (stderr, "can't open spec file '%s'\n", fn);
612 goto fail;
613 }
615 line = 1;
617 input_push_context (); /* create root input context */
618 input_push_context (); /* create inital input context */
620 output_push_context (); /* create root output context */
621 output_push_context (); /* create initial output context */
623 yyparse ();
625 if (first_input_context->image_count != first_output_context->page_count)
626 {
627 fprintf (stderr, "input image count %d != output page count %d\n",
628 first_input_context->image_count,
629 first_output_context->page_count);
630 goto fail;
631 }
633 fprintf (stderr, "%d pages specified\n", first_input_context->image_count);
635 result = 1;
637 #ifdef SEMANTIC_DEBUG
638 dump_input_tree ();
639 dump_output_tree ();
640 #endif /* SEMANTIC_DEBUG */
642 fail:
643 if (yyin)
644 fclose (yyin);
646 return (result);
647 }
650 bool process_controls (void)
651 {
652 input_image_t *image = NULL;
653 output_page_t *page = NULL;
654 int i = 0;
655 int p = 0;
656 int page_index = 0;
657 input_attributes_t input_attributes;
658 input_modifier_type_t parity;
659 page_label_t *page_label;
661 for (;;)
662 {
663 if ((! image) || (i >= range_count (image->range)))
664 {
665 char *input_fn;
666 if (image)
667 image = image->next;
668 else
669 image = first_input_image;
670 if (! image)
671 return (1); /* done */
672 i = 0;
673 input_fn = get_input_filename (image->input_context);
674 if (verbose)
675 fprintf (stderr, "opening input file '%s'\n", input_fn);
676 if (! open_input_file (input_fn))
677 {
678 fprintf (stderr, "error opening input file '%s'\n", input_fn);
679 return (0);
680 }
681 }
683 if ((! page) || (p >= range_count (page->range)))
684 {
685 char *output_fn;
686 if (page)
687 page = page->next;
688 else
689 page = first_output_page;
690 p = 0;
691 output_fn = get_output_filename (page->output_context);
692 if (verbose)
693 fprintf (stderr, "opening PDF file '%s'\n", output_fn);
694 if (! open_pdf_output_file (output_fn,
695 get_output_file_attributes (page->output_context)))
696 {
697 fprintf (stderr, "error opening PDF file '%s'\n", output_fn);
698 return (0);
699 }
700 }
702 parity = ((image->range.first + i) % 2) ? INPUT_MODIFIER_ODD : INPUT_MODIFIER_EVEN;
704 memset (& input_attributes, 0, sizeof (input_attributes));
706 input_attributes.rotation = 0;
707 input_attributes.has_rotation = get_input_rotation (image->input_context,
708 parity,
709 & input_attributes.rotation);
711 input_attributes.has_page_size = get_input_page_size (image->input_context,
712 parity,
713 & input_attributes.page_size);
715 if (verbose)
716 fprintf (stderr, "processing image %d\n", image->range.first + i);
718 if (p)
719 page_label = NULL;
720 else
721 {
722 page_label = get_output_page_label (page->output_context);
723 if (page_label)
724 {
725 page_label->page_index = page_index;
726 page_label->base = page->range.first;
727 page_label->count = range_count (page->range);
728 }
729 }
731 if (! process_page (image->range.first + i,
732 input_attributes,
733 p ? NULL : page->bookmark_list,
734 page_label))
735 {
736 fprintf (stderr, "error processing image %d\n", image->range.first + i);
737 return (0);
738 }
739 i++;
740 p++;
741 page_index++;
742 }
743 }
744 #endif /* CTL_LANG */