semantics.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 160
1f793b71ffff
permissions
-rw-r--r--

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 */