/*
Copyright (c) 2004-2005, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  its contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/

#define DKFIGRD_C	1
#include "dkfig.h"
#include "dkxsp.h"


#line 42 "dkfigrd.ctr"

#ifdef TRACE_DEBUG
#undef TRACE_DEBUG
#endif

#line 47 "dkfigrd.ctr"



typedef struct _rgb_ {
  int r; int g; int b;
} rgb;

/* {{{ low_rgb

  Color palette for colors 0...31
  as calculated from the floating point
  numbers.

*/
static rgb low_rgb[] = {
  /*  0: back */         { 0,0,0 },
  /*  1: blue */         { 0,    0,  255 },
  /*  2: green */        { 0,  255,    0 },
  /*  3: cyan */         { 0,  255,  255 },
  /*  4: red */          { 255,  0,    0 },
  /*  5: magenta */      { 255,  0,  255 },
  /*  6: yellow */       { 255, 255,   0 },
  /*  7: white */        { 255, 255, 255 },
  /*  8: dark blue */    {   0,   0, 143 },
  /*  9: blue */         {   0,   0, 176 },
  /* 10: blue */         {   0,   0, 209 },
  /* 11: blue */         { 135, 207, 255 },
  /* 12: dark green */   {   0, 143,   0 },
  /* 13: green */        {   0, 176,   0 },
  /* 14: green */        {   0, 209,   0 },
  /* 15: dark cyan */    {   0, 143, 143 },
  /* 16: cyan */         {   0, 176, 176 },
  /* 17: cyan */         {   0, 209, 209 },
  /* 18: dark red */     { 143,   0,   0 },
  /* 19: red */          { 176,   0,   0 },
  /* 20: red */          { 209,   0,   0 },
  /* 21: dark magenta */ { 143,   0, 143 },
  /* 22: magenta */      { 176,   0, 176 },
  /* 23: magenta */      { 209,   0, 209 },
  /* 24: dark brown */   { 127,  48,   0 },
  /* 25: brown */        { 160,  63,   0 },
  /* 26: brown */        { 191,  97,   0 },
  /* 27: dark pink */    { 255, 127, 127 },
  /* 28: pink */         { 255, 160, 160 },
  /* 29: pink */         { 255, 191, 191 },
  /* 30: pink */         { 255, 224, 224 },
  /* 31: gold */         { 255, 214,   0 }
};
/* }}} */



/* {{{ web_rgb

  Color palette for colors 0...31.
  We use the Web216 colors nearest to the
  calculation from the floating point numbers.

*/
static rgb web_rgb[] = {
  /*  0: back */         { 0,0,0 },
  /*  1: blue */         { 0,    0,  255 },
  /*  2: green */        { 0,  255,    0 },
  /*  3: cyan */         { 0,  255,  255 },
  /*  4: red */          { 255,  0,    0 },
  /*  5: magenta */      { 255,  0,  255 },
  /*  6: yellow */       { 255, 255,   0 },
  /*  7: white */        { 255, 255, 255 },
  /*  8: dark blue */    {   0,   0, 102 },
  /*  9: blue */         {   0,   0, 153 },
  /* 10: blue */         {   0,   0, 204 },
  /* 11: blue */         { 153, 204, 255 },
  /* 12: dark green */   {   0, 102,   0 },
  /* 13: green */        {   0, 153,   0 },
  /* 14: green */        {   0, 204,   0 },
  /* 15: dark cyan */    {   0, 102, 102 },
  /* 16: cyan */         {   0, 153, 153 },
  /* 17: cyan */         {   0, 204, 204 },
  /* 18: dark red */     { 102,   0,   0 },
  /* 19: red */          { 153,   0,   0 },
  /* 20: red */          { 204,   0,   0 },
  /* 21: dark magenta */ { 102,   0, 102 },
  /* 22: magenta */      { 153,   0, 153 },
  /* 23: magenta */      { 204,   0, 204 },
  /* 24: dark brown */   { 102,  51,   0 },
  /* 25: brown */        { 153,  51,   0 },
  /* 26: brown */        { 204, 102,   0 },
  /* 27: dark pink */    { 255,  51,  51 },
  /* 28: pink */         { 255, 102, 102 },
  /* 29: pink */         { 255, 153, 153 },
  /* 30: pink */         { 255, 204, 204 },
  /* 31: gold */         { 255, 204,   0 }
};
/* }}} */



/* {{{ The list of states */

#define STATE_EMPTY			0
#define STATE_EXPECT_ORIENTATION	1
#define STATE_EXPECT_JUSTIFICATION	2
#define STATE_EXPECT_UNITS		3
#define STATE_EXPECT_PAPERSIZE		4
#define STATE_EXPECT_MAGNIFICATION	5
#define STATE_EXPECT_MULTIPLE_PAGE	6
#define STATE_EXPECT_TRANSPARENT_COLOR	7
#define STATE_EXPECT_RESOLUTION		8
#define STATE_START			9
#define STATE_EXPECT_ARC_ARROW_1	10
#define STATE_EXPECT_ARC_ARROW_2	11
#define STATE_EXPECT_PL_ARROW_1		12
#define STATE_EXPECT_PL_ARROW_2		13
#define STATE_EXPECT_PL_POINTS		14
#define STATE_EXPECT_PL_IMAGENAME	15
#define STATE_EXPECT_SPLINE_ARROW_1	16
#define STATE_EXPECT_SPLINE_ARROW_2	17
#define STATE_EXPECT_SPLINE_POINTS	18
#define STATE_EXPECT_SPLINE_FACTORS	19

/* }}} */



/* {{{ Some text patterns, expected when reading a Fig file */

/* Recognize XFig file format.  */
static char str_fig[] = { "FIG" };
static char str_32[]  = { "3.2" };

/* Orientation */
static char *orientation_strings[] = {
  "Landscape", "Portrait", NULL
};

/* Justification */
static char *justification_strings[] = {
  "Center", "FlushLeft", "Flush Left", NULL
};

/* Units */
static char *unit_strings[] = {
  "Metric", "Inches", NULL
};

/* Paper sizes */
static char *paper_strings[] = {
  "Letter", "Legal", "Ledger", "Tabloid",
  "A", "B", "C", "D", "E", "A4", "A3", "A2", "A1", "A0", "B5",
  NULL
};

/* Multipage or Single page */
static char *multi_strings[] = {
  "Single", "Multiple", NULL
};

/* }}} */



/* {{{ dkfig_object_compare

  Comparison function for building the sorted storage.

*/
int
dkfig_object_compare DK_P3(void *,l, void *,r,int,cr)
{
  int back = 0;
  dk_fig_object *lo, *ro;
  
  if(l) {
    if(r) {
      lo = (dk_fig_object *)l; ro = (dk_fig_object *)r;
      if(lo->layer > ro->layer) back = -1;
      if(lo->layer < ro->layer) back =  1;
      if(!back) {
        if((lo->fpd).ot > (ro->fpd).ot) back =  1;
	if((lo->fpd).ot < (ro->fpd).ot) back = -1;
      }
      if(!back) {
        if(((lo->fpd).ot == DK_FIG_OBJ_TEXT) 
	    && ((ro->fpd).ot == DK_FIG_OBJ_TEXT))
	{
	}
      }
      if(!back) {
        if(((lo->fpd).ls) < ((ro->fpd).ls)) back = -1;
	if(((lo->fpd).ls) > ((ro->fpd).ls)) back =  1;
      }
      if(!back) {
        if(lo->lineno < ro->lineno) back = -1;
	if(lo->lineno > ro->lineno) back =  1;
      }
    } else {
      back = 1;
    }
  } else {
    if(r) {
      back = -1;
    }
  }
  
  return back;
}
/* }}} */



/* {{{ color_cell_compare

  Compare color cells by color number.

*/
int
color_cell_compare DK_P3(void *,l, void *,r,int,cr)
{
  int back = 0, *xr;
  dk_fig_colorcell *ccl, *ccr;
  
  if(l && r) {
    ccl = (dk_fig_colorcell *)l;
    ccr = (dk_fig_colorcell *)r;
    xr  = (int *)r;
    switch(cr) {
      case 1: {
        if((ccl->number) < (*xr)) { back = -1; }
	if((ccl->number) > (*xr)) { back =  1; }
      } break;
      default: {
        if((ccl->number) < (ccr->number)) { back = -1; }
	if((ccl->number) > (ccr->number)) { back =  1; }
      } break;
    }
  } 
  return back;
}
/* }}} */



/* {{{ is_first_line_ok

  Check the first line for #FIG 3.2.

*/
static
int
is_first_line_ok DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 0; char *p1;
  
  p1 = b; p1++;
  p1 = dkstr_start(p1, NULL);
  if(p1) {
    if(strlen(p1) >= 3) {
      if(strncmp(p1, str_fig, 3) == 0) {
        p1 = &(p1[3]);
	p1 = dkstr_start(p1, NULL);
	if(p1) {
	  if(strlen(p1) >= 3) {
	    if(strncmp(p1, str_32, 3) == 0) {
	      back = 1;
	      d->state = STATE_EXPECT_ORIENTATION;
	    }
	  }
	}
      }
    }
  }
  if(!back) {
    d->errc = DKFIG_ERROR_SYNTAX;
  }
  
  return back;
}
/* }}} */



/* {{{ add_spec_comm_to

 Store a special comment either belong to the entire
 drawing or belonging to a drawing primitive.

*/
static
int
add_spec_comm_to DK_P4(dk_fig_drawing *,d,char *,b,\
  dk_storage_t **,s,dk_storage_iterator_t **,i)
{
  int back = 0;
  dk_fig_opt *fo;
  
  if((!(*s)) && (!(*i))) {
    *s = dksto_open(0);
    if(*s) {
      *i = dksto_it_open(*s);
      if(*i) {
        dksto_set_comp(*s, dkfig_opt_compare, 0);
      } else {
        dksto_close(*s); *s = NULL;
      }
    }
  }
  if((*s) && (*i)) {
    fo = dkfig_opt_new(d->lineno, b);
    if(fo) {
      if(dksto_add(*s, (void *)fo)) {
        back = 1;
      } else {
        dkfig_opt_delete(fo); fo = NULL;
	d->errc = DKFIG_ERROR_MEMORY;
      }
    } else {					
      d->errc = DKFIG_ERROR_MEMORY;
    }
  } 
  return back;
}
/* }}} */



/* {{{ add_special_comment

  Handle a special comment. May be either
  document level special comment or related
  to the next object.

*/
static
int
add_special_comment DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 0;
  
  
  
  if(d->state < STATE_START) {
    back = add_spec_comm_to(d,b,&(d->dlsc),&(d->dlsci));
  } else {
    back = add_spec_comm_to(d,b,&(d->nosc),&(d->nosci));
  }
  
  return back;
}
/* }}} */



/* {{{ get_orientation

  Get orientation string (2nd line of file)

*/
static
int
get_orientation DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 1;
  d->orientation = dkstr_array_index(orientation_strings, b, 0);
  if(d->orientation < 0) {
    back = 0; d->errc = DKFIG_ERROR_SYNTAX;
  } else {
    d->state = STATE_EXPECT_JUSTIFICATION;
  }
  return back;
}
/* }}} */



/* {{{ get_justification

  The 3rd line contains the justification.

*/
static
int
get_justification DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 1;
  
  d->justification = dkstr_array_index(justification_strings,b,0);
  if(d->justification < 0) {
    back = 0; d->errc = DKFIG_ERROR_SYNTAX;
  } else {
    d->state = STATE_EXPECT_UNITS;
  }
  
  return back;
}
/* }}} */



/* {{{ get_units

  The 4th line contains the units used in the drawing.

*/
static
int
get_units DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 1;
  
  d->units = dkstr_array_index(unit_strings,b,0);
  if(d->units < 0) {
    back = 0; d->errc = DKFIG_ERROR_SYNTAX;
  } else {
    d->state = STATE_EXPECT_PAPERSIZE;
  }
  
  return back;
}
/* }}} */



/* {{{ get_papersize

  The 5th line contains the paper size.

*/
static
int
get_papersize DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 1;
  
  d->paper = dkstr_array_index(paper_strings,b,0);
  if(d->paper < 0) {
    if(!((d->opt1) & DKFIG_OPT_IGNORE_UNKNOWN_PAPER)) {
      back = 0; d->errc = DKFIG_ERROR_SYNTAX;
    } else {
      d->state = STATE_EXPECT_MAGNIFICATION;
    }
  } else {
    d->state = STATE_EXPECT_MAGNIFICATION;
  }
  
  return back;
}
/* }}} */



/* {{{ get_magnification

  The 6th line contains a magnification factor.

*/
static
int
get_magnification DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 1;
  double f;
  
  if(sscanf(b, "%lf", &f) == 1) {
    d->magnification = f; d->state = STATE_EXPECT_MULTIPLE_PAGE;
  } else {
    back = 0; d->errc = DKFIG_ERROR_SYNTAX;
  }
  
  return back;
}
/* }}} */



/* {{{ get_multipage

  The 7th line tells whether we have a single page
  or multipage drawing.
  How to handle multipage drawings? I don't know.

*/
static
int
get_multipage DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 1;
  
  d->multi = dkstr_array_index(multi_strings, b, 0);
  if(d->multi < 0) {
    d->errc = DKFIG_ERROR_SYNTAX; back = 0;
  } else {
    d->state = STATE_EXPECT_TRANSPARENT_COLOR;
  } 
  return back;
}
/* }}} */



/* {{{ get_transparent_color

  The 8th line contains the number of the transparent color.

*/
static
int
get_transparent_color DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 1; int i;
  
  if(sscanf(b, "%d", &i) == 1) {
    d->transparent = i;
    d->state = STATE_EXPECT_RESOLUTION;
  } else {
    back = 0; d->errc = DKFIG_ERROR_SYNTAX;
  }
  
  return back;
}
/* }}} */



/* {{{ get_resolution

  The 9th line contains the resolution in dots per unit
  (see line 4) and the co-ordinate system setting.
  The FIG format specification tells what the cs setting
  means in one place, in another place there is an advise
  to ignore this setting. This should be corrected in
  the file format specification to make things clear.

*/
static
int
get_resolution DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 0; long l; int i;
  char *nxptr;
  
  nxptr = dkstr_next(b, NULL);
  if(nxptr) {
    if(sscanf(b, "%ld", &l) == 1) {
      if(sscanf(nxptr, "%d", &i) == 1) {
        back = 1; d->state = STATE_START;
	d->resolution = l;
	d->cstype = i;
	d->fres = dkma_l_to_double(l);
      }
    }
  }
  if(!back) { d->errc = DKFIG_ERROR_SYNTAX; }
  
  return back;
}
/* }}} */



/* {{{ dkfig_delete_color_cell

  Destroy color cell data structure.

*/
void
dkfig_delete_color_cell DK_P1(dk_fig_colorcell *,cc)
{
  
  if(cc) { 
    cc->number = cc->r = cc->g = cc->b = 0;
    cc->drve = NULL;
    dk_delete(cc);
  }
  
}
/* }}} */



/* {{{ null_arc

  Initialize data structure for arc.

*/
static
void
null_arc DK_P1(dk_fig_arc *,a)
{
  
  DK_MEMRES(a,sizeof(dk_fig_arc)) ;
  a->centerx = a->centery = 0.0;
  a->lba = a->rba = 0.0;
  dkfig_tool_null_arc_calc(&(a->calc));
  
}
/* }}} */



/* {{{ delete_arc_details

  Destroy data structure for arc.

*/
static
void
delete_arc_details DK_P1(dk_fig_arc *,a)
{
  
  null_arc(a);
  dk_delete(a);
  
}
/* }}} */



/* {{{ null_ellipse

  Initialize data structure for ellipse.

*/
static
void
null_ellipse DK_P1(dk_fig_ellipse *,e)
{
  
  DK_MEMRES(e,sizeof(dk_fig_ellipse)) ;
  e->angle = 0.0;
  
}
/* }}} */



/* {{{ delete_ellipse_details

  Destroy data structure for ellipse.

*/
static
void
delete_ellipse_details DK_P1(dk_fig_ellipse *,e)
{
  
  null_ellipse(e);
  dk_delete(e);
  
}
/* }}} */



/* {{{ null_polyline

  Initialize data structure for polyline.

*/
static
void
null_polyline DK_P1(dk_fig_polyline *,p)
{
  
  DK_MEMRES(p,sizeof(dk_fig_polyline)) ;
  p->xvalues = p->yvalues = NULL;
  p->imagename = NULL;
  
}
/* }}} */



/* {{{ delete_polyline_details

  Destroy data structure for polyline.

*/
static
void
delete_polyline_details DK_P1(dk_fig_polyline *,p)
{
  void *ptr;
  
  if(p->xvalues) { ptr = p->xvalues; dk_delete(ptr); }
  if(p->yvalues) { ptr = p->yvalues; dk_delete(ptr); }
  if(p->imagename) { ptr = p->imagename; dk_delete(ptr); }
  null_polyline(p);
  dk_delete(p);
  
}
/* }}} */



/* {{{ null_spline
 
  Initialize data structure for spline.

*/
static
void
null_spline  DK_P1(dk_fig_spline *,s)
{
  
  DK_MEMRES(s,sizeof(dk_fig_spline)) ;
  s->xvalues = s->yvalues = NULL;
  s->svalues = NULL;
  s->bpoints = NULL;
  
}
/* }}} */



/* {{{ delete_spline_details

  Destroy data structure for spline.

*/
static
void
delete_spline_details DK_P1(dk_fig_spline *,s)
{
  void *ptr;
  
  if(s->xvalues) { ptr = s->xvalues; dk_delete(ptr); }
  if(s->yvalues) { ptr = s->yvalues; dk_delete(ptr); }
  if(s->svalues) { ptr = s->svalues; dk_delete(ptr); }
  null_spline(s);
  dk_delete(s);
  
}
/* }}} */



/* {{{ null_text

  Initialize data structure for text.

*/
static
void
null_text DK_P1(dk_fig_text *,t)
{
  
  DK_MEMRES(t,sizeof(dk_fig_text)) ;
  t->font_size = t->angle = t->height = t->length = 0.0;
  t->text = NULL; t->font_handling = NULL;
  
}
/* }}} */



/* {{{ delete_text_details

  Destroy data structure for text.

*/
static
void
delete_text_details DK_P1(dk_fig_text *,t)
{
  void *ptr;
  
  if(t->text) { ptr = t->text; dk_delete(ptr); }
  null_text(t);
  dk_delete(t);
  
}
/* }}} */



/* {{{ new_text_details
  
  Create data structure for a text.

*/
static
dk_fig_text *
new_text_details DK_P0()
{
  dk_fig_text *back = NULL;
  
  back = dk_new(dk_fig_text,1);
  if(back) {
    null_text(back);
  }
  
  return back;
}
/* }}} */


/* {{{ null_compound

  Initialize compound data structure.

*/
static
void
null_compound DK_P1(dk_fig_compound *,c)
{
  c->objects = NULL; c->objit = NULL;
  c->ulx = c->uly = c->lrx = c->lry = 0L;
}
/* }}} */



static void
dkfig_object_delete DK_PR((dk_fig_object *o));



/* {{{ delete_drawing_details

  Destroy drawing details data structure.

*/
static
int
delete_drawing_details DK_P1(dk_fig_drawing *,d)
{
  int back = 0;
  dk_fig_object *o; dk_fig_colorcell *c;
  dk_fig_opt *fo;
  char *cptr;
  
  if(d) {
    /* contents */
    if((d->objit) && (d->objects)) {
      
      dksto_it_reset(d->objit);
      while((o = (dk_fig_object *)dksto_it_next(d->objit)) != NULL) {
        dkfig_object_delete(o);
      }
      dksto_it_close(d->objit);
      
    }
    if(d->objects) {
      dksto_close(d->objects);
    }
    d->objit = NULL;
    d->objects = NULL;
    if((d->ccells) && (d->ccit)) {
      
      dksto_it_reset(d->ccit);
      while((c = (dk_fig_colorcell *)dksto_it_next(d->ccit)) != NULL) {
        dkfig_delete_color_cell(c);
      }
      dksto_it_close(d->ccit);
      
    }
    if(d->ccells) {
      dksto_close(d->ccells);
    }
    d->ccit = NULL; d->ccells = NULL;
    if((d->dlsc) && (d->dlsci)) {
      
      dksto_it_reset(d->dlsci);
      while((fo = (dk_fig_opt *)dksto_it_next(d->dlsci)) != NULL) {
        dkfig_opt_delete(fo);
      }
      dksto_it_close(d->dlsci);
      
    }
    if(d->dlsc) {
      dksto_close(d->dlsc);
    }
    d->dlsc = NULL; d->dlsci = NULL;
    if((d->nosc) && (d->nosci)) {
      
      dksto_it_reset(d->nosci);
      while((fo = (dk_fig_opt *)dksto_it_next(d->nosci)) != NULL) {
        dkfig_opt_delete(fo);
      }
      dksto_it_close(d->nosci);
      
    }
    if(d->nosc) {
      dksto_close(d->nosc);
    }
    d->nosc = NULL; d->nosci = NULL;
    if((d->fonth) && (d->fonthi)) {
      dk_fig_fonth_t *fhptr;
      
      dksto_it_reset(d->fonthi);
      while((fhptr = (dk_fig_fonth_t *)dksto_it_next(d->fonthi)) != NULL)
      {
        dkfig_fnt_del_fonth(fhptr);
      }
      dksto_it_close(d->fonthi);
      
    }
    if(d->fonth) {
      dksto_close(d->fonth);
    }
    d->fonth = NULL; d->fonthi = NULL;
    /* input file name */
    if(d->inputfilename) {
      
      cptr = d->inputfilename;
      dk_delete(cptr);
    }
    d->inputfilename = NULL;
    d->lineno = 0UL;
    d->currentcomp = NULL;
    d->state = 0;
    d->errc  = 0;
    d->orientation = 0;
    d->justification = 0;
    d->orientation	= 0;
    d->justification	= 0;
    d->units		= 0;
    d->paper		= 0;
    d->multi		= 0;
    d->transparent	= 0;
    d->resolution	= 0L;
    d->fres		= 0.0;
    d->cstype	= 0;
    d->magnification	= 0.0;
    /* finally release the drawing itself */
    dk_delete(d);
  }
  
  return back;
}
/* }}} */



static void
delete_compound_details DK_PR((dk_fig_compound *c));


/* {{{ dkfig_object_delete

  Destroy an object and all it's contents

*/
static
void
dkfig_object_delete DK_P1(dk_fig_object *,o)
{
  dk_fig_opt *fo;
  
  if(o) {				
    if(o->data) {			
      switch(o->objtype) {
        case DK_FIG_OBJ_DRAWING: {	
          delete_drawing_details((dk_fig_drawing *)(o->data));
	} break;
	case DK_FIG_OBJ_COLOR: {	
	} break;
	case DK_FIG_OBJ_ARC: {		
	  delete_arc_details((dk_fig_arc *)(o->data));
	} break;
	case DK_FIG_OBJ_ELLIPSE: {	
	  delete_ellipse_details((dk_fig_ellipse *)(o->data));
	} break;
	case DK_FIG_OBJ_POLYLINE: {	
	  delete_polyline_details((dk_fig_polyline *)(o->data));
	} break;
	case DK_FIG_OBJ_SPLINE: {	
	  delete_spline_details((dk_fig_spline *)(o->data));
	} break;
	case DK_FIG_OBJ_TEXT: {		
	  delete_text_details((dk_fig_text *)(o->data));
	} break;
	case DK_FIG_OBJ_COMPOUND: {	
	  delete_compound_details((dk_fig_compound *)(o->data));
	} break;
	default: {			
	} break;
      }
    } else {				
    }
    if((o->osc) && (o->osci)) {
      
      dksto_it_reset(o->osci);
      while ((fo = (dk_fig_opt *)dksto_it_next(o->osci)) != NULL) {
        dkfig_opt_delete(fo);
      }
      dksto_it_close(o->osci);
      
    }
    if(o->osc) {
      dksto_close(o->osc);
    }
    o->osc = NULL; o->osci = NULL;
    o->lineno = 0UL; o->layer = 0L; o->objtype = -1;
    o->data = NULL; o->drve = NULL; o->parent = NULL;
    dk_delete(o);
  } else {				
  }
  
}
/* }}} */



/* {{{ delete_compound_details

  Delete compound data structure.

*/
static
void
delete_compound_details DK_P1(dk_fig_compound *,c)
{
  dk_fig_object *o;
  
  if((c->objects) && (c->objit)) {
    
    dksto_it_reset(c->objit);
    while((o = (dk_fig_object *)dksto_it_next(c->objit)) != NULL) {
      dkfig_object_delete(o);
    }
    dksto_it_close(c->objit);
    
  }
  if(c->objects) {
    dksto_close(c->objects);
  }
  null_compound(c);
  dk_delete(c);
  
}
/* }}} */



/* {{{

  Create new object. No type specified yet.

*/
dk_fig_object *
dkfig_object_new DK_P4(unsigned long,l,dk_fig_object *,p,\
  dk_storage_t *,st,dk_storage_iterator_t *,it)
{
  dk_fig_object *back = NULL;
  int ok = 0;
  
  back = dk_new(dk_fig_object,1);
  if(back) {
    ok = 1;
    /* initialize all pointers to NULL */
    back->lineno  = l;
    back->layer   = 0L;
    back->objtype = -1;
    back->data    = NULL;
    back->drve	  = NULL;
    back->osc	  = st;
    back->osci	  = it;
    back->parent  = p;
    dkfig_tool_bb_reset(&(back->dbb));
    if(!ok) {
      dkfig_object_delete(back); back = NULL;
    }
  }
  
  return back;
}
/* }}} */



/* {{{ hex_to_int

  Convert hexadecimal character to int.

*/
static
int
hex_to_int DK_P1(char,c)
{
  int back = 0;
  switch(c) {
    case '0': { back = 0; } break;
    case '1': { back = 1; } break;
    case '2': { back = 2; } break;
    case '3': { back = 3; } break;
    case '4': { back = 4; } break;
    case '5': { back = 5; } break;
    case '6': { back = 6; } break;
    case '7': { back = 7; } break;
    case '8': { back = 8; } break;
    case '9': { back = 9; } break;
    case 'a': case 'A': { back = 10; } break;
    case 'b': case 'B': { back = 11; } break;
    case 'c': case 'C': { back = 12; } break;
    case 'd': case 'D': { back = 13; } break;
    case 'e': case 'E': { back = 14; } break;
    case 'f': case 'F': { back = 15; } break;
  }
  return back;
}
/* }}} */



/* {{{ dkfig_new_color_cell

 Create  data structure for a color cell.

*/
dk_fig_colorcell *
dkfig_new_color_cell DK_P4(int,n,int,r,int,g,int,b)
{
  dk_fig_colorcell *back = NULL;
  
  back = dk_new(dk_fig_colorcell,1);
  if(back) {
    back->number = n;
    back->r = r; back->g = g; back->b = b;
    back->drve = NULL;
  }
  
  return back;
}
/* }}} */



/* {{{ add_color_cell

  Create a new color cell from the text line, save
  the color cell in the drawing.

*/
static
int
add_color_cell DK_P2(dk_fig_drawing *,d,char *,t)
{
  int back = 0; int reason = 0; char *cnptr, *hexptr;
  int cellno, r, g, b;
  dk_fig_colorcell *ccptr;
  
  cellno = r = g = b = 0;
  cnptr = t; hexptr = dkstr_next(cnptr, NULL);
  reason = DKFIG_ERROR_SYNTAX;
  if(hexptr) {
    if(sscanf(cnptr, "%d", &cellno) == 1) {
      if((d->ccells) && (d->ccit)) {
        if(!dksto_it_find_like(d->ccit, &cellno, 1)) {
	  if(*hexptr == '#') {
	    hexptr++;
	    if(*hexptr) {
	      r = hex_to_int(*(hexptr++));
	      if(*hexptr) {
	        r = 16 * r + hex_to_int(*(hexptr++));
		if(*hexptr) {
		  g = hex_to_int(*(hexptr++));
		  if(*hexptr) {
		    g = 16 * g + hex_to_int(*(hexptr++));
		    if(*hexptr) {
		      b = hex_to_int(*(hexptr++));
		      if(*hexptr) {
		        b = 16 * b + hex_to_int(*(hexptr++));
			ccptr = dkfig_new_color_cell(cellno,r,g,b);
			if(ccptr) {
			  if(dksto_add(d->ccells, (void *)ccptr)) {
			    back = 1;	
			  } else {
			    dkfig_delete_color_cell(ccptr);
			    ccptr = NULL;
			    reason = DKFIG_ERROR_MEMORY;
			  }
			} else {
			  reason = DKFIG_ERROR_MEMORY;
			}
		      }
		    }
		  }
		}
	      }
	    }
	  }
	}
      }
    }
  }
  if(!back) {
    d->errc = reason;
  }
  
  return back;
}
/* }}} */



/* {{{ register_object_data

  Save object data in the current compound object.

*/
static
int
register_object_data DK_P4(dk_fig_drawing *,d, void *,data,long,l,dk_fig_fpd *,fpd)
{
  int back = 0;
  dk_storage_t *st;
  dk_fig_drawing *dptr; dk_fig_compound *cptr;
  dk_fig_object *o;
  
  st = NULL;
  if(d->currentcomp) {
    switch((d->currentcomp)->objtype) {
      case DK_FIG_OBJ_COMPOUND: {
        cptr = (dk_fig_compound *)((d->currentcomp)->data);
	st = cptr->objects;
      } break;
      case DK_FIG_OBJ_DRAWING: {
        dptr = (dk_fig_drawing *)((d->currentcomp)->data);
	st = dptr->objects;
      } break;
    }
  }
  if(st) {
    d->currentobj = NULL;
    o = dkfig_object_new(d->lineno, d->currentcomp, d->nosc, d->nosci);
    if(o) {
      o->layer = l;
      o->objtype = fpd->ot;
      o->subtype = fpd->st;
      o->data = data;
      DK_MEMCPY(&(o->fpd), fpd, sizeof(dk_fig_fpd)) ;
      d->nosc = NULL;
      d->nosci = NULL;
      if(dksto_add(st, (void *)o)) {
        back = 1;
	if(fpd->ot == DK_FIG_OBJ_COMPOUND) {
	  d->currentcomp = o;
	}
	d->currentobj = o;
      } else {
        o->layer = 0L;
	o->data = NULL;
	dkfig_object_delete(o);
	o = NULL;
      }
    }
  } else {			
  }
  
  return back;
}
/* }}} */



/* {{{ dkfig_delete

  Destroy object for entire drawing
  (including all contents)

*/
void
dkfig_delete DK_P1(dk_fig_object *,o)
{
  
  if(o) {
    dkfig_object_delete(o);
  }
  
}
/* }}} */



/* {{{ new_drawing_details

  Create and initialize details for new drawing.

*/
static
dk_fig_drawing *
new_drawing_details DK_P2(int,uwp,dk_fig_object *,cc)
{
  dk_fig_drawing *back = NULL;
  int ok = 0; int i;
  rgb *rgbptr;
  dk_fig_colorcell *ccptr;
  
  back = dk_new(dk_fig_drawing,1);
  if(back) {
    /* initialize all pointers to zero */
    back->spline_segs = 1;
    back->inputfilename = NULL;
    back->objects       = NULL;
    back->objit         = NULL;
    back->lineno	= 0UL;
    back->state         = 0;
    back->currentcomp   = cc;
    back->currentobj    = NULL;
    back->errc 		= 0;
    back->ccells	= NULL;
    back->ccit		= NULL;
    back->dlsc		= NULL;
    back->dlsci		= NULL;
    back->fonth		= NULL;
    back->fonthi	= NULL;
    back->orientation	= 0;
    back->justification	= 0;
    back->units		= 0;
    back->paper		= 0;
    back->multi		= 0;
    back->transparent	= 0;
    back->resolution	= 0L;
    back->fres		= 0.0;
    back->cstype	= 0;
    back->magnification = 100.0;
    back->numlatfonts   = 0UL;
    back->numlatalpha   = 0;
    back->opt1		= 0UL;
    back->ahlj		= 0;
    back->minitsteps	= 8UL;
    back->maxitsteps	= 2048UL;
    dkfig_tool_bb_reset(&(back->dbb));
    /* real initialization */
    back->objects       = dksto_open(0);
    if(back->objects) {
      dksto_set_comp(back->objects, dkfig_object_compare, 0);
      back->objit = dksto_it_open(back->objects);
      if(back->objit) {
        back->dlsc = dksto_open(0);
	if(back->dlsc) {
	  dksto_set_comp(back->dlsc, dkfig_opt_compare, 0);
	  back->dlsci = dksto_it_open(back->dlsc);
	  if(back->dlsci) {
	    back->ccells = dksto_open(0);
	    if(back->ccells) {
	      dksto_set_comp(back->ccells, color_cell_compare, 0);
              back->ccit = dksto_it_open(back->ccells);
	      if(back->ccit) {
	        back->fonth = dksto_open(0);
		if(back->fonth) {
		  dksto_set_comp(back->fonth, dkfig_fnt_comp_fonth, 0);
		  back->fonthi = dksto_it_open(back->fonth);
		  if(back->fonthi) { 
	            ok = 1;
	            rgbptr = (uwp ? web_rgb : low_rgb);
	            for(i = 0; i < 32; i++) {
	              ccptr = dkfig_new_color_cell(
	                        i, rgbptr->r, rgbptr->g, rgbptr->b
	                      );
	              if(ccptr) {
	                if(!dksto_add(back->ccells, (void *)ccptr)) {
		          ok = 0;
		          dkfig_delete_color_cell(ccptr);
		        }
	              } else {
	                ok = 0;
	              }
		      rgbptr++;
	            } 
		  }
		}
	      }
	    }
          }
	}
      }
    }
    if(!ok) {
      delete_drawing_details(back); back = NULL;
    }
  }
  
  return back;
}
/* }}} */



/* {{{

  Create object for entire drawing.

*/
dk_fig_object *
dkfig_new DK_P1(int,uwp)
{
  dk_fig_object *back = NULL;
  
  back = dkfig_object_new(0UL, NULL, NULL, NULL);
  if(back) {
    back->objtype = DK_FIG_OBJ_DRAWING;
    back->data = new_drawing_details(uwp, back);
    if(!(back->data)) {
      dkfig_delete(back); back = NULL;
    }
  }
  
  return back;
}
/* }}} */



/* {{{ new_ellipse_details

  Create data structure for an ellipse.

*/
static
dk_fig_ellipse *
new_ellipse_details DK_P0()
{
  dk_fig_ellipse *back = NULL;
  
  back = dk_new(dk_fig_ellipse,1);
  if(back) {
    null_ellipse(back);
  }
  
  return back;
}
/* }}} */



/* {{{ add_ellipse

  Process a line starting a new ellipse.

*/
static
int
add_ellipse DK_P2(dk_fig_drawing *,d,char *,text)
{
  int back = 0;
  int reason = 0;
  long layer = 100L;
  int di;
  double an; long cx, cy, rx, ry, sx, sy, ex, ey;
  char *ptr[21]; size_t sz;
  dk_fig_ellipse *e;
  dk_fig_fpd      fpd;

  
  reason = DKFIG_ERROR_SYNTAX;
  dkfig_tool_null_fpd(&fpd);
  fpd.ot = DK_FIG_OBJ_ELLIPSE;
  sz = dkstr_explode(ptr, 21, text, NULL);
  if(sz >= 19) {
    if(sscanf(ptr[0], "%d", &(fpd.st)) == 1) {	/* sub type */
    if(sscanf(ptr[1], "%d", &(fpd.ls)) == 1) {
    if(sscanf(ptr[2], "%ld", &(fpd.lt)) == 1) {
    if(sscanf(ptr[3], "%d", &(fpd.pc)) == 1) {
    if(sscanf(ptr[4], "%d", &(fpd.fc)) == 1) {
    if(sscanf(ptr[5], "%ld", &layer) == 1) {
    if(sscanf(ptr[6], "%d", &(fpd.ps)) == 1) {
    if(sscanf(ptr[7], "%d", &(fpd.af)) == 1) {
    if(sscanf(ptr[8], "%lf", &(fpd.sv)) == 1) {
    if(sscanf(ptr[9], "%d", &di) == 1) {
    if(sscanf(ptr[10], "%lf", &an) == 1) {
    if(sscanf(ptr[11], "%ld", &cx) == 1) {
    if(sscanf(ptr[12], "%ld", &cy) == 1) {
    if(sscanf(ptr[13], "%ld", &rx) == 1) {
    if(sscanf(ptr[14], "%ld", &ry) == 1) {
    if(sscanf(ptr[15], "%ld", &sx) == 1) {
    if(sscanf(ptr[16], "%ld", &sy) == 1) {
    if(sscanf(ptr[17], "%ld", &ex) == 1) {
    if(sscanf(ptr[18], "%ld", &ey) == 1) {
      reason = DKFIG_ERROR_MEMORY;
      e = new_ellipse_details();
      if(e) {
	fpd.cl = 1;
	e->direction = di; e->angle = an;
	e->centerx = cx; e->centery = cy;
	e->radiusx = rx; e->radiusy = ry;
	e->startx = sx; e->starty = sy;
	e->endx = ex; e->endy = ey;
        back = register_object_data(d, (void *)e, layer, &fpd);
	if(!back) {
	  delete_ellipse_details(e); e = NULL;
	}
      }
    } } } } } } } } } } } } } } } } } } }
  }
  if(!back) { d->errc = reason; }
  
  return back;
}
/* }}} */



/* {{{ new_polyline_details

  Create data structure for polyline.

*/
static
dk_fig_polyline *
new_polyline_details DK_P1(int,np)
{
  dk_fig_polyline *back = NULL;
  size_t sz;
  
  back = dk_new(dk_fig_polyline,1);
  if(back) {
    null_polyline(back);
    sz = (size_t)np;
    back->npoints = sz;
    back->xvalues = dk_new(long,sz);
    back->yvalues = dk_new(long,sz);
    if(!((back->xvalues) && (back->yvalues))) {
      delete_polyline_details(back);
      back = NULL;
    }
  }
  
  return back;
}
/* }}} */



/* {{{ start_polyline

  Process a line starting a new polyline.

*/
static
int
start_polyline DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 0;
  int reason;
  char *ptr[17]; size_t sz;
  dk_fig_polyline *p;
  dk_fig_fpd       fpd;
  long layer; 
  long radius; int ar1, ar2, np;
  
  reason = DKFIG_ERROR_SYNTAX;
  dkfig_tool_null_fpd(&fpd);
  fpd.ot = DK_FIG_OBJ_POLYLINE;
  sz = dkstr_explode(ptr, 16, b, NULL);
  if(sz >= 15) {
    if(sscanf(ptr[0], "%d", &(fpd.st)) == 1) {	/* sub type */
    if(sscanf(ptr[1], "%d", &(fpd.ls)) == 1) {	/* line style */
    if(sscanf(ptr[2], "%ld", &(fpd.lt)) == 1) {	/* thickness */
    if(sscanf(ptr[3], "%d", &(fpd.pc)) == 1) {	/* pen color */
    if(sscanf(ptr[4], "%d", &(fpd.fc)) == 1) {	/* fill color */
    if(sscanf(ptr[5], "%ld", &layer) == 1) {	/* layer */
    if(sscanf(ptr[6], "%d", &(fpd.ps)) == 1) {	/* pen style */
    if(sscanf(ptr[7], "%d", &(fpd.af)) == 1) {	/* area fill */
    if(sscanf(ptr[8], "%lf", &(fpd.sv)) == 1) {	/* style_val */
    if(sscanf(ptr[9], "%d", &(fpd.js)) == 1) {	/* join style */
    if(sscanf(ptr[10], "%d", &(fpd.cs)) == 1) {	/* cap style */
    if(sscanf(ptr[11], "%ld", &radius) == 1) {	/* radius for rounded edges */
    if(sscanf(ptr[12], "%d", &ar1) == 1) {	/* forward arrow */
    if(sscanf(ptr[13], "%d", &ar2) == 1) {	/* backward arrow */
    if(sscanf(ptr[14], "%d", &np) == 1) {	/* number of points */
      switch(fpd.st) { case 2: case 3: case 4: case 5: fpd.cl = 1; break; }
      reason = DKFIG_ERROR_MEMORY;
      p = new_polyline_details(np);
      if(p) {
	p->radius = radius; fpd.ar = 0;
	if(ar1) { fpd.ar |= 1;	
	}
	if(ar2) { fpd.ar |= 2;	
	}
	p->npoints = np;
        back = register_object_data(d, (void *)p, layer, &fpd);
	if(back) {
	  d->currentdet = p;
	  d->currentpnt = 0; d->xory = 0;
	  d->state = STATE_EXPECT_PL_POINTS;
	  if(fpd.st == 5) {
	    d->state = STATE_EXPECT_PL_IMAGENAME;
	  }
	  if(ar2) {		
	    d->state = STATE_EXPECT_PL_ARROW_2;
	  }
	  if(ar1) {		
	    d->state = STATE_EXPECT_PL_ARROW_1;
	  }
	} else {
	  delete_polyline_details(p); p = NULL;
	}
      } 
    } } } } } } } } } } } } } } }
  }
  if(!back) {
    d->errc = reason;
  }
  
  return back;
}
/* }}} */



/* {{{ new_spline_details

  Create data structure for a spline.

*/
static
dk_fig_spline *
new_spline_details DK_P4(int,np, size_t,subsegs, int,cl, dk_fig_conversion *,c)
{
  dk_fig_spline *back = NULL;
  size_t sz = 0;
  int ok = 0, matherr = 0;
  unsigned long s, p, bp;
  
  ok = 0;
  back = dk_new(dk_fig_spline,1);
  if(back) {		
    sz = (size_t)np;
    null_spline(back);	
    back->xvalues = dk_new(long,sz);		
    back->yvalues = dk_new(long,sz);		
    back->svalues = dk_new(double,sz);		
    back->npoints = np;				
    back->segs    = subsegs;
    if(back->segs < 1) { back->segs = 1; }
    s = back->segs; p = back->npoints; matherr = 0;
    bp = dkma_mul_ulong_ok(s, p, &matherr);
    if(!cl) {
      bp = dkma_sub_ulong_ok(
        dkma_add_ulong_ok(bp, 1UL, &matherr),
	s,
	&matherr
      );
    } 
    if(!matherr) {
      back->nbpoints = (size_t)bp;
      back->normals  = 0;
      back->normale  = (size_t)(bp - 1);
      if((unsigned long)(back->nbpoints) == bp) {
        back->bpoints = dk_new(dk_fig_bezier_point,(back->nbpoints));
      } else {
        /* ERROR: Number of points too large */
	if(c) {
	if(c->app) {
	  dkapp_err_memory(c->app, sizeof(dk_fig_bezier_point), back->nbpoints);
	}
	}
      }
    } else {
      /* ERROR: Number of points too large */
      if(c) {
        dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 43);
      }
    }
    if((back->xvalues) && (back->yvalues) && (back->svalues) && (back->nbpoints)) {
      ok = 1;
    }
    if(!ok) {
      delete_spline_details(back); back = NULL;
    }
  }
  
  return back;
}
/* }}} */



/* {{{ start_spline

  Process a line starting a new spline object.

*/
static
int
start_spline DK_P3(dk_fig_drawing *,d,char *,b, dk_fig_conversion *,c)
{
  int back = 0;
  dk_fig_spline *s;
  dk_fig_fpd     fpd;
  char *ptr[15]; size_t sz;
  int reason;
  long layer;
  int ar1, ar2, np;
  
  reason = DKFIG_ERROR_SYNTAX;
  dkfig_tool_null_fpd(&fpd);
  fpd.ot = DK_FIG_OBJ_SPLINE;
  sz = dkstr_explode(ptr, 14, b, NULL);
  if(sz >= 13) {
    if(sscanf(ptr[0], "%d", &(fpd.st)) == 1) {	/* sub type */
    if(sscanf(ptr[1], "%d", &(fpd.ls)) == 1) {	/* line style */
    if(sscanf(ptr[2], "%ld", &(fpd.lt)) == 1) {	/* thickness */
    if(sscanf(ptr[3], "%d", &(fpd.pc)) == 1) {	/* pen color */
    if(sscanf(ptr[4], "%d", &(fpd.fc)) == 1) {	/* fill color */
    if(sscanf(ptr[5], "%ld", &layer) == 1) {	/* layer */
    if(sscanf(ptr[6], "%d", &(fpd.ps)) == 1) {	/* pen style */
    if(sscanf(ptr[7], "%d", &(fpd.af)) == 1) {	/* area fill */
    if(sscanf(ptr[8], "%lf", &(fpd.sv)) == 1) {	/* style_val */
    if(sscanf(ptr[9], "%d", &(fpd.cs)) == 1) {	/* cap style */
    if(sscanf(ptr[10], "%d", &ar1) == 1) {	/* forward arrow */
    if(sscanf(ptr[11], "%d", &ar2) == 1) {	/* backward arrow */
    if(sscanf(ptr[12], "%d", &np) == 1) {	/* npoints */
      switch(fpd.st) { case 1: case 3: case 5: fpd.cl = 1; break; }
      reason = DKFIG_ERROR_MEMORY;
      s = new_spline_details(np,d->spline_segs,fpd.cl,c);
      if(s) {
	if(ar1) { fpd.ar |= 1; }
	if(ar2) { fpd.ar |= 2; }
	s->segs = d->spline_segs;
	/* s->flags = 0; */
        back = register_object_data(d, (void *)s, layer, &fpd);
	if(back) {
	  d->currentdet = s;
	  d->currentpnt = 0; d->xory = 0;
	  d->state = STATE_EXPECT_SPLINE_POINTS;
	  if(ar2) { d->state = STATE_EXPECT_SPLINE_ARROW_2; }
	  if(ar1) { d->state = STATE_EXPECT_SPLINE_ARROW_1; }
	} else {
	  delete_spline_details(s); s = NULL;
	}
      }
    } } } } } } } } } } } } }
  }
  if(!back) { d->errc = reason; }
  
  return back;
}
/* }}} */



/* {{{ copy_forward

  Copy string contents "forward" (to the left)
  after processing an octal code.

*/
static
void
copy_forward DK_P2(char *,d,char *,s)
{
  char *dptr, *sptr;
  
  dptr = d; sptr = s;
  while(*sptr) { *(dptr++) = *(sptr++); }
  /* 2004-06-15 bug fixed: forgot to finalize string */
  *dptr = '\0';
  /* 2004-06-15 end of bug fix */
  
}
/* }}} */



/* {{{

  Correct octal codes in text.

*/
static
void
correct_octal_codes DK_P1(char *,str)
{
  char *p; int i;
  
  p = str;
  while(*p) {
    
    if(*p == '\\') {
      if((p[1] >= '0') && (p[1] <= '9')) {
      if((p[2] >= '0') && (p[1] <= '9')) {
      if((p[3] >= '0') && (p[1] <= '9')) {
        i = 64 * hex_to_int(p[1]) + 8 * hex_to_int(p[2]) + hex_to_int(p[3]);
	*p = (char) i;
	if(*p == 0x01) {
	  *p = '\0';
	} else {
	  copy_forward(&(p[1]), &(p[4]));
	  p++;
	}
      } else {
        i = 8 * hex_to_int(p[1]) + hex_to_int(p[2]);
	*p = (char)i;
	if(*p == 0x01) {
	  *p = '\0';
	} else {
	  copy_forward(&(p[1]), &(p[3]));
	  p++;
	}
      }
      } else {
        i = hex_to_int(p[1]);
	*p = (char)i;
	if(*p == 0x01) {
	  *p = '\0';
	} else {
	  copy_forward(&(p[1]), &(p[2]));
	  p++;
	}
      }
      } else {
        switch(p[1]) {
	  case '\0' : {
	    /* 2004-06-15 bug fixed: standalone backslash was unhandled */
	    *p = '\0';
	    /* 2004-06-15 end of bug fix */
	  } break;
	  case '\\' : {
	    *p = '\\';
	    copy_forward(&(p[1]), &(p[2]));
	    p++;
	  } break;
	  case 'r': {
	    *p = '\r';
	    copy_forward(&(p[1]), &(p[2]));
	    p++;
	  } break;
	  case 'n': {
	    *p = '\n';
	    copy_forward(&(p[1]), &(p[2]));
	    p++;
	  } break;
	  case 't': {
	    *p = '\t';
	    copy_forward(&(p[1]), &(p[2]));
	    p++;
	  } break;
	  case 'a': {
	    *p = '\a';
	    copy_forward(&(p[1]), &(p[2]));
	    p++;
	  } break;
	  case 'b': {
	    *p = '\b';
	    copy_forward(&(p[1]), &(p[2]));
	    p++;
	  } break;
	}
      }
    } else {
      p++;
    }
  }
  
}
/* }}} */



/* {{{ my_explode

  Create a number of small strings from one
  text line.

*/
static size_t
my_explode DK_P3(char **,array, size_t,sz, char *,l)
{
  size_t back = 0;
  char **ptr; size_t i; char *nxptr, *oldptr;
  
  ptr = array; i = sz;
  while(i--) { *(ptr++) = NULL; }
  ptr = array; i = sz;
  nxptr = dkstr_start(l, NULL); oldptr = NULL;
  if(nxptr) {
    *(ptr++) = nxptr; i--; oldptr = nxptr; back++;
  }
  while(i-- > 0) {
    nxptr = NULL;
    if(oldptr) {
      nxptr = dkstr_next(oldptr, NULL);
      if(nxptr) {
        *(ptr++) = nxptr; back++;
      }
    }
    oldptr = nxptr;
  } 
  return back;
}
/* }}} */



/* {{{ start_text

  Process a line starting a text object.

*/
static
int
start_text DK_P2(dk_fig_drawing *,d,char *,line)
{
  int back = 0, ff = 0;
  int reason = DKFIG_ERROR_SYNTAX;
  char *str = NULL;
  long layer;
  char *ptr[16]; size_t sz;
  dk_fig_text *td;
  dk_fig_fpd   fpd;
  int fo;
  double fs = 0.0, an = 0.0, h = 0.0, l = 0.0;
  long x = 0L, y = 0L;
  
  dkfig_tool_null_fpd(&fpd);
  fpd.ot = DK_FIG_OBJ_TEXT;
  /* sz = dkstr_explode(ptr, 14, line, NULL); */
  sz = my_explode(ptr, 13, line);
  
  if(sz == 13) {
    if(sscanf(ptr[0], "%d", &(fpd.st)) == 1) {		/* sub type */
    if(sscanf(ptr[1], "%d", &(fpd.pc)) == 1) {		/* color */
    if(sscanf(ptr[2], "%ld", &layer) == 1) {		/* layer */
    if(sscanf(ptr[3], "%d", &(fpd.ps)) == 1) {		/* pen style */
    if(sscanf(ptr[4], "%d", &fo) == 1) {		/* font */
    if(sscanf(ptr[5], "%lf", &fs) == 1) {		/* font size */
    if(sscanf(ptr[6], "%lf", &an) == 1) {		/* angle */
    if(sscanf(ptr[7], "%d", &ff) == 1) {		/* font flags */
    if(sscanf(ptr[8], "%lf", &h) == 1) {		/* height */
    if(sscanf(ptr[9], "%lf", &l) == 1) {		/* length */
    if(sscanf(ptr[10], "%ld", &x) == 1) {		/* x */
    if(sscanf(ptr[11], "%ld", &y) == 1) {		/* y */
      
      
      reason = DKFIG_ERROR_MEMORY;
      str = dkstr_dup(ptr[12]);
      if(str) {		
        td = new_text_details();
	if(td) {	
	  td->font = fo; td->font_size = fs; td->angle = an;
	  td->font_flags = ff; td->height = h; td->length = l;
	  td->x = x; td->y = y; td->text = str;
	  
	  back = register_object_data(
	    d, (void *)td, layer, &fpd
	  );
	  if(back) {	
	    correct_octal_codes(str);
	  } else {	
	    delete_text_details(td); td = NULL;
	  }
	} else {
	  dk_delete(str);
	}
      }
    } } } } } } } } } } } }
  }
  if(!back) { d->errc = reason; }
  
  return back;
}
/* }}} */



/* {{{ new_arc_details

  Create data structure for arc.

*/
static
dk_fig_arc *
new_arc_details DK_P0()
{
  dk_fig_arc *back = NULL;
  
  back = dk_new(dk_fig_arc,1);
  if(back) {
    null_arc(back);
  }
  
  return back;
}
/* }}} */



/* {{{ start_arc

  Process a line starting a new arc.

*/
static
int
start_arc DK_P2(dk_fig_drawing *,d,char *,line)
{
  int back = 0; int reason;
  long layer;
  int di, ar1, ar2;
  double cx, cy;
  long x1, y1, x2, y2, x3, y3;
  char *ptr[32]; size_t sz;
  dk_fig_arc *a;
  dk_fig_fpd  fpd;
  
  dkfig_tool_null_fpd(&fpd);
  fpd.ot = DK_FIG_OBJ_ARC;
  reason = DKFIG_ERROR_SYNTAX;
  sz = dkstr_explode(ptr, 32, line, NULL);
  if(sz >= 21) {
    if(sscanf(ptr[0], "%d", &(fpd.st)) == 1) {	/* sub type */
    if(sscanf(ptr[1], "%d", &(fpd.ls)) == 1) {	/* line style */
    if(sscanf(ptr[2], "%ld", &(fpd.lt)) == 1) {	/* line thickness */
    if(sscanf(ptr[3], "%d", &(fpd.pc)) == 1) {	/* pen color */
    if(sscanf(ptr[4], "%d", &(fpd.fc)) == 1) {	/* fill color */
    if(sscanf(ptr[5], "%ld", &layer) == 1) {	/* depth */
    if(sscanf(ptr[6], "%d", &(fpd.ps)) == 1) {	/* pen style */
    if(sscanf(ptr[7], "%d", &(fpd.af)) == 1) {	/* area fill */
    if(sscanf(ptr[8], "%lf", &(fpd.sv)) == 1) {	/* style val */
    if(sscanf(ptr[9], "%d", &(fpd.cs)) == 1) {	/* cap style */
    if(sscanf(ptr[10], "%d", &di) == 1) {	/* direction */
    if(sscanf(ptr[11], "%d", &ar1) == 1) {	/* arrow forward */
    if(sscanf(ptr[12], "%d", &ar2) == 1) {	/* arrow backward */
    if(sscanf(ptr[13], "%lf", &cx) == 1) {	/* center x */
    if(sscanf(ptr[14], "%lf", &cy) == 1) {	/* center y */
    if(sscanf(ptr[15], "%ld", &x1) == 1) {	/* x 1 */
    if(sscanf(ptr[16], "%ld", &y1) == 1) {	/* y 1 */
    if(sscanf(ptr[17], "%ld", &x2) == 1) {	/* x 2 */
    if(sscanf(ptr[18], "%ld", &y2) == 1) {	/* y 2 */
    if(sscanf(ptr[19], "%ld", &x3) == 1) {	/* x 3 */
    if(sscanf(ptr[20], "%ld", &y3) == 1) {	/* y 3 */
      reason = DKFIG_ERROR_MEMORY;
      a = new_arc_details();
      if(a) {
	a->direction = di; fpd.ar = 0;
	if(ar1) { fpd.ar |= 1; }
	if(ar2) { fpd.ar |= 2; }
	a->centerx = cx; a->centery = cy; a->x1 = x1; a->y1 = y1;
	a->x2 = x2; a->y2 = y2; a->x3 = x3; a->y3 = y3;
	switch(fpd.st) { case 2: { fpd.cl = 1; } break; }
	back = register_object_data(
	  d, (void *)a, layer, &fpd
	);
	if(back) {
	  d->currentdet = (void *)a;
	  if(ar2) {
	    d->state = STATE_EXPECT_ARC_ARROW_2;
	  }
	  if(ar1) {
	    d->state = STATE_EXPECT_ARC_ARROW_1;
	  }
	} else {
	  delete_arc_details(a); a = NULL;
	}
      }
    } } } } } } } } } } } } } } } } } } } } }
  }
  if(!back) { d->errc = reason; }
  
  return back;
}
/* }}} */



/* {{{ finish_compound

  This compound is finished, go back to parent
  compound.

*/
static
int
finish_compound DK_P1(dk_fig_drawing *,d)
{
  int back = 0;
  
  if(d->currentcomp) {
    d->currentcomp = (d->currentcomp)->parent;
    if(d->currentcomp) {
      back = 1;
    } else {
      
    }
  }
  
  return back;
}
/* }}} */



/* {{{ new_compound_details

  Create data structure for a new compound object.
*/
static
dk_fig_compound *
new_compound_details DK_P4(long,ulx,long,uly,long,lrx,long,lry)
{
  int ok;
  dk_fig_compound *back = NULL;
  
  ok = 0;
  back = dk_new(dk_fig_compound,1);
  if(back) {
    null_compound(back);
    back->objects = dksto_open(0);
    if(back->objects) {
      dksto_set_comp(back->objects, dkfig_object_compare, 0);
      back->objit = dksto_it_open(back->objects);
      if(back->objit) {
        ok = 1;
	back->ulx = ulx; back->uly = uly;
	back->lrx = lrx; back->lry = lry;
      }
    }
    if(!ok) {
      delete_compound_details(back);
      back = NULL;
    }
  }
  
  return back;
}
/* }}} */



/* {{{ start_compound

  Process a line starting a new compound.

*/
static
int
start_compound DK_P2(dk_fig_drawing *,d,char *,line)
{
  int back = 0, reason; char *p1, *p2, *p3, *p4;
  long ulx, uly, lrx, lry;
  dk_fig_compound *cptr;
  dk_fig_fpd       fpd;
  
  reason = DKFIG_ERROR_SYNTAX;
  dkfig_tool_null_fpd(&fpd);
  fpd.ot = DK_FIG_OBJ_COMPOUND;
  p1 = dkstr_start(line, NULL);
  if(p1) {
    p2 = dkstr_next(p1, NULL);
    if(p2) {
      p3 = dkstr_next(p2, NULL);
      if(p3) {
        p4 = dkstr_next(p3, NULL);
	if(p4) {
	  if(sscanf(p1, "%ld", &ulx) == 1) {
	  if(sscanf(p2, "%ld", &uly) == 1) {
	  if(sscanf(p3, "%ld", &lrx) == 1) {
	  if(sscanf(p4, "%ld", &lry) == 1) {
	    reason = DKFIG_ERROR_MEMORY;
            cptr = new_compound_details(ulx,uly,lrx,lry);
	    if(cptr) {
	      back = register_object_data(
	        d, (void *)cptr, 0UL, &fpd
	      );
	      if(!back) {
	        delete_compound_details(cptr); cptr = NULL;
	      }
	    }
	  } } } }
	}
      }
    }
  }
  if(!back) {
    d->errc = reason;
  }
  
  return back;
}
/* }}} */



/* {{{

  We are at the beginning of a new object.

*/
static
int
begin_object DK_P3(dk_fig_drawing *,d,char *,b, dk_fig_conversion *,c)
{
  int back = 0;
  char *nxptr;
  int objtype;
  
  nxptr = dkstr_next(b, NULL);
  if(sscanf(b, "%d", &objtype) == 1) {
    switch(objtype) {
      case 0:	{	/* color cell */
        back = add_color_cell(d,nxptr);
      } break;
      case 1:	{	/* ellipse */
        back = add_ellipse(d,nxptr);
      } break;
      case 2:	{	/* polyline */
        back = start_polyline(d, nxptr);
      } break;
      case 3:	{	/* spline */
        back = start_spline(d, nxptr, c);
      } break;
      case 4:	{	/* text */
        back = start_text(d, nxptr);
      } break;
      case 5: 	{	/* arc */
        back = start_arc(d, nxptr);
      } break;
      case 6:	{	/* compound */
        back = start_compound(d, nxptr);
      } break;
      case -6:	{	/* end of compound */
        back = finish_compound(d);
      } break;
    }
  } else {
    d->errc = DKFIG_ERROR_SYNTAX;
  }
  
  return back;
}
/* }}} */



/* {{{ add_arrow_info

  Fill data structure for arrowhead.

*/
static
int
add_arrow_info DK_P2(dk_fig_arrow *,a,char *,text)
{
  int back = 0;
  char *ptr[10]; size_t sz;
  int i; double d;
  
  sz = dkstr_explode(ptr, 9, text, NULL);
  if(sz >= 5) {
    if(sscanf(ptr[0], "%d", &i) == 1) {
      a->type = i;
      if(sscanf(ptr[1], "%d", &i) == 1) {
        a->style = i;
	if(sscanf(ptr[2], "%lf", &d) == 1) {
	  a->thickness = d;
	  if(sscanf(ptr[3], "%lf", &d) == 1) {
	    a->w = d;
	    if(sscanf(ptr[4], "%lf", &d) == 1) {
	      a->h = d;
	      back = 1;
	    }
	  }
	}
      }
    }
  }
  
  return back;
}
/* }}} */



/* {{{ add_arc_arrow_1
  
  Add forward arrow information to arc.

*/
static
int
add_arc_arrow_1 DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 0;
  
  if(d->currentobj) {
    back = add_arrow_info(&(((d->currentobj)->fpd).ahf), b);
  }
  if((((d->currentobj)->fpd).ar) & 2) {
    d->state = STATE_EXPECT_ARC_ARROW_2;
  } else {
    d->state = STATE_START;
    d->currentdet = NULL; d->currentobj = NULL;
  }
  if(!back) { d->errc = DKFIG_ERROR_SYNTAX; }
  
  return back;
}
/* }}} */



/* {{{ add_arc_arrow_2

  Add backward arrow information to arc.

*/
static
int
add_arc_arrow_2 DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 0;
  
  if(d->currentobj) {
    back = add_arrow_info(&(((d->currentobj)->fpd).ahb), b);
  }
  d->state = STATE_START;
  d->currentdet = NULL; d->currentobj = NULL;
  if(!back) { d->errc = DKFIG_ERROR_SYNTAX; }
  
  return back;
}
/* }}} */



/* {{{ add_pl_arrow_1

  Add forward arrow information to a polyline.

*/
static
int
add_pl_arrow_1 DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 0;
  
  if(d->currentobj) {
    back = add_arrow_info(&(((d->currentobj)->fpd).ahf), b);
  }
  d->state = STATE_EXPECT_PL_POINTS;
  if(((d->currentobj)->fpd).st == 5) {
    d->state = STATE_EXPECT_PL_IMAGENAME;
  }
  if((((d->currentobj)->fpd).ar) & 2) {	
    d->state = STATE_EXPECT_PL_ARROW_2;
  }
  if(!back) { d->errc = DKFIG_ERROR_SYNTAX; }
  
  return back;
}
/* }}} */



/* {{{ add_pl_arrow_2

  Add backward arrow information to polyline.

*/
static
int
add_pl_arrow_2 DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 0;
  
  if(d->currentobj) {
    back = add_arrow_info(&(((d->currentobj)->fpd).ahb), b);
  }
  d->state = STATE_EXPECT_PL_POINTS;
  if(((d->currentobj)->fpd).st == 5) {
    d->state = STATE_EXPECT_PL_IMAGENAME;
  }
  if(!back) { d->errc = DKFIG_ERROR_SYNTAX; }
  
  return back;
}
/* }}} */



/* {{{ add_sp_arrow_1

  Add forward arrow information to a spline.

*/
static
int
add_sp_arrow_1 DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 0;
  
  if(d->currentobj) {
    back = add_arrow_info(&(((d->currentobj)->fpd).ahf), b);
  }
  if((((d->currentobj)->fpd).ar) & 2) {
    d->state = STATE_EXPECT_SPLINE_ARROW_2;
  } else {
    d->state = STATE_EXPECT_SPLINE_POINTS;
  }
  if(!back) { d->errc = DKFIG_ERROR_SYNTAX; }
  
  return back;
}
/* }}} */



/* {{{ add_sp_arrow_2

  Add backward arrow information to a spline.

*/
static
int
add_sp_arrow_2 DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 0;
  
  if(d->currentobj) {
    back = add_arrow_info(&(((d->currentobj)->fpd).ahb), b);
  }
  d->state = STATE_EXPECT_SPLINE_POINTS;
  if(!back) { d->errc = DKFIG_ERROR_SYNTAX; }
  
  return back;
}
/* }}} */



/* {{{ add_pl_points

  Add a line of polyline point co-ordinates.

*/
static
int
add_pl_points DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 1; long l; dk_fig_polyline *p;
  char *cptr, *nptr;
  int cc;
  
  if((d->currentdet) && (d->currentobj)) {
    p = (dk_fig_polyline *)(d->currentdet);
    cptr = dkstr_start(b, NULL); cc = 1;
    if(p->npoints == 0) cc = 0;
    while(cptr && (cc == 1)) {
      nptr = dkstr_next(cptr, NULL);
      if(sscanf(cptr, "%ld", &l) == 1) {
        if(d->currentpnt < p->npoints) {
	  if(d->xory) {
	    (p->yvalues)[d->currentpnt] = l;
	    d->xory = 0;
	    d->currentpnt += 1;
	    if(d->currentpnt >= p->npoints) {
	      cc = 0;
	      d->state = STATE_START;
#if VERSION_BEFORE_2004_07_12
	      if(((d->currentobj)->fpd).st == 5) {
	        d->state = STATE_EXPECT_PL_IMAGENAME;
	      } else {
	        d->currentdet = NULL; d->currentobj = NULL;
	      }
#else
              d->currentdet = NULL; d->currentobj = NULL;
#endif
	    }
	  } else {
	    (p->xvalues)[d->currentpnt] = l;
	    d->xory = 1;
	  }
	} else {
	  cc = 0; 
	  d->state = STATE_START;
#if VERSION_BEFORE_2004_07_12
	  if(((d->currentobj)->fpd).st == 5) {
	    d->state = STATE_EXPECT_PL_IMAGENAME;
	  } else {
	    d->currentdet = NULL; d->currentobj = NULL;
	  }
#else
          d->currentdet = NULL; d->currentobj = NULL;
#endif
	}
      } else {
        cc = 0; back = 0;
      }
      cptr = nptr;
    }
  } else {
    back = 0;
  }
  
  return back;
}
/* }}} */



/* {{{ add_pl_imagename

  Save the name of a bitmap image.

*/
static
int
add_pl_imagename DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 0; char *fn; int fl;
  int reason; dk_fig_polyline *p;
  
  reason = DKFIG_ERROR_SYNTAX;
  if(d->currentdet) {
    p = (dk_fig_polyline *)(d->currentdet);
    fn = dkstr_next(b, NULL);
    if(fn) {
      if(sscanf(b, "%d", &fl) == 1) {
        reason = DKFIG_ERROR_MEMORY;
	p->flipped = fl;
        fn = dkstr_dup(fn);
        if(fn) {
          if(p->imagename) {
	    char *ptr;
	    ptr = p->imagename; dk_delete(ptr);
	  }
          p->imagename = fn;	
	  back = 1;
	  d->state = STATE_EXPECT_PL_POINTS;
        }
      }
    }
  }
  if(!back) { d->errc = reason; }
  
  return back;
}
/* }}} */



/* {{{ add_sp_points

  Add a line of point co-ordinates.

*/
static
int
add_sp_points DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 0; int cc; long l;
  dk_fig_spline *s; char *cptr, *nptr;
  
  s = (dk_fig_spline *)(d->currentdet);
  if(s) {
    back = 1;
    cptr = dkstr_start(b, NULL); cc = 1;
    if(s->npoints == 0) cc = 0;
    if(d->currentpnt < (size_t)(s->npoints))  {
      while(cc && cptr) {
        nptr = dkstr_next(cptr, NULL);
        if(sscanf(cptr, "%ld", &l) == 1) {
          if(d->xory) {
	    (s->yvalues)[d->currentpnt] = l;
	    d->xory = 0;
	    d->currentpnt += 1;
	    if(d->currentpnt >= (size_t)(s->npoints)) {
	      cc = 0;
	      d->state = STATE_EXPECT_SPLINE_FACTORS;
	      d->currentpnt = 0;
	    }
	  } else {
	    (s->xvalues)[d->currentpnt] = l;
	    d->xory = 1;
	  }
        } else {
          cc = 0; back = 0;
        }
        cptr = nptr;
      }
    } else {
      d->state = STATE_EXPECT_SPLINE_FACTORS;
      d->currentpnt = 0;
    }
  }
  
  return back;
}
/* }}} */



/* {{{ add_sp_factors

  Add a line of s_k blending coefficients.

*/
static 
int
add_sp_factors DK_P2(dk_fig_drawing *,d,char *,b)
{
  int back = 0; dk_fig_spline *s;
  char *cptr, *nptr; double dx;
  int cc;
  
  s = (dk_fig_spline *)(d->currentdet);
  if(s) {
    if(d->currentpnt < (size_t)(s->npoints)) {
      back = 1;
      cc = 1; cptr = dkstr_start(b, NULL);
      while(cc && cptr) {
        nptr = dkstr_next(cptr, NULL);
	if(sscanf(cptr, "%lf", &dx) == 1) {
	  (s->svalues)[d->currentpnt] = dx;
	  d->currentpnt += 1;
	  if(d->currentpnt >= (size_t)(s->npoints)) {
	    cc = 0;
	    d->state = STATE_START;
	    d->currentdet = NULL;
	    d->currentobj = NULL;
	    d->currentpnt = 0;
	    d->xory = 0;
	  }
	} else {
	  back = cc = 0;
	}
	cptr = nptr;
      }
    } else {
      d->state = STATE_START;
      d->currentdet = NULL;
      d->currentobj = NULL;
      d->currentpnt = 0;
      d->xory = 0;
    }
  }
  
  return back;
}
/* }}} */



/* {{{

  Add a line (no comment line) to the drawing.

*/
static
int
add_contents_line DK_P3(dk_fig_drawing *,d,char *,b, dk_fig_conversion *,c)
{
  int back = 0;
  
  
  switch(d->state) {
    case STATE_EXPECT_ORIENTATION: {
      back = get_orientation(d,b);
    } break;
    case STATE_EXPECT_JUSTIFICATION: {
      back = get_justification(d,b);
    } break;
    case STATE_EXPECT_UNITS: {
      back = get_units(d,b);
    } break;
    case STATE_EXPECT_PAPERSIZE: {
      back = get_papersize(d,b);
    } break;
    case STATE_EXPECT_MAGNIFICATION: {
      back = get_magnification(d,b);
      
    } break;
    case STATE_EXPECT_MULTIPLE_PAGE: {
      back = get_multipage(d,b);
    } break;
    case STATE_EXPECT_TRANSPARENT_COLOR: {
      back = get_transparent_color(d,b);
    } break;
    case STATE_EXPECT_RESOLUTION: {
      back = get_resolution(d,b);
    } break;
    case STATE_START: {
      d->currentdet = NULL;
      d->currentobj = NULL;
      back = begin_object(d,b,c);
    } break;
    case STATE_EXPECT_ARC_ARROW_1: {
      back = add_arc_arrow_1(d,b);
    } break;
    case STATE_EXPECT_ARC_ARROW_2: {
      back = add_arc_arrow_2(d,b);
    } break;
    case STATE_EXPECT_PL_POINTS: {
      back = add_pl_points(d,b);
    } break;
    case STATE_EXPECT_PL_ARROW_1: {
      back = add_pl_arrow_1(d,b);
    } break;
    case STATE_EXPECT_PL_ARROW_2: {
      back = add_pl_arrow_2(d,b);
    } break;
    case STATE_EXPECT_PL_IMAGENAME: {
      back = add_pl_imagename(d,b);
    } break;
    case STATE_EXPECT_SPLINE_ARROW_1: {
      back = add_sp_arrow_1(d,b);
    } break;
    case STATE_EXPECT_SPLINE_ARROW_2: {
      back = add_sp_arrow_2(d,b);
    } break;
    case STATE_EXPECT_SPLINE_POINTS: {
      back = add_sp_points(d,b);
    } break;
    case STATE_EXPECT_SPLINE_FACTORS: {
      back = add_sp_factors(d,b);
    } break;
  }
  
  return back;
}
/* }}} */



/* {{{

  Add one line of text to the drawing.

*/
int
dkfig_read_add_line DK_P3(dk_fig_object *,o,char *,b, dk_fig_conversion *,c)
{
  int back = 0;
  dk_fig_drawing *d = NULL;
  char *p1;
  
  if(o && b) {
    d = (dk_fig_drawing *)(o->data);
    if(d) {				
      d->lineno += 1UL; d->errl = d->lineno;
      p1 = dkstr_start(b, NULL);
      if(p1) {
        if(*p1 == '#') {
	  if(d->state == STATE_EMPTY) {	
	    back = is_first_line_ok(d,b);
	  } else {			
	    p1++; p1 = dkstr_start(p1, NULL);
	    if(p1) {
	      if(*p1 == '#') {		
	        p1++; p1 = dkstr_start(p1, NULL);
		if(p1) {
	          back = add_special_comment(d,p1);
		} else {		
		  back = 1;
		}
	      } else {			
	        back = 1;
	      }
	    } else {			
	      back = 1;
	    }
	  }
	} else {			
	  back = add_contents_line(d,p1,c);
	}
      } else {				
        back = 1;
      }
    } else {				
    }
  }
  
  return back;
}
/* }}} */



/* {{{ build_splines_and_fonth

  Build splines and collect font information.

*/
static
int
build_splines_and_fonth DK_P4(dk_fig_object *,o, int,n,\
  int,s, dk_fig_conversion *,c)
{
  int back = 1;
  dk_fig_object *objptr, *no;
  dk_fig_drawing *dptr;
  dk_fig_compound *cptr;
  dk_storage_iterator_t *it;
  int error_was_here;
  int error_type;
  
  objptr = o;
  dptr = (dk_fig_drawing *)(o->data);
  if(dptr) {
    it = dptr->objit;
    dksto_it_reset(it);
    while(objptr) {
      dptr->errl = objptr->lineno;
      error_was_here = 0;
      error_type = 0;		/* 0=math, 1=mem, 2=setup */
      no = (dk_fig_object *)dksto_it_next(it);
      if(no) {
        switch(no->objtype) {
	  case DK_FIG_OBJ_ARC: {		
	    if(!dkfig_tool_arc_complete(no, dptr,c)) {
	      error_was_here = 1;
	      back = 0;
	    }
	    dkfig_tool_bb_arc(no, dptr);
	    dkfig_tool_bb_add(&(dptr->dbb), &(no->dbb));
	  } break;
	  case DK_FIG_OBJ_POLYLINE: {		
	    if(!dkfig_tool_polyline_complete(no, dptr,c)) {
	      error_was_here = 1;
	      back = 0;
	    }
	    dkfig_tool_bb_polyline(no, dptr);
	    dkfig_tool_bb_add(&(dptr->dbb), &(no->dbb));
	  } break;
	  case DK_FIG_OBJ_ELLIPSE: {		
	    dkfig_tool_bb_ellipse(no, dptr);
	    dkfig_tool_bb_add(&(dptr->dbb), &(no->dbb));
	  } break;
	  case DK_FIG_OBJ_DRAWING: {	
	  } break;
	  case DK_FIG_OBJ_COMPOUND: {
	    cptr = (dk_fig_compound *)(no->data);
	    if((cptr->objects) && (cptr->objit)) {
	      objptr = no; it = cptr->objit;
	      dksto_it_reset(it);
	    }
	  } break;
	  case DK_FIG_OBJ_SPLINE: {		
	    if(dkfig_tool_build_one_spline(no, dptr, c)) {
	      dkfig_tool_bb_spline(no, dptr);
	      dkfig_tool_bb_add(&(dptr->dbb), &(no->dbb));
	    } else {
	      error_was_here = 1;
	      back = 0;	
	    }
	  } break;
	  case DK_FIG_OBJ_TEXT: {
	    dk_fig_fonth_t fh, *fhptr;		
            dkfig_fnt_fonth_for_text(&fh, n, s, (dk_fig_text *)(no->data));
	    if((dptr->fonthi) && (dptr->fonth)) {
              fhptr = (dk_fig_fonth_t *)dksto_it_find_like(
	        dptr->fonthi, (void *)(&fh), 0
	      );
	      if(fhptr) {
	        ((dk_fig_text *)(no->data))->font_handling = fhptr;
	      } else {
	        fhptr = dkfig_fnt_copy_fonth(&fh);
		if(fhptr) {
		  if(dksto_add(dptr->fonth, (void *)fhptr)) {
	            ((dk_fig_text *)(no->data))->font_handling = fhptr;
		  } else {
		    error_was_here = 1; error_type = 1;
		    back = 0; 
		    dkfig_fnt_del_fonth(fhptr);
		  }
		} else {
		  error_was_here = 1; error_type = 1;
		  back = 0; 
		}
	      }
	    } else {
	      error_was_here = 1; error_type = 2;
	      back = 0;	
	    }
	  } break;
	}
	if(error_was_here) {
	  /* ERROR message, use error_type and no->lineno */
	  if(c) {
	  if(c->app && c->msg1) {
	    char *msg[3];
	    switch(error_type) {
	      case 0: {
		dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 13);
	      } break;
	      case 1: {
		dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 11);
	      } break;
	      default: {
		dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 44);
	      } break;
	    }
	  }
	  }
	}
      } else {
        objptr = (objptr->parent); it = NULL;
	if(objptr) {
          if(objptr->objtype == DK_FIG_OBJ_DRAWING) {
	    dptr = (dk_fig_drawing *)(objptr->data);
	    it = dptr->objit;
	  } else {
	    cptr = (dk_fig_compound *)(objptr->data);
	    it = cptr->objit;
	  }
	}
      }
    }
  }
  
  return back;
}
/* }}} */



static char *driver_names[] = {
  "mp", "mmp", "eps", "svg", "tex", "bb", "pdf",
  "none",
  NULL
};
static
int use_dlsc DK_P2(dk_fig_conversion *,c, dk_fig_drawing *,dr)
{
  int back = 1;
  dk_fig_opt *fo;
  char *drname;
  int i;
  if(dr->dlsc) {
    if(dr->dlsci) {
      drname = driver_names[7];
      if(c->bdnum >= 0) {
        if(c->bdnum < 7) { drname = driver_names[c->bdnum]; }
      }
      dksto_it_reset(dr->dlsci);
      while((fo = (dk_fig_opt *)dksto_it_next(dr->dlsci)) != NULL) {
        if(fo->name) {
	  i = dkfig_opt_process_special_comment(c, fo->name, drname, 1);
	  if(i != 1) {
	    /* Problem */
	    switch(i) {
	      case -1: case -2: case -3: case -4: { fo->used = 0x01; } break;
	    }
	    if(c->bdnum != 3) {
	      dkfig_tool2_report_special_comment(c, fo, i);
	    }
	  } else {
	    fo->used = 0x01;
	  }
	}
      }
    }
  }
  return back;
}



/* {{{ dkfig_read_input_finished

  Input is finished. Check whether the state is ok.
  Build spline data.

*/
int
dkfig_read_input_finished DK_P4(dk_fig_object *,o, int,n,\
  int,s, dk_fig_conversion *,c)
{
  int back = 0;
  dk_fig_drawing *dr = NULL;
  dk_fig_fonth_t *fp = NULL;
  unsigned long numfonts = 0UL;
  
  if(o) {
    if((o->objtype) == DK_FIG_OBJ_DRAWING) {
      dr = (dk_fig_drawing *)(o->data);
      if(dr) {
        dr->errl = 0UL;
        if((dr->state == STATE_START) && (dr->errc == 0)) {
	  if(use_dlsc(c,dr)) {
	    back = build_splines_and_fonth(o, n, s, c);
	    if(dr->fonthi) {
	      dksto_it_reset(dr->fonthi);
	      while((fp = (dk_fig_fonth_t *)dksto_it_next(dr->fonthi)) != NULL) {
	        if(dkfig_dt_is_latex_text(fp)) {
	          fp->fonthno = numfonts++;
	        }
	      }
	      dr->numlatfonts = numfonts;
	      dr->numlatalpha = dkfig_dt_needed_alpha(numfonts);
	    }
	  } else {
	  }
	} else {
	  if(dr->state != STATE_START) {
	    /* ERROR: Syntax error! */
	    if(c) {
	      dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 12);
	    }
	  }
	}
      }
    }
  }
  
  return back;
}
/* }}} */



/* {{{ dkfig_rd_get_lineno

  Retrieve current line number.

*/
unsigned long
dkfig_rd_get_lineno DK_P1(dk_fig_object *,o)
{
  unsigned long back = 0UL;
  
  if(o) {
    if(o->data) {
      back = ((dk_fig_drawing *)(o->data))->lineno;
    }
  } 
  return back;
}
/* }}} */



/* {{{ dkfig_rd_get_errl

  Retrieve line number of last error.

*/
unsigned long
dkfig_rd_get_errl DK_P1(dk_fig_object *,o)
{
  unsigned long back = 0UL;
  
  if(o) {
    if(o->data) {
      back = ((dk_fig_drawing *)(o->data))->errl;
    }
  } 
  return back;
}
/* }}} */



/* {{{ dkfig_rd_get_errc

  Retrieve last error code.

*/
int
dkfig_rd_get_errc DK_P1(dk_fig_object *,o)
{
  int back = 0;
  
  if(o) {
    if(o->data) {
      back = ((dk_fig_drawing *)(o->data))->errc;
    }
  } 
  return back;
}
/* }}} */



/* {{{

  Set input file name, keep it in mind for
  log/error messages.

*/
int
dkfig_set_input_filename DK_P2(dk_fig_object *,o,char *,n)
{
  int back = 0;
  dk_fig_drawing *d;
  char *newfn, *ofn;
  
  if(o && n) {
    d = (dk_fig_drawing *)(o->data);
    if(d) {
      newfn = dkstr_dup(n);
      if(newfn) {
        if(d->inputfilename) {
	  ofn = d->inputfilename; dk_delete(ofn);
	}
	d->inputfilename = newfn; back = 1;
      } else {
        d->errc = DKFIG_ERROR_MEMORY;
      }
    }
  }
  
  return back;
}
/* }}} */



/* {{{ dkfig_read_prepare_for_input

  Get ready to process input lines.

*/
int
dkfig_read_prepare_for_input DK_P1(dk_fig_object *,o)
{
  int back = 0;
  dk_fig_drawing *dr;
  
  if(o) {
    if((o->objtype) == DK_FIG_OBJ_DRAWING) {
      dr = (dk_fig_drawing *)(o->data);
      if(dr) {
        dr->lineno = 0UL;
	dr->currentcomp = o;
	dr->state = 0;
	back = 1;
      }
    }
  }
  
  return back;
}
/* }}} */



/* {{{ dkfig_set_spline_segments

  Set the number of bezier spline segments per
  X-spline segment.

*/
int
dkfig_set_spline_segments DK_P2(dk_fig_object *,o,size_t,n)
{
  int back = 0;
  dk_fig_drawing *d;
  
  if(o) {
    d = (dk_fig_drawing *)(o->data);
    if(d) {
      back = 1;
      d->spline_segs = n;
      if(d->spline_segs < 1) {
        d->spline_segs = 1;
      }
    }
  }
  
  return back;
}
/* }}} */



/* {{{ dkfig_rd_set_opts

  Pass options from dk_fig_conversion to the
  dk_fig_drawing we are about to fill from stream.

*/
void
dkfig_rd_set_opts DK_P2(dk_fig_drawing *,d, unsigned long,o)
{
  if(d) { d->opt1 = o; }
}
/* }}} */



/* {{{ dkfig_rd_set_ahlj

  The arrowhead line join setup is needed to calculate
  arrowhead corrections. This function passes this
  information to the drawing.

*/
void
dkfig_rd_set_ahlj DK_P2(dk_fig_drawing *,d, int,ahlj)
{
  if(d) { d->ahlj = ahlj; }
}
/* }}} */



/* {{{ dkfig_flat_list

  Create a flattened container of drawing primitives.
  
*/
dk_storage_t *
dkfig_flat_list DK_P2(dk_fig_conversion *,c, dk_fig_object *,o)
{
  dk_storage_t *back = NULL;
  int ok = 0;
  dk_fig_object *optr, *no;
  dk_fig_compound *cptr;
  dk_fig_drawing  *dptr;
  dk_storage_iterator_t *it;
  
  if(c && o) {
    if(o->objtype == DK_FIG_OBJ_DRAWING) {
      back = dksto_open(0);
      if(back) {
        dksto_set_comp(back, dkfig_object_compare, 0);
        optr = o;
	if(optr->data) {
	  dptr = (dk_fig_drawing *)(optr->data);
	  if((dptr->objects) && (dptr->objit)) {
	    dksto_it_reset(dptr->objit);
	    it = dptr->objit;
	    ok = 1;
	    while(optr) {
	      no = (dk_fig_object *)dksto_it_next(it);
	      if(no) {
	        if(c->app) {
		  dkapp_set_source_lineno(c->app, no->lineno);
		}
	        switch(no->objtype) {
		  case DK_FIG_OBJ_DRAWING: {	
		  } break;
		  case DK_FIG_OBJ_COMPOUND: {	
		    cptr = (dk_fig_compound *)(no->data);
		    if(cptr) {
		      if((cptr->objects) && (cptr->objit)) {
		        dksto_it_reset(cptr->objit);
			optr = no; it = cptr->objit;
		      } else { ok = 0; }
		    } else { ok = 0; }
		  } break;
		  case DK_FIG_OBJ_ARC:
		  case DK_FIG_OBJ_TEXT:
		  case DK_FIG_OBJ_SPLINE:
		  case DK_FIG_OBJ_POLYLINE:
		  case DK_FIG_OBJ_ELLIPSE:
		  {
		    if(!dksto_add(back, (void *)no)) {
		      ok = 0;
                      if(c->app) {
         dkapp_err_memory(c->app, sizeof(dk_storage_node_t), 1);
		      }
		    }
		    
		  } break;
		  default: {			
		  } break;
		}
	      } else {		
	        optr = optr->parent;
		if(optr) {
		  if(optr->objtype == DK_FIG_OBJ_DRAWING) {
		    dptr = (dk_fig_drawing *)(optr->data);
		    it = dptr->objit;
		    if(!it) {
		      ok = 0;
		      optr = NULL;
		      /* ERROR: Setup problem */
		      dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 44);
		    }
		  } else {
		    cptr = (dk_fig_compound *)(optr->data);
		    it = cptr->objit;
		  }
		}
	      }
	    }
	  } else {
	    /* ERROR: Setup problem */
	    dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 44);
	  }
	} else {
	  /* ERROR: Setup problem */
	  dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 44);
	}
	if(!ok) {
	  dksto_close(back); back = NULL;
	}
      } else {
        if(c->app) {
          dkapp_err_memory(c->app, sizeof(dk_storage_t), 1);
	}
      }
    }
  }
  
  return back;
}
/* }}} */



/* {{{ dkfig_rd_set_it_steps

  Set the minimum and maximum number of iteration steps.

*/
void
dkfig_rd_set_it_steps DK_P3(dk_fig_drawing *,d,\
  unsigned long,min, unsigned long,max)
{
  if(d) {
    d->minitsteps = min; d->maxitsteps = max;
  }
}
/* }}} */



/* {{{ SCCS ID */
#ifndef LINT
static char sccs_id[] = {
"@(#)dkfigrd.ctr 1.107 05/29/07\t(krause) - fig2vect"
};
#endif
/* }}} */
