semantics.c

Fri, 14 Mar 2003 08:24:37 +0000

author
eric
date
Fri, 14 Mar 2003 08:24:37 +0000
changeset 131
4b8c80d77f76
parent 130
d47b66e1722f
child 134
313aba417199
permissions
-rw-r--r--

finished implementing page labels.

     1 /*
     2  * tumble: build a PDF file from image files
     3  *
     4  * Semantic routines for spec file parser
     5  * $Id: semantics.c,v 1.21 2003/03/14 00:24:37 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_spec_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_specs (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 TIFF file '%s'\n", input_fn);
   671 	  if (! open_tiff_input_file (input_fn))
   672 	    {
   673 	      fprintf (stderr, "error opening TIFF 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 }