/*
Copyright (c) 2004-2010, 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
  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.
*/



/**	@file	dkfigrd.c	Fig file reader.
*/


/**	Inside the dkfigrd module.
*/
#define DKFIGRD_C	1



#include "dkfig.h"
#include "dkxsp.h"




#line 55 "dkfigrd.ctr"




#ifdef TRACE_DEBUG
#undef TRACE_DEBUG
#endif




#line 66 "dkfigrd.ctr"



/**	RGB color definition.
*/
typedef struct _rgb_ {
  int r;	/**< Red. */
  int g;	/**< Green. */
  int b;	/**< Blue. */
} 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 }
};



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



/**	State: empty.
*/
#define STATE_EMPTY			0

/**	State: Expect orientation.
*/
#define STATE_EXPECT_ORIENTATION	1

/**	State: Expect justification.
*/
#define STATE_EXPECT_JUSTIFICATION	2

/**	State: Expect units.
*/
#define STATE_EXPECT_UNITS		3

/**	State: Expect papersize. 
*/
#define STATE_EXPECT_PAPERSIZE		4

/**	State: Expect magnification.
*/
#define STATE_EXPECT_MAGNIFICATION	5

/**	State: Expect multiple page information.
*/
#define STATE_EXPECT_MULTIPLE_PAGE	6

/**	State: Expect transparent color information.
*/
#define STATE_EXPECT_TRANSPARENT_COLOR	7

/**	State: Expect resolution.
*/
#define STATE_EXPECT_RESOLUTION		8

/**	State: Expect start of Fig objects list.
*/
#define STATE_START			9

/**	State: Expect arc arrowhead 1.
*/
#define STATE_EXPECT_ARC_ARROW_1	10

/**	State: Expect arc arrowhead 2.
*/
#define STATE_EXPECT_ARC_ARROW_2	11

/**	State: Expect polyline arrowhead 1.
*/
#define STATE_EXPECT_PL_ARROW_1		12

/**	State: Expect polyline arrowhead 2.
*/
#define STATE_EXPECT_PL_ARROW_2		13

/**	State: Expect polyline points.
*/
#define STATE_EXPECT_PL_POINTS		14

/**	State: Expect image file name.
*/
#define STATE_EXPECT_PL_IMAGENAME	15

/**	State: Expect spline arrowhead 1.
*/
#define STATE_EXPECT_SPLINE_ARROW_1	16

/**	State: Expect spline arrowhead 2.
*/
#define STATE_EXPECT_SPLINE_ARROW_2	17

/**	State: Expect spline points.
*/
#define STATE_EXPECT_SPLINE_POINTS	18

/**	State: Expect spline factors (-1 -- 1).
*/
#define STATE_EXPECT_SPLINE_FACTORS	19





/** FIG is a part of the file format string.
*/
static char str_fig[] = { "FIG" };

/** File format version is a part of the file format string.
*/
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
};

/**	Names of output drivers.
*/
static char *driver_names[] = {
  "mp", "mmp", "eps", "svg", "tex", "bb", "pdf",
  "java", "none",
  NULL
};



/**	Compare two Fig objects.
	Comparison by layer number (largest layer number is
	lowest layer), object type, line style and source
	file line number.
	@param	l	Left object.
	@param	r	Right object.
	@param	cr	Comparison criteria
*/
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;
}



/**	Compare two color cells.
	@param	l	Left color cell.
	@param	r	Right color cell or color number.
	@param	cr	Comparison criteria (1=color/number,
	default=color/color).
	@return	The comparison result.
*/
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;
}



/**	Check the first line for \#FIG 3.2.
	@param	d	Drawing structure.
	@param	b	Buffer containing the first line.
	@return	1 on success (line valid), 0 on error.
*/
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;
}



/**	Store a special comment either belong to the entire drawing or
	belonging to a drawing primitive.
	@param	d	Drawing structure.
	@param	b	Buffer containing a special comment.
	@param	s	Array of storage pointers.
	@param	i	Array of storage iterator pointers.
	@return	1 on success, 0 on error.
*/
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;
}



/**	Handle a special comment. May be either document level special comment
	or related to the next object.
	@param	d	Drawing structure.
	@param	b	Buffer containing the special comment.
	@return	1 on success, 0 on error.
*/
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 string (2nd line of file).
	@param	d	Drawing structure.
	@param	b	Buffer containing an input line.
	@return	1 on success, 0 on error.
*/
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;
}



/**	The 3rd line contains the justification.
	@param	d	Drawing structure.
	@param	b	Buffer containing one line.
	@return	1 on success, 0 on error.
*/
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;
}



/**	The 4th line contains the units used in the drawing.
	@param	d	Drawing structure.
	@param	b	Buffer containing one input line.
	@return	1 on success, 0 on error.
*/
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;
}



/**	The 5th line contains the paper size.
	@param	d	Drawing structure.
	@param	b	Buffer containing one input line.
	@return	1 on success, 0 on error.
*/
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;
}



/**	The 6th line contains a magnification factor.
	@param	d	Drawing structure.
	@param	b	Buffer containing one input line.
	@return	1 on success, 0 on error.
*/
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;
}



/**	The 7th line tells whether we have a single page or multipage drawing.
	How to handle multipage drawings? I don't know.
	@param	d	Drawing structure.
	@param	b	Buffer containing one input line.
	@return	1 on success, 0 on error.
*/
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;
}



/**	The 8th line contains the number of the transparent color.
	@param	d	Drawing structure.
	@param	b	Buffer containing one input line.
	@return	1 on success, 0 on error.
*/
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;
}



/**	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.
	@param	d	Drawing structure.
	@param	b	Buffer containing one input line.
	@return	1 on success, 0 on error.
*/
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;
}



/**	Delete color cell, release memory.
	@param	cc	Color cell.
*/
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);
  }
  
}



/**	Initialize data structure for arc.
	@param	a	Arc structure.
*/
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));
  
}



/**	Destroy data structure for arc.
	@param	a	Arc structure.
*/
static
void
delete_arc_details DK_P1(dk_fig_arc *,a)
{
  
  null_arc(a);
  dk_delete(a);
  
}



/**	Initialize data structure for ellipse.
	@param	e	Ellipse structure.
*/
static
void
null_ellipse DK_P1(dk_fig_ellipse *,e)
{
  
  DK_MEMRES(e,sizeof(dk_fig_ellipse)) ;
  e->angle = 0.0;
  
}



/**	Destroy data structure for ellipse.
	@param	e	Ellipse structure.
*/
static
void
delete_ellipse_details DK_P1(dk_fig_ellipse *,e)
{
  
  null_ellipse(e);
  dk_delete(e);
  
}



/**	Initialize data structure for polyline.
	@param	p	Polyline structure.
*/
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;
  
}



/**	Destroy data structure for polyline.
	@param	p	Polyline structure.
*/
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);
  
}



/**	Initialize data structure for spline.
	@param	s	Spline structure.
*/
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;
  
}



/**	Destroy data structure for spline.
	@param	s	Spline structure.
*/
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);
  
}



/**	Initialize data structure for text.
	@param	t	Text object structure.
*/
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;
  
}



/**	Destroy data structure for text.
	@param	t	Text object structure.
*/
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);
  
}



/**	Create data structure for a text.
	@return	Pointer to new text details structure on success,
	NULL on error.
*/
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;
}


/**	Initialize compound data structure.
	@param	c	Compound 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));



/**	Destroy drawing details data structure.
	@param	d	Drawing structure.
	@return	1 on success, 0 on error.
*/
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));


/**	Destroy an object and all it's contents
	@param	o	Fig object structure.
*/
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 data structure.
	@param	c	Compound 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 Fig object structure.
	@param	l	Line number in source file.
	@param	p	Parent object (drawing or compound).
	@param	st	Storage for special comments.
	@param	it	Iterator for special comments storage.
	@return	Pointer to new object on success, NULL on error.
*/
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;
}



/**	Convert hexadecimal character to int.
	@param	c	Character to convert.
	@return	Conversion result.
*/
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;
}



/**	Create new color cell structure.
	@param	n	Color number.
	@param	r	Red component.
	@param	g	Green component.
	@param	b	Blue component.
	@return	Pointer to new structure on success, NULL on error.
*/
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;
}



/**	Create a new color cell from the text line, save the color cell in the
	drawing.
	@param	d	Drawing structure.
	@param	t	Text line containing the color cell information.
	@return	1 on success, 0 on error.
*/
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;
}



/**	Save object data in the current compound object.
	@param	d	Drawing structure.
	@param	data	Object-type specific data.
	@param	l	Line number of object in source file.
	@param	fpd	Fill, pattern and draw structure.
	@param	c	Conversion job.
	@return	1 on success, 0 on error.
*/
static
int
register_object_data DK_P5(dk_fig_drawing *,d, void *,data,long,l,dk_fig_fpd *,fpd, dk_fig_conversion *,c)
{
  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;
      if(!(fpd->cl)) {
        if(fpd->af != -1) {
	  fpd->af = -1;
	  if(c) {
	    if(c->app) {
	      unsigned long bckln;
	      bckln = dkapp_get_source_lineno(c->app);
	      dkapp_set_source_lineno(c->app, o->lineno);
	      dkfig_tool2_msg1(c, DK_LOG_LEVEL_WARNING, 141);
	      dkapp_set_source_lineno(c->app, bckln);
	    } else {
	      dkfig_tool2_msg1(c, DK_LOG_LEVEL_WARNING, 141);
	    }
	  }
	}
      }
      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;
}



/**	Destroy drawing object including all contents.
	@param	o	Drawing root object.
*/
void
dkfig_delete DK_P1(dk_fig_object *,o)
{
  
  if(o) {
    dkfig_object_delete(o);
  }
  
}



/**	Create and initialize details for new drawing.
	@param	uwp	Flag: Use web color palette (1) or normal
	color palette (0).
	@param	cc	Current component.
	@return	Pointer to new drawing details structure on success,
	NULL on error.
*/
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->opt2		= 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 Fig object for entire drawing.
	@param	uwp	Flag: Use web-optimized color palette.
	@return	Pointer to new object on success, NULL on error.
*/
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;
}



/**	Create data structure for an ellipse.
	@return	Pointer to ellipse object structure on success, NULL on
	error.
*/
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;
}



/**	Process a line starting a new ellipse.
	@param	d	Drawing structure.
	@param	text	Buffer containing the line to process.
	@param	c	Conversion job.
	@return	1 on success, 0 on error.
*/
static
int
add_ellipse DK_P3(dk_fig_drawing *,d,char *,text, dk_fig_conversion *,c)
{
  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, c);
	if(!back) {
	  delete_ellipse_details(e); e = NULL;
	}
      }
    } } } } } } } } } } } } } } } } } } }
  }
  if(!back) { d->errc = reason; }
  
  return back;
}



/**	Create data structure for polyline.
	@param	np	Number of points.
	@return	Pointer to new polyline structure on success,
	NULL on error.
*/
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;
}



/**	Process a line starting a new polyline.
	@param	d	Drawing structure.
	@param	b	Buffer containing the first text line for polyline.
	@param	c	Conversion job.
	@return	1 on success, 0 on error.
*/
static
int
start_polyline DK_P3(dk_fig_drawing *,d,char *,b, dk_fig_conversion *,c)
{
  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, c);
	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;
}



/**	Create data structure for a spline.
	@param	np	Number of points,
	@param	subsegs	Number of Bezier segments per X-spline segment.
	@param	cl	Flag: Closed spline (1) or open spline (0).
	@param	c	Conversion job structure.
	@return	Pointer to new spline details structure on success, NULL
	on error.
*/
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;
}



/**	Process a line starting a new spline object.
	@param	d	Drawing structure.
	@param	b	Buffer containing the line.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
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, c);
	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 string contents "forward" (to the left) after processing an octal
	code.
	@param	d	Destination pointer.
	@param	s	Source pointer.
*/
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.
	@param	str	String to correct.
*/
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++;
    }
  }
  
}



/**	Create a number of small strings from one text line.
	@param	array	Array of pointers to receive the small strings.
	@param	sz	Size of array (Number of elements).
	@param	l	Line to process.
	@return	Number of elements used in \arg array.
*/
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;
}



/**	Process a line starting a text object.
	@param	d	Drawing structure.
	@param	line	Input line.
	@param	c	Conversion job.
	@return	1 on success, 0 on error.
*/
static
int
start_text DK_P3(dk_fig_drawing *,d,char *,line, dk_fig_conversion *,c)
{
  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, c
	  );
	  if(back) {	
	    correct_octal_codes(str);
	  } else {	
	    delete_text_details(td); td = NULL;
	  }
	} else {
	  dk_delete(str);
	}
      }
    } } } } } } } } } } } }
  }
  if(!back) { d->errc = reason; }
  
  return back;
}



/**	Create data structure for arc.
	@return	Pointer to new arc structure on success, NULL on error.
*/
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;
}



/**	Process a line starting a new arc.  
	@param	d	Drawing structure.
	@param	line	Input line.
	@param	c	Conversion job.
	@return	1 on success, 0 on error.
*/
static
int
start_arc DK_P3(dk_fig_drawing *,d,char *,line, dk_fig_conversion *,c)
{
  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, c
	);
	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;
}



/**	This compound is finished, go back to parent compound.
	@param	d	Drawing structure.
	@return	1 on success, 0 on error.
*/
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;
}



/**	Create data structure for a new compound object.
	@param	ulx	Upper left x value.
	@param	uly	Upper left y value.
	@param	lrx	Lower right x value.
	@param	lry	Lower right y value.
	@return	Pointer to new compound structure on success, NULL on error.
*/
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;
}



/**	Process a line starting a new compound
	@param	d	Drawing structure.
	@param	line	Input line.
	@param	c	Conversion job.
	@return	1 on success, 0 on error.
*/
static
int
start_compound DK_P3(dk_fig_drawing *,d,char *,line, dk_fig_conversion *,c)
{
  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, c
	      );
	      if(!back) {
	        delete_compound_details(cptr); cptr = NULL;
	      }
	    }
	  } } } }
	}
      }
    }
  }
  if(!back) {
    d->errc = reason;
  }
  
  return back;
}



/**	Beginning of a new object.
	@param	d	Drawing structure.
	@param	b	Input line buffer.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
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, c);
      } break;
      case 2:	{	/* polyline */
        back = start_polyline(d, nxptr, c);
      } break;
      case 3:	{	/* spline */
        back = start_spline(d, nxptr, c);
      } break;
      case 4:	{	/* text */
        back = start_text(d, nxptr, c);
      } break;
      case 5: 	{	/* arc */
        back = start_arc(d, nxptr, c);
      } break;
      case 6:	{	/* compound */
        back = start_compound(d, nxptr, c);
      } break;
      case -6:	{	/* end of compound */
        back = finish_compound(d);
      } break;
    }
  } else {
    d->errc = DKFIG_ERROR_SYNTAX;
  }
  
  return back;
}



/**	Fill data structure for arrowhead.
	@param	a	Arrowhead structure.
	@param	text	Text line containing arrowhead information.
	@return	1 on success, 0 on error.
*/
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 forward arrow information to arc.
	@param	d	Drawing structure.
	@param	b	Text line buffer.
	@return	1 on success, 0 on error.
*/
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 backward arrow information to arc.
	@param	d	Drawing structure.
	@param	b	Text line buffer,
	@return	1 on success, 0 on error.
*/
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 forward arrow information to a polyline.
	@param	d	Drawing structure.
	@param	b	Text line buffer.
	@return	1 on success, 0 on error.
*/
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 backward arrow information to polyline.
	@param	d	Drawing structure.
	@param	b	Text line buffer.
	@return	1 on success, 0 on error.
*/
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 forward arrow information to a spline.
	@param	d	Drawing structure.
	@param	b	Text line buffer.
	@return	1 on success, 0 on error.
*/
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 backward arrow information to a spline.
	@param	d	Drawing structure.
	@param	b	Text line buffer.
	@return	1 on success, 0 on error.
*/
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 a line of polyline point co-ordinates.
	@param	d	Drawing structure.
	@param	b	Text line buffer.
	@return	1 on success, 0 on error.
*/
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;
              d->currentdet = NULL; d->currentobj = NULL;
	    }
	  } else {
	    (p->xvalues)[d->currentpnt] = l;
	    d->xory = 1;
	  }
	} else {
	  cc = 0; 
	  d->state = STATE_START;
          d->currentdet = NULL; d->currentobj = NULL;
	}
      } else {
        cc = 0; back = 0;
      }
      cptr = nptr;
    }
  } else {
    back = 0;
  }
  
  return back;
}



/**	Save the name of a bitmap image.
	@param	d	Drawing structure.
	@param	b	Text line buffer.
	@return	1 on success, 0 on error.
*/
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 a line of point co-ordinates.
	@param	d	Drawing structure.
	@param	b	Text line buffer.
	@return	1 on success, 0 on error.
*/
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 a line of s_k blending coefficients.
	@param	d	Drawing structure.
	@param	b	Text line buffer.
	@return	1 on success, 0 on error.
*/
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.
	@param	d	Drawing structure.
	@param	b	Text line buffer.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
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 to drawing structure.
	@param	o	Fig drawing root object.
	@param	b	Buffer containing the line to add.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
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 collect font information.
	@param	o	Fig object structure.
	@param	n	Text handling flags for normal texts.
	@param	s	Text handling flags for special texts.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
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;
}



/**	Process document level special comments.
	@param	c	Conversion job structure.
	@param	dr	Drawing structure.
	@return	1 on success, 0 on error.
*/
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[8];
      if(c->bdnum >= 0) {
        if(c->bdnum < 8) { 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;
}



/**	Handle end of input.
	Check parser state (last object must be finished) and prepare
	some structures for text handling.
	@param	o	Fig drawing root object.
	@param	n	Text handling type for normal texts.
	@param	s	Text handling type for special texts.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
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;
}



/**	Retrieve object line number (the line number in which the
	object was started).
	@param	o	Fig object structure.
	@return	The 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;
}



/**	Retrieve line number of last error.
	@param	o	Drawing root object.
	@return	The line number.
*/
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;
}



/**	Retrieve error code of last error.
	@param	o	Drawing root object.
	@return	The 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;
}



/**	Save input file name for later use.
	@param	o	Drawing root object.
	@param	n	File name.
	@return	1 on success, 0 on error.
*/
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;
}



/**	Initialize root object to parse input.
	@param	o	Drawing root object.
	@return	1 on success, 0 on error.
*/
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;
}



/**	Set number of Bezier spline segments per X-spline segment.
	@param	o	Drawing root object.
	@param	n	Number of segments.
	@return	1 on success, 0 on error.
*/
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;
}



/**	Set options to drawing.
	@param	d	Fig drawing structure.
	@param	o	Options as obtained from conversion job structure.
*/
void
dkfig_rd_set_opts DK_P2(dk_fig_drawing *,d, unsigned long,o)
{
  if(d) { d->opt1 = o; }
}



/**	Set arrowhead line join.
	@param	d	Fig drawing structure.
	@param	ahlj	The arrowhead line join.
*/
void
dkfig_rd_set_ahlj DK_P2(dk_fig_drawing *,d, int,ahlj)
{
  if(d) { d->ahlj = ahlj; }
}



/**	Create flattened container of Fig objects in order of drawing.
	@param	c	Conversion job structure.
	@param	o	Drawing root object.
*/
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;
}



/**	Set minimum and maximum number of iteration steps
	for arrowhead length on splines calculation.
	@param	d	Fig drawing structure.
	@param	min	Mininum number of steps.
	@param	max	Maximum number of 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;
  }
}



