/*
Copyright (c) 2009-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	dkfigw.c	Fig writer module.
This module contains functions to create a graphic in-memory
and save that graphics to a Fig file.
Programs like XFig or jFig can be used to apply modifications
to the graphics file after it is written.

Fig files are text files, so you could use standard I/O
operations like fprintf() to produce the file.
The purpose of this module is to ease up the Fig file
production by doing calculations from centimeters and
inches to Fig units and the coordinates transformation
from "origin in lower left corner" used by the module
to "origin in upper left corner" used by the Fig file format.
*/



#include <dk.h>

#if DK_HAVE_MATH_H
#include <math.h>
#endif
#if DK_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if DK_HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <dksf.h>
#include <dkmem.h>
#include <dkstr.h>
#include <dkstream.h>
#include <dkma.h>



/**	Inside the dkfigw module.
*/
#define DKFIGW_C	1
#include "dkfigw.h"




#line 84 "dkfigw.ctr"




/**	File name suffixes used for compressed files.
*/
static char *suffixes[] = {
  ".gz", ".bz2", NULL
};



/**	Keywords used while producing a Fig file.
*/
static char *kw[] = {
  /*  0 */	"\n",
  /*  1 */	" ",
  /*  2 */	"w",
  /*  3 */	"#FIG 3.2",
  /*  4 */	"Portrait",
  /*  5 */	"Landscape",
  /*  6 */	"Center",
  /*  7 */	"Flush Left",
  /*  8 */	"Metric",
  /*  9 */	"Inches",
  /* 10 */	"100.0",
  /* 11 */	"Single",
  /* 12 */	"Multiple",
  /* 13 */	"1200 2",
  /* 14 */	"\t",
  /* 15 */	"2 2 0 0 7 7 999 0 -1 0 0 0 0 0 0 5",
  /* 16 */	"## *: remove background rectangle = yes",
  /* 17 */	"## *: background rectangle color  = white",
  /* 18 */	"\\001",
  /* 19 */	"\\\\",
  /* 20 */	"## *: encoding                    = utf-8",
  NULL
};



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



/**	Factor for cm to Fig unit conversion.
*/
static double cm_to_fig = 1200.0 / 2.54;

/**	Factor for inch to Fig unit conversion.
*/
static double in_to_fig = 1200.0;



/**	Color names.
*/
static char *color_names[] = {
"black",
"blue",
"green",
"cyan",
"red",
"magenta",
"yellow",
"white",
"blue.darkest",
"blue.dark",
"blue.light",
"blue.lightest",
"green.dark",
"green.normal",
"green.light",
"cyan.dark",
"cyan.normal",
"cyan.light",
"red.dark",
"red.normal",
"red.light",
"magenta.dark",
"magenta.normal",
"magenta.light",
"brown.dark",
"brown.normal",
"brown.light",
"pink.darkest",
"pink.dark",
"pink.light",
"pink.lightest",
"gold",
NULL
};



/**	PS font names.
*/
static char *font_names[] = {
"Times Roman",
"Times Italic",
"Times Bold",
"Times Bold Italic",
"Avantgarde Book",
"Avantgarde Book Oblique",
"Avantgarde Demi",
"Avantgarde Demi Oblique",
"Bookman Light",
"Bookman Light Italic",
"Bookman Demi",
"Bookman Demi Italic",
"Courier",
"Courier Oblique",
"Courier Bold",
"Courier Bold Oblique",
"Helvetica",
"Helvetica Oblique",
"Helvetica Bold",
"Helvetica Bold Oblique",
"Helvetica Narrow",
"Helvetica Narrow Oblique",
"Helvetica Narrow Bold",
"Helvetica Narrow Bold Oblique",
"New Century Schoolbook Roman",
"New Century Schoolbook Italic",
"New Century Schoolbook Bold",
"New Century Schoolbook Bold Italic",
"Palatino Roman",
"Palatino Italic",
"Palatino Bold",
"Palatino Bold Italic",
"Symbol",
"Zapf Chancery Medium Italic",
"Zapf Dingbats",
NULL
};



/**	Find index of text \arg t in array \arg a.
	When in verbose mode, print a warning and a list of allowed
	texts if the text is not found.
	@param	a	Array of text pointers.
	@param	t	Text to find.
	@param	c	Flag: Case sensitive (nonzero) or not (0).
	@param	ver	Flag: Verbose mode on.
	@return	Index on success, -1 on error.
*/
static
int
my_str_array_index DK_P4(char **,a, char *,t, int,c, unsigned char,ver) {
  int back = -1;
  char **ptr;
  back = dkstr_array_index(a,t,c);
  if(back < 0) {
    if(ver) {
      fprintf(stderr, "Warning: Illegal name \"%s\"! Allowed names:\n", t);
      ptr = a;
      while(*ptr) {
        fprintf(stderr, "         %s\n", *(ptr++));
      }
      fflush(stderr);
    }
  }
  return back;
}



/**	Convert Distance to Fig units.
	@param	fwp	Fig writer structure.
	@param	x	Distance to convert.
	@return	The conversion result as double value.
*/
static
double
d_to_fig_d DK_P2(dk_fig_writer_t *,fwp, double,x) {
  double back = 0.0;
  switch(fwp->uni) {
    case 1: {
      back = in_to_fig * x;
    } break;
    case 2: {
      back = cm_to_fig * x;
    } break;
    default: {
      back = x;
    } break;
  }
  return back;
}



/**	Convert distance to Fig units.
	@param	fwp	Fig writer structure.
	@param	x	Distance to convert.
	@return	The conversion result as long value.
*/
static
long
d_to_fig_l DK_P2(dk_fig_writer_t *,fwp, double,x) {
  long back = 0L;
  back = dkma_double_to_l(dkma_rint(d_to_fig_d(fwp, x)));
  return back;
}



/**	Convert x position to Fig units.
	@param	fwp	Fig writer structure.
	@param	x	X position.
	@return	The conversion result as double value.
*/
static
double
x_to_fig_d DK_P2(dk_fig_writer_t *,fwp, double,x) {
  double back = 0.0;
  back = d_to_fig_d(fwp, x) + fwp->xsh;
  return back;
}



/**	Convert x position to Fig units.
	@param	fwp	Fig writer structure.
	@param	x	X position.
	@return	The conversion result as long value.
*/
static
long
x_to_fig_l DK_P2(dk_fig_writer_t *,fwp, double,x) {
  long back;
  back = dkma_double_to_l(dkma_rint(x_to_fig_d(fwp, x)));
  return back;
}



/**	Convert y position to Fig units.
	@param	fwp	Fig writer structure.
	@param	y	Y position.
	@return	The conversion result as double value.
*/
static
double
y_to_fig_d DK_P2(dk_fig_writer_t *,fwp, double,y) {
  double back;
  back = d_to_fig_d(fwp, y);
  if(!(fwp->olt)) {
    back = d_to_fig_d(fwp, fwp->imh) - back;
  }
  back += fwp->ysh;
  return back;
}



/**	Convert y position to Fig units.
	@param	fwp	Fig writer structure.
	@param	y	Y position.
	@return	The conversion result as long value.
*/
static
long
y_to_fig_l  DK_P2(dk_fig_writer_t *,fwp, double,y) {
  long back;
  back= dkma_double_to_l(dkma_rint(y_to_fig_d(fwp, y)));
  return back;
}



/**	Convert unsigned char to integer.
	@param	uc	Character to convert.
	@return	Conversion result.
*/
static
int
uc_to_int DK_P1(unsigned char,uc) {
  int back = 0;
  if(uc == 0xFF) {
    back = -1;
  } else {
    back = (int)((unsigned)uc);
  }
  return back;
}



/**	Write unsigned number to output stream.
	@param	os	Output stream.
	@param	u	Value to write.
*/
static
void
put_unsigned DK_P2(dk_stream_t *,os, unsigned,u) {
  char buffer[64];
  sprintf(buffer, "%u", u);
  dkstream_puts(os, buffer);
}



/**	Return corrected value fitting in range.
	@param	x	Value to correct.
	@param	min	Range minimum.
	@param	max	Range maximum.
	@return	Corrected value.
*/
#define TO_RANGE(x,min,max) ((x > max) ? max : ((x < min) ? min : x))


#if 0
static
void
put_unsigned_range DK_P4(dk_stream_t *,os, unsigned,u, unsigned,min, unsigned,max)
{
  unsigned i;
  i = TO_RANGE(u,min,max);
  put_unsigned(os, i);
}
#endif



/**	Put integer value to output stream.
	@param	os	Output stream.
	@param	i	Value to put.
*/
static
void
put_int DK_P2(dk_stream_t *,os, int,i) {
  char buffer[64];
  sprintf(buffer, "%d", i);
  dkstream_puts(os, buffer);
}



/**	Put integer value to output stream, correct value if necessary.
	@param	os	Output stream.
	@param	i	Value to put.
	@param	min	Minimum value.
	@param	max	Maximum value.
*/
static
void
put_int_range DK_P4(dk_stream_t *,os, int,i, int,min, int,max) {
  int j;
  j = TO_RANGE(i,min,max);
  put_int(os, j);
}



/**	Put long value to output stream.
	@param	os	Output stream.
	@param	l	Value to put.
*/
static
void
put_long DK_P2(dk_stream_t *,os, long,l) {
  char buffer[64];
  sprintf(buffer, "%ld", l);
  dkstream_puts(os, buffer);
}



/**	Put double value to output stream.
	@param	os	Output stream.
	@param	d	Value to put.
*/
static
void
put_double DK_P2(dk_stream_t *,os, double,d) {
  char buffer[64];
  sprintf(buffer, "%.2lf", d);
  dkstream_puts(os, buffer);
}



/**	Put double value to output stream, use
	large number of decimal digits.
	@param	os	Output stream.
	@param	d	Value to put.
*/
static
void
put_long_double DK_P2(dk_stream_t *,os, double,d) {
  char buffer[128];
  sprintf(buffer, "%.16lf", d);
  dkstream_puts(os, buffer);
}


#if 0
static
void
put_double_range DK_P4(dk_stream_t *,os, double,d, double,min, double,max) {
  double e;
  e = TO_RANGE(d,min,max);
  put_double(os, e);
}
#endif


/**	Initialize arrowhead.
	@param	ahp	Arrowhead to initialize.
*/
static
void
init_arrowhead DK_P1(dk_fig_ah_t *,ahp) {
  if(ahp) {
    ahp->sha = FIG_AS_TRIANGLE;
    ahp->fil = FIG_AF_PC;
    ahp->wid = 10.0;
    ahp->hei = 5.0;
  }
}



/**	Initialize style information structure.
	@param	stp	Structure to initialize.
*/
static
void
init_style DK_P1(dk_fig_style_t *,stp) {
  
  if(stp) {
    stp->lay = 998;
    stp->liw = 1;
    stp->pco = FIG_COLOR_BLACK;
    stp->fco = FIG_COLOR_WHITE;
    stp->fil = FIG_FILL_NONE;
    stp->lst = FIG_LS_SOLID;
    stp->ljn = FIG_LJ_MITER;
    stp->lcp = FIG_CS_BUTT;
    stp->ahs = 0x00;
    stp->ffl = FIG_TEXTFLAG_PS;
    stp->fno = FIG_FONT_PS_TIMES_ROMAN;
    stp->fsz = 12.0;
    stp->tal = FIG_SUB_TEXT_LEFT;
    stp->sva = 5.0;
    init_arrowhead(&(stp->fwa));
    init_arrowhead(&(stp->bwa));
  } 
}



/**	Compare two polyline/polygon points by number.
	@param	l	Left point.
	@param	r	Right point.
	@param	c	Comparison criteria (ignored).
	@return	Comparison result.
*/
int
dkfigw_compare_pl_points DK_P3(void *,l, void *,r, int,c) {
  int			back = 0;
  dk_fig_pl_pt_t	*pl;
  dk_fig_pl_pt_t	*pr;
  pl = (dk_fig_pl_pt_t *)l; pr = (dk_fig_pl_pt_t *)r;
  if(l) {
    if(r) {
      if(pl->npt > pr->npt) { back = 1; }
      else {
        if(pl->npt < pr->npt) { back = -1; }
      }
    } else { back = 1; }
  } else {
    if(r) { back = -1; }
  }
  return back;
}



/**	Compare two spline points by number.
	@param	l	Left point.
	@param	r	Right point.
	@param	c	Comparison criteria (ignored).
	@return	Comparison result.
*/
int
dkfigw_compare_sp_points DK_P3(void *,l, void *,r, int,c) {
  int			back = 0;
  dk_fig_sp_pt_t	*pl;
  dk_fig_sp_pt_t	*pr;
  pl = (dk_fig_sp_pt_t *)l; pr = (dk_fig_sp_pt_t *)r;
  if(l) {
    if(r) {
      if(pl->npt > pr->npt) { back = 1; }
      else {
        if(pl->npt < pr->npt) { 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.
	@param	c	Comparison criteria (0=cell/cell, 1=cell/number).
	@return	Comparison result.
*/
int
dkfigw_compare_color_cells DK_P3(void *,l, void *,r, int,c) {
  int			back = 0;
  dk_fig_cc_t		*lp;
  dk_fig_cc_t		*rp;
  unsigned short	*ccnop;
  lp = (dk_fig_cc_t *)l; rp = (dk_fig_cc_t *)r; ccnop = (unsigned short *)r;
  switch(c) {
    case 1: {
      if(lp) {
        if(ccnop) {
	  if(lp->ccn > *ccnop) { back = 1; }
	  else {
	    if(lp->ccn < *ccnop) { back = -1; }
	  }
	} else {
	  back = 1;
	}
      } else {
        if(ccnop) { back = -1; }
      }
    } break;
    default: {
      if(lp) {
        if(rp) {
	  if(rp->ccn > lp->ccn) {
	    back = 1;
	  } else {
	    if(rp->ccn < lp->ccn) { back = -1; }
	  }
	} else {
	  back = 1;
	}
      } else {
        if(rp) { back = -1; }
      }
    } break;
  }
  return back;
}



/**	Compare two objects.
	@param	l	Left object.
	@param	r	Right object.
	@param	c	Comparison criteria (0=object/object, 1=object/number).
	@return	Comparison result.
*/
int
dkfigw_compare_objects DK_P3(void *,l, void *,r, int,c) {
  int			back = 0;
  dk_fig_object_t	*lp;
  dk_fig_object_t	*rp;
  unsigned long		*obnp;
  lp = (dk_fig_object_t *)l; rp = (dk_fig_object_t *)r;
  obnp = (unsigned long *)r;
  switch(c) {
    case 1: {
      if(lp) {
        if(obnp) {
	  if(lp->obn > *obnp) { back = 1; }
	  else {
	    if(lp->obn < *obnp) { back = -1; }
	  }
	} else { back = 1; }
      } else {
        if(obnp) { back = -1; }
      }
    } break;
    default: {
      if(lp) {
        if(rp) {
	  if(lp->obn > rp->obn) { back = 1; }
	  else {
	    if(lp->obn < rp->obn) { back = -1; }
	  }
	} else { back = 1; }
      } else {
        if(rp) { back = -1; }
      }
    } break;
  }
  return back;
}



/**	Release memory for Fig object.
	@param	obj	Object to release.
*/
static
void
object_delete DK_P1(dk_fig_object_t *,obj) {
  void			*x;
  
  if(obj) {
    switch(obj->otp) {
      case FIG_OBJECT_POLYLINE:
      case FIG_OBJECT_SPLINE:
      {
        if((obj->det).pll.spt) {
	  if((obj->det).pll.ipt) {
	    dksto_it_reset((obj->det).pll.ipt);
	    while((x = dksto_it_next((obj->det).pll.ipt)) != NULL) {
	      dk_delete(x); 
	    }
	    dksto_it_close((obj->det).pll.ipt);
	  }
	  dksto_close((obj->det).pll.spt);
	}
	if((obj->det).pll.fn) {
	  dk_delete((obj->det).pll.fn); 
	}
      }
      break;
      case FIG_OBJECT_TEXT: {
        if((obj->det).txt.txt) {
	  dk_delete((obj->det).txt.txt); 
	}
      } break;
    }
    DK_MEMRES(obj,sizeof(dk_fig_object_t));
    dk_delete(obj);
  } 
}



/**	Create structure for Fig object.
	@param	fwp	Fig writer structure.
	@param	ot	Fig object type.
	@param	st	Object sub type.
	@return	Pointer to new object on success, NULL on error.
*/
static
dk_fig_object_t *
object_new DK_P3(dk_fig_writer_t *,fwp, unsigned char,ot, unsigned char,st)
{
  dk_fig_object_t *back = NULL;
  back = dk_new(dk_fig_object_t,1);
  if(back) {
    back->obn = fwp->non;
    fwp->non += 1UL;
    back->otp = ot; back->stp = st;
    DK_MEMCPY(&(back->sty), &(fwp->sty), sizeof(dk_fig_style_t));
    switch(ot) {
      case FIG_OBJECT_ARC: {
        (back->det).arc.xm = 0.0;
	(back->det).arc.ym = 0.0;
	(back->det).arc.ra = 0.0;
	(back->det).arc.a0 = 0.0;
	(back->det).arc.a1 = 0.0;
      } break;
      case FIG_OBJECT_ELLIPSE: {
        (back->det).ell.xm = 0.0;
	(back->det).ell.ym = 0.0;
	(back->det).ell.rx = 0.0;
	(back->det).ell.ry = 0.0;
	(back->det).ell.ro = 0.0;
      } break;
      case FIG_OBJECT_SPLINE:
      case FIG_OBJECT_POLYLINE: {
        int ok;
        (back->det).pll.spt = NULL;
	(back->det).pll.ipt = NULL;
	(back->det).pll.npn = 1UL;
	(back->det).pll.fn = NULL;
	(back->det).pll.x0 = 0.0;
	(back->det).pll.y0 = 0.0;
	(back->det).pll.x1 = 0.0;
	(back->det).pll.cr = 0.0;
	ok = 0;
	switch(ot) {
	  case FIG_OBJECT_SPLINE: {
	    (back->det).pll.spt = dksto_open(0);
	    if((back->det).pll.spt) {
	      dksto_set_comp((back->det).pll.spt, dkfigw_compare_sp_points, 0);
	      (back->det).pll.ipt = dksto_it_open((back->det).pll.spt);
	      if((back->det).pll.ipt) {
	        ok = 1;
	      }
	    }
	    if(!ok) {
	      object_delete(back); back = NULL;
	    }
	  } break;
	  case FIG_OBJECT_POLYLINE: {
	    (back->det).pll.spt = dksto_open(0);
	    if((back->det).pll.spt) {
	      dksto_set_comp((back->det).pll.spt, dkfigw_compare_pl_points, 0);
	      (back->det).pll.ipt = dksto_it_open((back->det).pll.spt);
	      if((back->det).pll.ipt) {
	        ok = 1;
	      }
	    }
	    if(!ok) {
	      object_delete(back); back = NULL;
	    }
	  switch(st) {
	  case FIG_SUB_POLYLINE:
	  case FIG_SUB_POLYGON: {
	    (back->det).pll.spt = dksto_open(0);
	    if((back->det).pll.spt) {
	      dksto_set_comp((back->det).pll.spt, dkfigw_compare_pl_points, 0);
	      (back->det).pll.ipt = dksto_it_open((back->det).pll.spt);
	      if((back->det).pll.ipt) {
	        ok = 1;
	      }
	    }
	    if(!ok) {
	      object_delete(back); back = NULL;
	    }
	  } break;
	  }
	  } break;
	}
      } break;
      case FIG_OBJECT_TEXT: {
        (back->det).txt.x = 0.0;
	(back->det).txt.y = 0.0;
	(back->det).txt.rot = 0.0;
	(back->det).txt.txt = NULL;
      } break;
    }
  }
  if(back) {
    if(!dksto_add(fwp->ost, (void *)back)) {
      object_delete(back); back = NULL;
    }
  }
  fwp->cob = back;
  return back;
}



/**	Release memory for color cell.
	@param	ccp	Color cell to release.
*/
static
void
cc_delete DK_P1(dk_fig_cc_t *,ccp) {
  if(ccp) {	
    dk_delete(ccp);
  }
}



/**	Remove writer structure, release memory.
	@param	fwp	Pointer to structure to release.
*/
void
dkfigw_delete DK_P1(dk_fig_writer_t *,fwp) {
  dk_fig_object_t		*op;
  dk_fig_cc_t			*cp;
  
  if(fwp) {
    if(fwp->ost) {
      if(fwp->osi) {
        dksto_it_reset(fwp->osi);
	while((op = (dk_fig_object_t *)dksto_it_next(fwp->osi)) != NULL) {
	  object_delete(op);
	}
	dksto_it_close(fwp->osi); fwp->osi = NULL;
      }
      dksto_close(fwp->ost); fwp->ost = NULL;
    }
    if(fwp->ccs) {
      if(fwp->cci) {
        dksto_it_reset(fwp->cci);
	while((cp = (dk_fig_cc_t *)dksto_it_next(fwp->cci)) != NULL) {
	  cc_delete(cp);
	}
	dksto_it_close(fwp->cci); fwp->cci = NULL;
      }
      dksto_close(fwp->ccs); fwp->ccs = NULL;
    }
  } 
}



/**	Create new writer structure.
	@return	Pointer to new writer structure on success, NULL on error.
*/
dk_fig_writer_t	*
dkfigw_new DK_P0() {
  dk_fig_writer_t		*back = NULL;
  int				ok = 0;
  char				*ptr;
  
  back = dk_new(dk_fig_writer_t,1);
  if(back) {
    back->ver = 0x00;
    back->ost = NULL; back->osi = NULL;
    back->ccs = NULL; back->cci = NULL;
    back->cob = NULL; back->sob = NULL;
    back->non = 1UL;
    back->ncc = FIG_COLOR_GOLD + 1;
    init_style(&(back->sty));
    back->olt = 0x00;
    back->uni = 0;
    back->ang = 0x00;
    back->imw = back->imh = 0.0;
    back->erc = 0; back->mer = 0;
    back->tco = -2;
    back->ori = FIG_ORIENTATION_LANDSCAPE;
    back->jus = FIG_JUSTIFICATION_CENTER;
    back->utf = 0x00;
    ptr = getenv("LANG");
    if(ptr) {
      ptr = strchr(ptr, '.');
      if(ptr) {
        ptr++;
	if(dkstr_casecmp(ptr, "utf-8") == 0) {
	  back->utf = 0x01;
	}
      }
    }
    back->fun = 0x00;
    back->pap = FIG_PAPER_A4;
    back->ost = dksto_open(0);
    if(back->ost) {
      dksto_set_comp(back->ost, dkfigw_compare_objects, 0);
      back->osi = dksto_it_open(back->ost);
      if(back->osi) {
        back->ccs = dksto_open(0);
	if(back->ccs) {
	  dksto_set_comp(back->ccs, dkfigw_compare_color_cells, 0);
	  back->cci = dksto_it_open(back->ccs);
	  if(back->cci) {
	    ok = 1;
	  }
	}
      }
    }
    if(!ok) {
      dkfigw_delete(back); back = NULL;
    }
  } 
  return back;
}



/**	Set image origin.
	@param	fwp	Writer structure.
	@param	olt	Flag: Origin at left top (1) or left bottom (0).
*/
void
dkfigw_set_image_origin DK_P2(dk_fig_writer_t *,fwp, int,olt) {
  if(fwp) { fwp->olt = (olt ? 0x01 : 0x00); }
}



/**	Set image size.
	@param	fwp	Writer structure.
	@param	uni	Size unit (0=Fig, 1=inch, 2=cm).
	@param	w	Image width.
	@param	h	Image height.
	@return	1 on success, 0 on error.
*/
int
dkfigw_set_image_size DK_P4(dk_fig_writer_t *,fwp, int,uni, double,w, double,h)
{
  int back = 0;
  if(fwp) {
    back = 1;
    fwp->imw = w;
    fwp->imh = h;
    fwp->uni = uni;
  }
  return back;
}



/**	Add rectangle.
	@param	fwp	Writer structure.
	@param	x0	Point 1 x.
	@param	y0	Point 1 y.
	@param	x1	Point 2 x.
	@param	y1	Point 2 y.
	@return	Positive number on success, 0 on error.
*/
unsigned long
dkfigw_rectangle DK_P5(dk_fig_writer_t *,fwp, double,x0, double,y0, double,x1, double,y1)
{
  unsigned long		back	= 0UL;
  dk_fig_object_t	*obj;
  obj = object_new(fwp, FIG_OBJECT_POLYLINE, FIG_SUB_BOX);
  if(obj) {
    back = obj->obn;
    (obj->det).pll.x0 = x0;
    (obj->det).pll.y0 = y0;
    (obj->det).pll.x1 = x1;
    (obj->det).pll.y1 = y1;
  }
  return back;
}



/**	Add arc box.
	@param	fwp	Writer structure.
	@param	x0	Point 1 x.
	@param	y0	Point 1 y.
	@param	x1	Point 2 x.
	@param	y1	Point 2 y.
	@param	r	Corner radius.
	@return	Positive number on success, 0 on error.
*/
unsigned long
dkfigw_arc_box DK_P6(dk_fig_writer_t *,fwp, double,x0, double,y0, double,x1, double,y1, double,r)
{
  unsigned long		back	= 0UL;
  dk_fig_object_t	*obj;
  obj = object_new(fwp, FIG_OBJECT_POLYLINE, FIG_SUB_ARC_BOX);
  if(obj) {
    back = obj->obn;
    (obj->det).pll.x0 = x0;
    (obj->det).pll.y0 = y0;
    (obj->det).pll.x1 = x1;
    (obj->det).pll.y1 = y1;
    (obj->det).pll.cr = r;
  }
  return back;
}



/**	Add image.
	@param	fwp	Writer structure.
	@param	x0	Point 1 x.
	@param	y0	Point 1 y.
	@param	x1	Point 2 x.
	@param	y1	Point 2 y.
	@param	fn	File name.
	@return	Positive number on success, 0 on error.
*/
unsigned long
dkfigw_image DK_P6(dk_fig_writer_t *,fwp, double,x0, double,y0, double,x1, double,y1, char *,fn)
{
  unsigned long		back	= 0UL;
  dk_fig_object_t	*obj;
  obj = object_new(fwp, FIG_OBJECT_POLYLINE, FIG_SUB_IMAGE);
  if(obj) {
    (obj->det).pll.fn = dkstr_dup(fn);
    if((obj->det).pll.fn) {
      back = obj->obn;
      (obj->det).pll.x0 = x0;
      (obj->det).pll.y0 = y0;
      (obj->det).pll.x1 = x1;
      (obj->det).pll.y1 = y1;
    } else {	
    }
  }
  return back;
}



/**	Add polygon. After creating the polygon use dkfigw_point() to add points.

	@param	fwp	Writer structure.
	@return Positive number on success, 0 on error.
*/
unsigned long
dkfigw_polygon DK_P1(dk_fig_writer_t *,fwp) {
  unsigned long		back	= 0UL;
  dk_fig_object_t	*obj;
  obj = object_new(fwp, FIG_OBJECT_POLYLINE, FIG_SUB_POLYGON);
  if(obj) {
    back = obj->obn;
  }
  return back;
}



/**	Add polyline. After creating the polygon use dkfigw_point() to add points.
	@param	fwp	Writer structure.
	@return Positive number on success, 0 on error.
*/
unsigned long
dkfigw_polyline DK_P1(dk_fig_writer_t *,fwp) {
  unsigned long		back	= 0UL;
  dk_fig_object_t	*obj;
  
  obj = object_new(fwp, FIG_OBJECT_POLYLINE, FIG_SUB_POLYLINE);
  if(obj) {
    back = obj->obn;
  } 
  return back;
}



/**	Add point to the current polygon or polyline object.
	@param	fwp	Writer structure.
	@param	x	X position.
	@param	y	Y position.
	@return	Positive number on success, 0 on error.
*/
unsigned long
dkfigw_point DK_P3(dk_fig_writer_t *,fwp, double,x, double,y) {
  unsigned long		back = 0UL;
  dk_fig_pl_pt_t	*spp = NULL;
  
  if(fwp) {					
    if(fwp->cob) {				
      if(((fwp->cob)->otp) == FIG_OBJECT_POLYLINE) {	
        switch((fwp->cob)->stp) {
	  case FIG_SUB_POLYLINE:
	  case FIG_SUB_POLYGON:
	  {
	    
	    if(((fwp->cob)->det).pll.spt) {	
	      spp = dk_new(dk_fig_pl_pt_t,1);
	      if(spp) {				
	        spp->npt = ((fwp->cob)->det).pll.npn;
		((fwp->cob)->det).pll.npn = ((fwp->cob)->det).pll.npn + 1UL;
		spp->x = x;
		spp->y = y;
		if(dksto_add(((fwp->cob)->det).pll.spt, (void *)spp)) {
		  back = spp->npt;		
		} else {
		  dk_delete(spp);		
		}
	      } else {		
	      }
	    } else {		
	    }
	  }
	  break;
	}
      }
    }
  } 
  return back;
}



/**	Add open spline.
	After creating the spline use dkfigw_spline_point() to add points.
	@param	fwp	Writer structure.
	@return Positive number on success, 0 on error.
*/
unsigned long
dkfigw_open_spline DK_P1(dk_fig_writer_t *,fwp) {
  unsigned long		back = 0UL;
  dk_fig_object_t	*obj;
  obj = object_new(fwp, FIG_OBJECT_SPLINE, FIG_SUB_SPLINE_OPEN_X);
  if(obj) {
    back = obj->obn;
  }
  return back;
}



/**	Add closed spline.
	After creating the spline use dkfigw_spline_point() to add points.
	@param	fwp	Writer structure.
	@return Positive number on success, 0 on error.
*/
unsigned long
dkfigw_closed_spline DK_P1(dk_fig_writer_t *,fwp) {
  unsigned long		back = 0UL;
  dk_fig_object_t	*obj;
  obj = object_new(fwp, FIG_OBJECT_SPLINE, FIG_SUB_SPLINE_CLOSED_X);
  if(obj) {
    back = obj->obn;
  }
  return back;
}



/**	Add point to current spline object.
	@param	fwp	Writer structure.
	@param	x	X position.
	@param	y	Y position.
	@param	s	Approximation/interpolation parameter.
	@return Positive number on success, 0 on error.
*/
unsigned long
dkfigw_spline_point DK_P4(dk_fig_writer_t *,fwp, double,x, double,y, double,s)
{
  unsigned long		back = 0UL;
  dk_fig_sp_pt_t	*spp = NULL;
  if(fwp) {
    if(fwp->cob) {
      if(((fwp->cob)->otp) == FIG_OBJECT_SPLINE) {
        switch((fwp->cob)->stp) {
	  case FIG_SUB_SPLINE_OPEN_X:
	  case FIG_SUB_SPLINE_CLOSED_X:
	  {
	    if(((fwp->cob)->det).pll.spt) {
	      spp = dk_new(dk_fig_sp_pt_t,1);
	      if(spp) {
	        spp->npt = ((fwp->cob)->det).pll.npn;
		((fwp->cob)->det).pll.npn = ((fwp->cob)->det).pll.npn + 1UL;
		spp->x = x;
		spp->y = y;
		spp->s = s;
		if(dksto_add(((fwp->cob)->det).pll.spt, (void *)spp)) {
		  back = spp->npt;
		} else {
		  dk_delete(spp);
		}
	      }
	    }
	  }
	  break;
	}
      }
    }
  }
  return back;
}



/**	Add open arc.
	@param	fwp	Writer structure.
	@param	x	Center x.
	@param	y	Center y.
	@param	r	Radius.
	@param	a0	Start angle in radians.
	@param	a1	End angle in radians.
	@return Positive number on success, 0 on error.
*/
unsigned long
dkfigw_open_arc DK_P6(dk_fig_writer_t *,fwp, double,x, double,y, double,r, double,a0, double,a1)
{
  unsigned long		back = 0UL;
  dk_fig_object_t	*obj;
  obj = object_new(fwp, FIG_OBJECT_ARC, FIG_SUB_ARC_OPEN);
  if(obj) {
    back = obj->obn;
    (obj->det).arc.xm = x;
    (obj->det).arc.ym = y;
    (obj->det).arc.ra = r;
    (obj->det).arc.a0 = a0;
    (obj->det).arc.a1 = a1;
  }
  return back;
}



/**	Add closed arc (pie wedge).
	@param	fwp	Writer structure.
	@param	x	Center x.
	@param	y	Center y.
	@param	r	Radius.
	@param	a0	Start angle in radians.
	@param	a1	End angle in radians.
	@return Positive number on success, 0 on error.
*/
unsigned long
dkfigw_closed_arc DK_P6(dk_fig_writer_t *,fwp, double,x, double,y, double,r, double,a0, double,a1)
{
  unsigned long		back = 0UL;
  dk_fig_object_t	*obj;
  obj = object_new(fwp, FIG_OBJECT_ARC, FIG_SUB_ARC_CLOSED);
  if(obj) {
    back = obj->obn;
    (obj->det).arc.xm = x;
    (obj->det).arc.ym = y;
    (obj->det).arc.ra = r;
    (obj->det).arc.a0 = a0;
    (obj->det).arc.a1 = a1;
  }
  return back;
}



/**	Add circle.
	@param	fwp	Writer structure.
	@param 	x	Center x.
	@param	y	Center y.
	@param	r	Radius.
	@return Positive number on success, 0 on error.
*/
unsigned long
dkfigw_circle DK_P4(dk_fig_writer_t *,fwp, double,x, double,y, double,r) {
  unsigned long		back = 0UL;
  dk_fig_object_t	*obj;
  obj = object_new(fwp, FIG_OBJECT_ELLIPSE, FIG_SUB_CIRCLE_RADIUS);
  if(obj) {
    back = obj->obn;
    (obj->det).ell.xm = x;
    (obj->det).ell.ym = y;
    (obj->det).ell.rx = r;
    (obj->det).ell.ry = r;
    (obj->det).ell.ro = 0.0;
  }
  return back;
}



/**	Add ellipse.
	@param	fwp	Writer structure.
	@param	x	Center x.
	@param	y	Center y.
	@param	rx	Horizontal radius.
	@param	ry	Vertical radius.
	@param	rot	Rotation in radians.
	@return Positive number on success, 0 on error.
*/
unsigned long
dkfigw_ellipse DK_P6(dk_fig_writer_t *,fwp, double,x, double,y, double,rx, double,ry, double,rot)
{
  unsigned long		back = 0UL;
  dk_fig_object_t	*obj;
  obj = object_new(fwp, FIG_OBJECT_ELLIPSE, FIG_SUB_ELLIPSE_RADII);
  if(obj) {
    back = obj->obn;
    (obj->det).ell.xm = x;
    (obj->det).ell.ym = y;
    (obj->det).ell.rx = rx;
    (obj->det).ell.ry = ry;
    (obj->det).ell.ro = rot;
  }
  return back;
}



/**	Add text.
	@param	fwp	Writer structure.
	@param	x	X position.
	@param	y	Y position.
	@param	t	Text.
	@param	a	Rotation in radians.
	@return Positive number on success, 0 on error.
*/
unsigned long
dkfigw_text DK_P5(dk_fig_writer_t *,fwp, double,x, double,y, char *,t, double,a)
{
  unsigned long back = 0UL;
  dk_fig_object_t	*obj;
  if(fwp) {
    obj = object_new(fwp, FIG_OBJECT_TEXT, (fwp->sty).tal);
    if(obj) {
      (obj->det).txt.x = x;
      (obj->det).txt.y = y;
      (obj->det).txt.rot = a;
      (obj->det).txt.txt = dkstr_dup(t);
      if((obj->det).txt.txt) {
        back = obj->obn;
      }
    }
  }
  return back;
}



/**	Set text alignment for all following texts added.
	@param	fwp	Writer structure.
	@param	tal	Alignment (FIG_SUB_TEXT_xxx).
*/
void
dkfigw_set_text_align DK_P2(dk_fig_writer_t *,fwp, unsigned char,tal) {
  if(fwp) {
    if(tal < FIG_SUB_TEXT_LEFT) {
      (fwp->sty).tal = FIG_SUB_TEXT_LEFT;
    } else {
      if(tal > FIG_SUB_TEXT_RIGHT) {
        (fwp->sty).tal = FIG_SUB_TEXT_RIGHT;
      } else {
        (fwp->sty).tal = tal;
      }
    }
  }
}



/**	Set/reset rigid flag for all following texts added.
	@param	fwp	Writer structure.
	@param	fl	Set (non-zero) or reset (0) flag.
*/
void
dkfigw_set_text_rigid DK_P2(dk_fig_writer_t *,fwp, int,fl) {
  if(fwp) {
    if(fl) {
      (fwp->sty).ffl |= FIG_TEXTFLAG_RIGID;
    } else {
      (fwp->sty).ffl &= (~( FIG_TEXTFLAG_RIGID));
    }
  }
}



/**	Set/reset special flag for all following texts added.
	@param	fwp	Writer structure.
	@param	fl	Set (non-zero) or reset (0) flag.
*/
void
dkfigw_set_text_special DK_P2(dk_fig_writer_t *,fwp, int,fl) {
  if(fwp) {
    if(fl) {
      (fwp->sty).ffl |= FIG_TEXTFLAG_SPECIAL;
    } else {
      (fwp->sty).ffl &= (~(FIG_TEXTFLAG_SPECIAL));
    }
  }
}



/**	Set PS font index for all following texts added.
	@param	fwp	Writer structure.
	@param	fno	Font index (0 to 34).
*/
void
dkfigw_set_text_psfont DK_P2(dk_fig_writer_t *,fwp, unsigned char,fno) {
  if(fwp) {
    if(fno < 0x00) {
      (fwp->sty).fno = FIG_FONT_PS_DEFAULT;
    } else {
      if(fno > FIG_FONT_PS_ZAPF_DINGBATS) {
        (fwp->sty).fno = FIG_FONT_PS_ZAPF_DINGBATS;
      } else {
        (fwp->sty).fno = fno;
      }
    }
    (fwp->sty).ffl |= FIG_TEXTFLAG_PS;
  }
}



/**	Set PS font for all following texts added, choose font by name.
	@param	fwp	Writer structure.
	@param	n	Font name: one of the strings in font_names.
*/
void
dkfigw_set_text_psfont_by_name DK_P2(dk_fig_writer_t *,fwp, char *,n) {
  int i;
  if(fwp) {
    if(n) {
      i = my_str_array_index(font_names, n, 0, fwp->ver);
      if(i < 0) i = 0;
      dkfigw_set_text_psfont(fwp, (unsigned char)i);
    }
  }
}



/**	Set LaTeX font index for all following texts added.
	@param	fwp	Writer structure.
	@param	fno	Font index (0=default, 1=roman, 2=bold, 3=italic,
	4=sans-serif, 5=typewriter).
*/
void
dkfigw_set_text_latexfont DK_P2(dk_fig_writer_t *,fwp, unsigned char,fno) {
  if(fwp) {
    if(fno < 0x00) {
      (fwp->sty).fno = 0x00;
    } else {
      if(fno > FIG_FONT_LATEX_TYPEWRITER) {
        (fwp->sty).fno = FIG_FONT_LATEX_TYPEWRITER;
      } else {
        (fwp->sty).fno = fno;
      }
    }
    (fwp->sty).ffl &= (~(FIG_TEXTFLAG_PS));
  }
}



/**	Set text size fo all following texts added.
	@param	fwp	Writer structure.
	@param	fsz	Font size in pt.
*/
void
dkfigw_set_text_size DK_P2(dk_fig_writer_t *,fwp, double,fsz) {
  if(fwp) {
    (fwp->sty).fsz = fsz;
    if((fwp->sty).fsz < 0.0) (fwp->sty).fsz = 0.0;
  }
}



/**	Set layer for all following objects.
	@param	fwp	Writer structure.
	@param	lay	Layer number.
*/
void
dkfigw_set_layer DK_P2(dk_fig_writer_t *,fwp, unsigned short,lay) {
  if(fwp) {
    if(lay < 0) {
      (fwp->sty).lay = 0;
    } else {
      if(lay > 999) {
        (fwp->sty).lay = 999;
      } else {
        (fwp->sty).lay = lay;
      }
    }
  }
}



/**	Move one layer upwards.
	@param	fwp	Writer structure.
	@return	Non-zero number on success, 0 on error.
*/
int
dkfigw_layer_up DK_P1(dk_fig_writer_t *,fwp) {
  int back = 0;
  if(fwp) {
    (fwp->sty).lay = (fwp->sty).lay - 1;
    if((fwp->sty).lay >= 0) {
      back = 1;
    } else {
      (fwp->sty).lay = 0;
    }
  }
  return back;
}



/**	Set pen color for all following objects.
	@param	fwp	Writer structure.
	@param	col	Color cell number (0 to 31 for predefined colors,
	32 and above for self-defined colors).
*/
void
dkfigw_set_pen_color DK_P2(dk_fig_writer_t *,fwp, short,col) {
  if(fwp) {
    if(col < -1) {
      (fwp->sty).pco = -1;
    } else {
      (fwp->sty).pco = col;
    }
  }
}



/**	Set pen color for all following objects by color name.
	@param	fwp	Writer structure.
	@param	n	Color name: One of the names in color_names.
*/
void
dkfigw_set_pen_color_by_name DK_P2(dk_fig_writer_t *,fwp, char *,n) {
  int i;
  if(fwp) {
    if(n) {
      i = my_str_array_index(color_names, n, 0, fwp->ver);
      if(i < 0) i = 0;
      dkfigw_set_pen_color(fwp, (short)i);
    }
  }
}



/**	Set fill color for all following objects.
	@param	fwp	Writer structure.
	@param	col	Color cell number, either a user defined
	color (32 and above) or a predefined color:
	FIG_COLOR_BLACK, FIG_COLOR_BLUE, FIG_COLOR_GREEN, FIG_COLOR_CYAN,
	FIG_COLOR_RED, FIG_COLOR_MAGENTA, FIG_COLOR_YELLOW, FIG_COLOR_WHITE,
	FIG_COLOR_DARKEST_BLUE, FIG_COLOR_DARK_BLUE, FIG_COLOR_LIGHT_BLUE,
	FIG_COLOR_LIGHTEST_BLUE, FIG_COLOR_DARK_GREEN,
	FIG_COLOR_NORMAL_GREEN, FIG_COLOR_LIGHT_GREEN, FIG_COLOR_DARK_CYAN,
	FIG_COLOR_NORMAL_CYAN, FIG_COLOR_LIGHT_CYAN, FIG_COLOR_DARK_RED,
	FIG_COLOR_NORMAL_RED, FIG_COLOR_LIGHT_RED, FIG_COLOR_DARK_MAGENTA,
	FIG_COLOR_NORMAL_MAGENTA, FIG_COLOR_LIGHT_MAGENTA, FIG_COLOR_DARK_BROWN,
	FIG_COLOR_NORMAL_BROWN, FIG_COLOR_LIGHT_BROWN, FIG_COLOR_DARKEST_PINK,
	FIG_COLOR_DARK_PINK, FIG_COLOR_LIGHT_PINK, FIG_COLOR_LIGHTEST_PINK or
	FIG_COLOR_GOLD.
*/
void
dkfigw_set_fill_color DK_P2(dk_fig_writer_t *,fwp, short,col) {
  if(fwp) {
    if(col < -1) {
      (fwp->sty).fco = -1;
    } else {
      (fwp->sty).fco = col;
    }
  }
}



/**	Set fill color for all following objects by color name.
	@param	fwp	Writer structure.
	@param	n	Color name: One of the names in color_names.
*/
void
dkfigw_set_fill_color_by_name DK_P2(dk_fig_writer_t *,fwp, char *,n) {
  int i;
  if(fwp) {
    if(n) {
      i = my_str_array_index(color_names, n, 0, fwp->ver);
      if(i < 0) i = 7;
      dkfigw_set_fill_color(fwp, (short)i);
    }
  }
}



/**	Set transparent color
	@param	fwp	Writer structure.
	@param	col	Index of the transparent color.
*/
void
dkfigw_set_transparent_color  DK_P2(dk_fig_writer_t *,fwp, short,col) {
  if(fwp) {
    if(col < -3) {
      fwp->tco = -3;
    } else {
      fwp->tco = col;
    }
  }
}



/**	Set fill style for all following objects.
	@param	fwp	Writer structure.
	@param	fil	Fill style, one from:
	FIG_FILL_NONE, FIG_FILL_BLACK, FIG_FILL_005, FIG_FILL_010, FIG_FILL_015,
	FIG_FILL_020, FIG_FILL_025, FIG_FILL_030, FIG_FILL_035, FIG_FILL_040,
	FIG_FILL_045, FIG_FILL_050, FIG_FILL_055, FIG_FILL_060, FIG_FILL_065,
	FIG_FILL_070, FIG_FILL_075, FIG_FILL_080, FIG_FILL_085, FIG_FILL_090,
	FIG_FILL_095, FIG_FILL_100, FIG_FILL_PURE, FIG_FILL_105, FIG_FILL_110,
	FIG_FILL_115, FIG_FILL_120, FIG_FILL_125, FIG_FILL_130, FIG_FILL_135,
	FIG_FILL_140, FIG_FILL_145, FIG_FILL_150, FIG_FILL_155, FIG_FILL_160,
	FIG_FILL_165, FIG_FILL_170, FIG_FILL_175, FIG_FILL_180, FIG_FILL_185,
	FIG_FILL_190, FIG_FILL_195, FIG_FILL_200, FIG_FILL_WHITE,
	FIG_FILL_LEFT_DIAGONAL_30, FIG_FILL_RIGHT_DIAGONAL_30,
	FIG_FILL_CROSSHATCH_30, FIG_FILL_LEFT_DIAGONAL_45,
	FIG_FILL_RIGHT_DIAGONAL_45, FIG_FILL_CROSSHATCH_45,
	FIG_FILL_BRICKS_HORIZONTAL, FIG_FILL_BRICKS_VERTICAL,
	FIG_FILL_HORIZONTAL_LINES, FIG_FILL_VERTICAL_LINES,
	FIG_FILL_CROSSHATCH, FIG_FILL_HORIZONTAL_SHINGLES_RIGHT,
	FIG_FILL_HORIZONTAL_SHINGLES_LEFT, FIG_FILL_VERTICAL_SHINGLES_1,
	FIG_FILL_VERTICAL_SHINGLES_2, FIG_FILL_LARGE_FISH_SCALES,
	FIG_FILL_SMALL_FISH_SCALES, FIG_FILL_CIRCLES,
	FIG_FILL_HEXAGONS, FIG_FILL_OCTAGONS,
	FIG_FILL_HORIZONTAL_TIRE_TREADS or FIG_FILL_VERTICAL_TIRE_TREADS.
*/
void
dkfigw_set_fill_style DK_P2(dk_fig_writer_t *,fwp, unsigned char, fil) {
  if(fwp) {
    if(fil < -1) {
      (fwp->sty).fil = -1;
    } else {
      if(fil > 62) {
        (fwp->sty).fil = 62;
	if(fil > 127) {
	  (fwp->sty).fil = 0xFF;
	}
      } else {
        (fwp->sty).fil = fil;
      }
    }
  }
}



/**	Set line style for all following objects.
	@param	fwp	Writer structure.
	@param	lst	Line style, one from:
	FIG_LS_SOLID, FIG_LS_DASHED, FIG_LS_DOTTED, FIG_LS_DASH_DOTTED,
	FIG_LS_DASH_DOUBLE_DOTTED or FIG_LS_DASH_TRIPLE_DOTTED.
*/
void
dkfigw_set_line_style DK_P2(dk_fig_writer_t *,fwp, unsigned char, lst) {
  if(fwp) {
    if(lst < FIG_LS_SOLID) {
      (fwp->sty).lst = FIG_LS_DEFAULT;
    } else {
      if(lst > FIG_LS_DASH_TRIPLE_DOTTED) {
        (fwp->sty).lst =  FIG_LS_DASH_TRIPLE_DOTTED;
      } else {
        (fwp->sty).lst = lst;
      }
    }
  }
}



/**	Set line width for all following objects.
	@param	fwp	Writer structure.
	@param	lw	Line width in Fig linewidth units (1/80 inch).
*/
void
dkfigw_set_line_width DK_P2(dk_fig_writer_t *,fwp, short,lw) {
  if(fwp) {
    (fwp->sty).liw = ((lw >= 0) ? lw : 0);
  }
}



/**	Set "angle-in-degree" flag in writer.
	@param	fwp	Writer structure.
	@param	fl	Set (non-zero value) or reset (0) the flag.
*/
void
dkfigw_set_angle_degree DK_P2(dk_fig_writer_t *,fwp, int,fl) {
  if(fwp) {
    fwp->ang = (fl ? 0x01 : 0x00);
  }
}



/**	Set line join style for all following objects.
	@param	fwp	Writer structure.
	@param	ljn	Line join style, one from:
	FIG_LJ_MITER, FIG_LJ_ROUND, or FIG_LJ_BEVEL.
*/
void
dkfigw_set_line_join DK_P2(dk_fig_writer_t *,fwp, unsigned char, ljn) {
  if(fwp) {
    if(ljn < FIG_LJ_MITER) {
      (fwp->sty).ljn = FIG_LJ_MITER;
    } else {
      if(ljn > FIG_LJ_BEVEL) {
        (fwp->sty).ljn = FIG_LJ_BEVEL;
      } else {
        (fwp->sty).ljn = ljn;
      }
    }
  }
}



/**	Set line cap style for all following objects.
	@param	fwp	Writer structure.
	@param	lcp	Line cap style, one from:
	FIG_CS_BUTT, FIG_CS_ROUND or FIG_CS_PROJECTING.
*/
void
dkfigw_set_line_cap DK_P2(dk_fig_writer_t *,fwp, unsigned char, lcp) {
  if(fwp) {
    if(lcp < FIG_CS_BUTT) {
      (fwp->sty).lcp = FIG_CS_BUTT;
    } else {
      if(lcp > FIG_CS_PROJECTING) {
        (fwp->sty).lcp = FIG_CS_PROJECTING;
      } else {
        (fwp->sty).lcp = lcp;
      }
    }
  }
}



/**	Set shape, fill type and lengths for one arrowhead.
	@param	ah	Arrowhead structure.
	@param	sha	Shape.
	@param	fil	Fill type.
	@param	wid	Width.
	@param	hei	Height
*/
static
void
set_arrowhead DK_P5(dk_fig_ah_t *,ah, unsigned char,sha, unsigned char,fil, double,wid, double,hei)
{
  ah->sha = sha; ah->fil = fil; ah->wid = wid; ah->hei = hei;
}



/**	Set shape, fill type and lengths for forward arrowhead.
	@param	fwp	Fig writer structure.
	@param	sha	Shape.
	@param	fil	Fill type.
	@param	wid	Width.
	@param	hei	Height
*/
void
dkfigw_set_arrowhead_f DK_P5(dk_fig_writer_t *,fwp, unsigned char,sha, unsigned char,fil, double,wid, double, hei)
{
  if(fwp) { set_arrowhead(&((fwp->sty).fwa), sha, fil, wid, hei); }
}



/**	Set shape, fill type and lengths for backward arrowhead.
	@param	fwp	Fig writer structure.
	@param	sha	Shape.
	@param	fil	Fill type.
	@param	wid	Width.
	@param	hei	Height
*/
void
dkfigw_set_arrowhead_b DK_P5(dk_fig_writer_t *,fwp, unsigned char,sha, unsigned char,fil, double,wid, double, hei)
{
  if(fwp) { set_arrowhead(&((fwp->sty).bwa), sha, fil, wid, hei); }
}



/**	Set arrowheads directions.
	@param	fwp	Fig writer structure.
	@param	ahs	Directions (0x00=no arrowheads, 0x01=forward,
	0x02=backward, 0x03=both forward and backward).
*/
void
dkfigw_set_arrowheads DK_P2(dk_fig_writer_t *,fwp, unsigned char,ahs) {
  if(fwp) { (fwp->sty).ahs = ahs; }
}



/**	Write one keyword from the \arg kw array to output stream.
	@param	os	Output stream.
	@param	n	Keyword index.
*/
static
void
kw_out DK_P2(dk_stream_t *,os, size_t,n) {
  dkstream_puts(os, kw[n]);
}



/**	Write one arrowhead line to output stream.
	@param	os	Output stream.
	@param	fwp	Fig writer structure.
	@param	sty	Fig style structure.
	@param	ah	Arrowhead structure.
	@param	o	Current object.
*/
static
void
put_arrow DK_P5(dk_stream_t *,os, dk_fig_writer_t *,fwp, dk_fig_style_t *,sty, dk_fig_ah_t *,ah, dk_fig_object_t *,o)
{
  
  kw_out(os, 1); kw_out(os, 1);
  put_int(os, uc_to_int(ah->sha)); kw_out(os, 1);
  put_int(os, uc_to_int(ah->fil)); kw_out(os, 1);
  put_double(os, dkma_l_to_double((long)(sty->liw))); kw_out(os, 1);
  put_double(os, dkma_rint(d_to_fig_d(fwp, ah->wid))); kw_out(os, 1);
  put_double(os, dkma_rint(d_to_fig_d(fwp, ah->hei))); kw_out(os, 0);
  
}



/**	Initial settings for arrowheads.
	@param	fwp	Fig structure.
*/
void
dkfigw_set_suggested_arrow_settings DK_P1(dk_fig_writer_t *,fwp) {
  
  if(fwp) {
    switch(fwp->uni) {
      case 1: {
        set_arrowhead(&((fwp->sty).fwa),1,1,72.001/in_to_fig,120.001/in_to_fig);
        set_arrowhead(&((fwp->sty).bwa),1,1,72.001/in_to_fig,120.001/in_to_fig);
      } break;
      case 2: {
        set_arrowhead(&((fwp->sty).fwa),1,1,72.001/cm_to_fig,120.001/cm_to_fig);
        set_arrowhead(&((fwp->sty).bwa),1,1,72.001/cm_to_fig,120.001/cm_to_fig);
      } break;
      default: {
        set_arrowhead(&((fwp->sty).fwa), 1, 1, 72.001, 120.001);
        set_arrowhead(&((fwp->sty).bwa), 1, 1, 72.001, 120.001);
      } break;
    }
  } 
}



/**	Write line to start a spline to output stream.
	@param	os	Output stream.
	@param	f	Fig writer structure.
	@param	o	Current object.
	@param	sty	Fig style structure.
	@param	np	Number of points.
	@param	afw	Flag: Arrowhead forward.
	@param	abw	Flag: Arrowhead backward.
*/
static
void
spline_start DK_P7(dk_stream_t *,os, dk_fig_writer_t *,f, dk_fig_object_t *,o, dk_fig_style_t *,sty, unsigned long,np, int,afw, int,abw)
{
  
  /* 3 (type) */
  put_int(os, 3); kw_out(os, 1);
  /* sub type */
  put_int(os, o->stp); kw_out(os, 1);
  /* line style */
  put_int_range(os, uc_to_int(sty->lst),-1,FIG_LS_DASH_TRIPLE_DOTTED);
  kw_out(os, 1);
  /* thickness */
  put_int(os, (int)(sty->liw)); kw_out(os, 1);
  /* pen color */
  put_int(os, (int)(sty->pco)); kw_out(os, 1);
  /* fill color */
  put_int(os, (int)(sty->fco)); kw_out(os, 1);
  /* layer */
  put_unsigned(os, (unsigned)(sty->lay)); kw_out(os, 1);
  /* pen style */
  put_unsigned(os, 0); kw_out(os, 1);
  /* area fill */
  put_int(os, uc_to_int(sty->fil)); kw_out(os, 1);
  /* style value */
  put_double(os, sty->sva); kw_out(os, 1);
  /* cap style */
  put_int(os, uc_to_int(sty->lcp)); kw_out(os, 1);
  /* forward arrow */
  put_int(os, (afw ? 1 : 0)); kw_out(os, 1);
  /* backward arrow */
  put_int(os, (abw ? 1 : 0)); kw_out(os, 1);
  /* npoints */
  put_long(os, (long)np); kw_out(os, 0); 
}



/**	Write points coordinates to output stream.
	@param	os	Output stream.
	@param	f	Fig writer structure.
	@param	o	Current object.
*/
static
void
spline_points DK_P3(dk_stream_t *,os, dk_fig_writer_t *,f, dk_fig_object_t *,o)
{
  dk_storage_iterator_t	*it;
  dk_fig_sp_pt_t	*pt;
  unsigned long		ptno;
  
  ptno = 0UL;
  it = (o->det).pll.ipt;
  dksto_it_reset(it);
  while((pt = (dk_fig_sp_pt_t *)dksto_it_next(it)) != NULL) {
    if((ptno % 5UL) == 0UL) {
      if(ptno) {
        kw_out(os, 0);
      }
      kw_out(os, 14);
    } else {
      kw_out(os, 1);
    }
    put_long(os, x_to_fig_l(f, pt->x)); kw_out(os, 1);
    put_long(os, y_to_fig_l(f, pt->y));
    ptno++;
  }
  kw_out(os, 0);
  ptno = 0UL;
  it = (o->det).pll.ipt;
  dksto_it_reset(it);
  while((pt = (dk_fig_sp_pt_t *)dksto_it_next(it)) != NULL) {
    if((ptno % 10UL) == 0UL) {
      if(ptno) {
        kw_out(os, 0);
      }
      kw_out(os, 14);
    } else {
      kw_out(os, 1);
    }
    put_double(os, pt->s);
    ptno++;
  }
  kw_out(os, 0); 
}



/**	Write start-of-polyline line to output stream.
	@param	os	Output stream.
	@param	f	Fig writer structure.
	@param	o	Current object.
	@param	sty	Fig style structure.
	@param	np	Number of points.
	@param	afw	Flag: Arrowhead forward.
	@param	abw	Flag: Arrowhead backward.
*/
static
void
polyline_start DK_P7(dk_stream_t *,os, dk_fig_writer_t *,f, dk_fig_object_t *,o, dk_fig_style_t *,sty, unsigned long,np, int,afw, int,abw)
{
  long			rcorner;
  
  /* 2 (type) */
  put_int(os, o->otp); kw_out(os, 1);
  /* subtype */
  put_int(os, o->stp); kw_out(os, 1);
  /* line style */
  put_int_range(os, uc_to_int(sty->lst),-1,FIG_LS_DASH_TRIPLE_DOTTED);
  kw_out(os, 1);
  /* thickness */
  put_int(os, (int)(sty->liw)); kw_out(os, 1);
  /* pen color */
  put_int(os, (int)(sty->pco)); kw_out(os, 1);
  /* fill color */
  put_int(os, (int)(sty->fco)); kw_out(os, 1);
  /* layer */
  put_unsigned(os, (unsigned)(sty->lay)); kw_out(os, 1);
  /* penstyle */
  put_unsigned(os, 0); kw_out(os, 1);
  /* area fill */
  put_int(os, uc_to_int(sty->fil)); kw_out(os, 1);
  /* style value */
  put_double(os, sty->sva); kw_out(os, 1);
  /* join style */
  put_int(os, uc_to_int(sty->ljn)); kw_out(os, 1);
  /* cap style */
  put_int(os, uc_to_int(sty->lcp)); kw_out(os, 1);
  /* radius */
  switch(o->stp) {
    case FIG_SUB_ARC_BOX: {
      rcorner = d_to_fig_l(f, (o->det).pll.cr);
      if(rcorner < 0L) rcorner = 0L;
      rcorner = rcorner / 15L;
      put_long(os, rcorner);
    } break;
    default: {
      put_int(os, 0);
    } break;
  }
  kw_out(os, 1);
  /* flag: forward arrow */
  put_int(os, (afw ? 1 : 0)); kw_out(os, 1);
  /* flag: backward arrow */
  put_int(os, (abw ? 1 : 0)); kw_out(os, 1);
  /* number of points */
  put_long(os, (long)np); kw_out(os, 0); 
}



/**	Write polyline points coordinates to output stream.
	@param	os	Output stream.
	@param	f	Fig writer structure.
	@param	o	Current object.
	@param	flcl	Flag: Closed polygon (repeat first point at end).
*/
static
void
polyline_points DK_P4(dk_stream_t *,os, dk_fig_writer_t *,f, dk_fig_object_t *,o, int,flcl)
{
  dk_storage_iterator_t	*it;
  dk_fig_pl_pt_t	*pt;
  unsigned long		ptno;
  
  ptno = 0UL;
  it = (o->det).pll.ipt;
  dksto_it_reset(it);
  while((pt = (dk_fig_pl_pt_t *)dksto_it_next(it)) != NULL) {
    if((ptno % 5UL) == 0UL) {
      if(ptno) {
        kw_out(os, 0);
      }
      kw_out(os, 14);
    } else {
      kw_out(os, 1);
    }
    put_long(os, x_to_fig_l(f, pt->x)); kw_out(os, 1);
    put_long(os, y_to_fig_l(f, pt->y));
    ptno++;
  }
  if(flcl) {
    dksto_it_reset(it);
    pt = (dk_fig_pl_pt_t *)dksto_it_next(it);
    if(pt) {
      if((ptno % 5UL) == 0UL) {
        if(ptno) {
	  kw_out(os, 0);
	}
	kw_out(os, 14);
      } else {
        kw_out(os, 1);
      }
      put_long(os, x_to_fig_l(f, pt->x)); kw_out(os, 1);
      put_long(os, y_to_fig_l(f, pt->y));
    }
  }
  kw_out(os, 0); 
}



/**	Write points coordinates for box (rectangle, image or arc box).
	@param	os	Output stream.
	@param	f	Fig writer structure.
	@param	o	Current object.
*/
static
void
box_points DK_P3(dk_stream_t *,os, dk_fig_writer_t *,f, dk_fig_object_t *,o)
{
  long x0;
  long y0;
  long x1;
  long y1;
  long t;
  
  x0 = x_to_fig_l(f, (o->det).pll.x0);
  y0 = y_to_fig_l(f, (o->det).pll.y0);
  x1 = x_to_fig_l(f, (o->det).pll.x1);
  y1 = y_to_fig_l(f, (o->det).pll.y1);
  if(x0 > x1) { t = x1; x1 = x0; x0 = t; }
  if(y0 > y1) { t = y1; y1 = y0; y0 = t; }
  kw_out(os, 14);
  /* x0 y0 */
  put_long(os, x0); kw_out(os, 1);
  put_long(os, y0); kw_out(os, 1);
  /* x1 y0 */
  put_long(os, x1); kw_out(os, 1);
  put_long(os, y0); kw_out(os, 1);
  /* x1 y1 */
  put_long(os, x1); kw_out(os, 1);
  put_long(os, y1); kw_out(os, 1);
  /* x0 y1 */
  put_long(os, x0); kw_out(os, 1);
  put_long(os, y1); kw_out(os, 1);
  /* x0 y0 */
  put_long(os, x0); kw_out(os, 1);
  put_long(os, y0); kw_out(os, 0); 
}



/**	Put single character to stream.
	@param	os	Output stream.
	@param	c	Character to write.
*/
static
void
stream_putc DK_P2(dk_stream_t *,os, char,c) {
  char buffer[2];
  buffer[0] = c; buffer[1] = '\0';
  dkstream_write(os, buffer, 1);
}



/**	Write text contents to output stream, apply conversion
	if necessary.
	@param	os	Output stream.
	@param	txt	Text to write.
*/
static
void
put_text DK_P2(dk_stream_t *,os, char *,txt) {
  char buffer[32];
  char 	c;
  char *ptr;
  ptr = txt;
  
  while(*ptr) {
    c = *ptr;
    if((c >= 'a') && (c <= 'z')) {
      stream_putc(os, c);
    } else {
      if((c >= 'A') && (c <= 'Z')) {
        stream_putc(os, c);
      } else {
        if((c >= '1') && (c <= '9')) {
	  stream_putc(os, c);
	} else {
	  switch(c) {
	    case '0':
	    case '^':
	    case '!':
	    case '\"':
	    case '$':
	    case '%':
	    case '&':
	    case '/':
	    case '(':
	    case ')':
	    case '=':
	    case '?':
	    case '\'':
	    case '`':
	    case '#':
	    case '+':
	    case '*':
	    case '-':
	    case '.':
	    case ',':
	    case '_':
	    case ':':
	    case ';':
	    case '<':
	    case '>':
	    case '|':
	    {
	      stream_putc(os, c);
	    } break;
	    case '\\': {
	      dkstream_puts(os, kw[19]);
	    } break;
	    default: {
	      sprintf(buffer, "\\%03o", c);
	      dkstream_puts(os, buffer);
	    } break;
	  }
	}
      }
    }
    ptr++;
  }
  dkstream_puts(os, kw[18]); 
}



/**	Print one object to output stream.
	@param	os	Output stream.
	@param	f	Fig writer structure.
	@param	o	Object to print.
*/
static
void
print_object DK_P3(dk_stream_t *,os, dk_fig_writer_t *,f, dk_fig_object_t *,o)
{
  dk_fig_style_t	sty;
  unsigned char		afw;
  unsigned char		abw;
  double		alpha;
  double		v;
  unsigned long		npoints;
  int			textflags;
  double		tw;
  double		th;
  
  afw = abw = 0x00;
  DK_MEMCPY(&sty,&(o->sty),sizeof(dk_fig_style_t));
  if((o->sty).ahs & 0x01) {
    afw = 0x01;	
  }
  if((o->sty).ahs & 0x02) {
    abw = 0x01;	
  }
  switch(o->otp) {
    case FIG_OBJECT_ARC: {
      switch(o->stp) {
        case FIG_SUB_ARC_OPEN: {
	  sty.fco = FIG_COLOR_DEFAULT;
	  sty.fil = FIG_FILL_NONE;
	} break;
	case FIG_SUB_ARC_CLOSED: {
	  afw = abw = 0x00; 
	} break;
      }
      /* object code */
      put_int(os, 5); kw_out(os, 1);
      /* sub type */
      put_int(os, o->stp); kw_out(os, 1);
      /* line style */
      put_int_range(os,uc_to_int(sty.lst),-1,FIG_LS_DASH_TRIPLE_DOTTED);
      kw_out(os, 1);
      /* line width */
      put_int(os, (int)(sty.liw)); kw_out(os, 1);
      /* pen color */
      put_int(os, (int)(sty.pco)); kw_out(os, 1);
      /* fill color */
      put_int(os, (int)(sty.fco)); kw_out(os, 1);
      /* layer */
      put_unsigned(os, (unsigned)(sty.lay)); kw_out(os, 1);
      /* pen_style */
      put_unsigned(os, 0); kw_out(os, 1);
      /* area fill */
      put_int(os, uc_to_int(sty.fil)); kw_out(os, 1);
      /* float styleval */
      put_double(os, sty.sva); kw_out(os, 1);
      /* cap style */
      put_int(os, uc_to_int(sty.lcp)); kw_out(os, 1);
      /* direction (0) */
      put_unsigned(os, 0); kw_out(os, 1);
      /* forward arrow */
      put_int(os, (afw ? 1 : 0)); kw_out(os, 1);
      /* backward arrow */
      put_int(os, (abw ? 1 : 0)); kw_out(os, 1);
      /* float center x */
      put_double(os, x_to_fig_d(f, (o->det).arc.xm)); kw_out(os, 1);
      /* float center y */
      put_double(os, y_to_fig_d(f, (o->det).arc.ym)); kw_out(os, 1);
      /* x1 */
      alpha = (o->det).arc.a0;
      if(f->ang) alpha = alpha * M_PI / 180.0;
      v = (o->det).arc.xm + (o->det).arc.ra * cos(alpha);
      put_long(os, x_to_fig_l(f, v)); kw_out(os, 1);
      /* y1 */
      v = (o->det).arc.ym + (o->det).arc.ra * sin(alpha);
      put_long(os, y_to_fig_l(f, v)); kw_out(os, 1);
      /* x2 */
      alpha = 0.5 * ((o->det).arc.a0 + (o->det).arc.a1);
      if(f->ang) alpha = alpha * M_PI / 180.0;
      v = (o->det).arc.xm + (o->det).arc.ra * cos(alpha);
      put_long(os, x_to_fig_l(f, v)); kw_out(os, 1);
      /* y2 */
      v = (o->det).arc.ym + (o->det).arc.ra * sin(alpha);
      put_long(os, y_to_fig_l(f, v)); kw_out(os, 1);
      /* x3 */
      alpha = (o->det).arc.a1;
      if(f->ang) alpha = alpha * M_PI / 180.0;
      v = (o->det).arc.xm + (o->det).arc.ra * cos(alpha);
      put_long(os, x_to_fig_l(f, v)); kw_out(os, 1);
      /* y3 */
      v = (o->det).arc.ym + (o->det).arc.ra * sin(alpha);
      put_long(os, y_to_fig_l(f, v));
      kw_out(os, 0);
      if(afw) {
        put_arrow(os, f, &sty, &(sty.fwa), o);
      }
      if(abw) {
        put_arrow(os, f, &sty, &(sty.bwa), o);
      }
    } break;
    case FIG_OBJECT_ELLIPSE: {
      long cx, cy, rx, ry;
      switch(o->stp) {
        case FIG_SUB_ELLIPSE_RADII: {
	  afw = abw = 0x00;
	} break;
	case FIG_SUB_CIRCLE_RADIUS: {
	  afw = abw = 0x00;
	} break;
      }
      /* object type (1) */
      put_int(os, 1); kw_out(os, 1);
      /* sub type (1 or 3) */
      put_int(os, o->stp); kw_out(os, 1);
      /* line style */
      put_int(os, uc_to_int((o->sty).lst)); kw_out(os, 1);
      /* thickness */
      put_int(os, (int)((o->sty).liw)); kw_out(os, 1);
      /* pen color */
      put_int(os, (int)((o->sty).pco)); kw_out(os, 1);
      /* fill color */
      put_int(os, (int)((o->sty).fco)); kw_out(os, 1);
      /* layer (depth) */
      put_unsigned(os, (o->sty).lay); kw_out(os, 1);
      /* pen style */
      put_int(os, 0); kw_out(os, 1);
      /* area fill */
      put_int(os, uc_to_int((o->sty).fil)); kw_out(os, 1);
      /* float style value */
      put_double(os, (o->sty).sva); kw_out(os, 1);
      /* direction (always 1) */
      put_int(os, 1); kw_out(os, 1);
      /* float angle (rotation) */
      alpha = (o->det).ell.ro;
      if(f->ang) { alpha = alpha * M_PI / 180.0; }
      put_long_double(os, alpha); kw_out(os, 1);
      /* center x */
      cx = x_to_fig_l(f, (o->det).ell.xm);
      put_long(os, cx); kw_out(os, 1);
      /* center y */
      cy = y_to_fig_l(f, (o->det).ell.ym);
      put_long(os, cy); kw_out(os, 1);
      /* radius x */	
      rx = d_to_fig_l(f, (o->det).ell.rx);
      put_long(os, rx); kw_out(os, 1);
      /* radiux y */	
      ry = d_to_fig_l(f, (o->det).ell.ry);
      put_long(os, ry); kw_out(os, 1);
      /* start x */
      put_long(os, (cx - rx)); kw_out(os, 1);
      /* start y */
      put_long(os, (cy - ry)); kw_out(os, 1);
      /* end x */
      put_long(os, (cx + rx)); kw_out(os, 1);
      /* end y */
      put_long(os, (cy + ry)); kw_out(os, 0);
    } break;
    case FIG_OBJECT_POLYLINE: {
      switch(o->stp) {
        case FIG_SUB_POLYLINE: {
	  sty.fco = FIG_COLOR_DEFAULT;
	  sty.fil = FIG_FILL_NONE;
	  npoints = 0UL;
	  if((o->det).pll.spt) {
	    if((o->det).pll.ipt) {
	      dksto_it_reset((o->det).pll.ipt);
	      while(dksto_it_next((o->det).pll.ipt)) npoints++;
	      if(npoints > 1UL) {
	        polyline_start(os, f, o, &sty, npoints, afw, abw);
		if(afw) {
		  put_arrow(os, f, &sty, &(sty.fwa), o);
		}
		if(abw) {
		  put_arrow(os, f, &sty, &(sty.bwa), o);
		}
		polyline_points(os, f, o, 0);
	      }
	    }
	  }
	} break;
	case FIG_SUB_BOX: {
	  afw = abw = 0x00;
	  polyline_start(os, f, o, &sty, 5UL, afw, abw);
	  /* Points line. */
	  box_points(os, f, o);
	} break;
	case FIG_SUB_POLYGON: {
	  afw = abw = 0x00;
	  npoints = 1UL;
	  if((o->det).pll.spt) {
	    if((o->det).pll.ipt) {
	      dksto_it_reset((o->det).pll.ipt);
	      while(dksto_it_next((o->det).pll.ipt)) npoints++;
	      if(npoints > 2UL) {
	        polyline_start(os, f, o, &sty, npoints, afw, abw);
		polyline_points(os, f, o, 1);
	      }
	    }
	  }
	} break;
	case FIG_SUB_ARC_BOX: {
	  afw = abw = 0x00;
	  polyline_start(os, f, o, &sty, 5UL, afw, abw);
	  /* Points line */
	  box_points(os, f, o);
	} break;
	case FIG_SUB_IMAGE: {
	  if((o->det).pll.fn) {
	    sty.fco = FIG_COLOR_DEFAULT;
	    sty.fil = FIG_FILL_NONE;
	    afw = abw = 0x00;
	    polyline_start(os, f, o, &sty, 5UL, afw, abw);
	    /* file name line */
	    kw_out(os, 1); put_int(os, 0); kw_out(os, 1);
	    dkstream_puts(os, (o->det).pll.fn); kw_out(os, 0);
	    /* Points line */
	    box_points(os, f, o);
	  }
	} break;
      }
    } break;
    case FIG_OBJECT_SPLINE: {
      switch(o->stp) {
        case FIG_SUB_SPLINE_OPEN_X: {
	  sty.fco = FIG_COLOR_DEFAULT;
	  sty.fil = FIG_FILL_NONE;
	} break;
	case FIG_SUB_SPLINE_CLOSED_X: {
	  afw = abw = 0x00;
	} break;
      }
      npoints = 0UL;
      if((o->det).pll.spt) {
        if((o->det).pll.ipt) {
	  dksto_it_reset((o->det).pll.ipt);
	  while(dksto_it_next((o->det).pll.ipt)) { npoints++; }
	  if(npoints > 1UL) {
	    spline_start(os, f, o, &sty, npoints, afw, abw);
	    if(afw) {
	      put_arrow(os, f, &sty, &(sty.fwa), o);
	    }
	    if(abw) {
	      put_arrow(os, f, &sty, &(sty.bwa), o);
	    }
	    spline_points(os, f, o);
	  }
	}
      }
    } break;
    case FIG_OBJECT_TEXT: {
      sty.fco = FIG_COLOR_DEFAULT;
      sty.fil = FIG_FILL_NONE;
      afw = abw = 0x00;
      if((o->det).txt.txt) {
        alpha = (o->det).ell.ro;
        if(f->ang) { alpha = alpha * M_PI / 180.0; }
	textflags = uc_to_int(sty.ffl) & 15;
	/* 4 (type) */
	put_int(os, 4); kw_out(os, 1);
	/* sub type */
	put_int(os, (o->stp)); kw_out(os, 1);
	/* color */
	put_int(os, (int)(sty.pco)); kw_out(os, 1);
	/* layer */
	put_unsigned(os, (unsigned)(sty.lay)); kw_out(os, 1);
	/* pen style */
	put_unsigned(os, 1); kw_out(os, 1);
	/* font number */
	put_int(os, uc_to_int(sty.fno)); kw_out(os, 1);
	/* float: font size */
	put_double(os, sty.fsz); kw_out(os, 1);
	th = 250.0 * sty.fsz / 12.0;
	tw = strlen((o->det).txt.txt) * 833.0 / 5.0;
	/* float: rotation */
	put_double(os, alpha); kw_out(os, 1);
	/* font flags */
	put_int(os, textflags); kw_out(os, 1);
	/* float: height (Fig units) */
	put_double(os, th); kw_out(os, 1);
	/* float: length (Fig units) */
	put_double(os, tw); kw_out(os, 1);
	/* x */
	put_long(os, x_to_fig_l(f, (o->det).txt.x)); kw_out(os, 1);
	/* y */
	put_long(os, y_to_fig_l(f, (o->det).txt.y)); kw_out(os, 1);
	/* text */
	put_text(os, (o->det).txt.txt); kw_out(os, 0);
      }
    } break;
  }
  
}



/**	Write Fig file to stream.
	@param	os	Output stream.
	@param	fwp	Fig structure.
	@return	1 on success, 0 on error.
*/
int
dkfigw_write DK_P2(dk_stream_t *,os, dk_fig_writer_t *,fwp) {
  int			back = 1;
  unsigned		u;
  char			buffer[64];
  dk_fig_cc_t		*ccptr;
  dk_fig_object_t	*objptr;
  
  switch(fwp->uni) {
    case 1: {	/* inches */
      fwp->iwf = in_to_fig * fwp->imw;
      fwp->ihf = in_to_fig * fwp->imh;
      fwp->xsh = 0.5 * (ceil(fwp->iwf) - fwp->iwf);
      fwp->ysh = 0.5 * (ceil(fwp->ihf) - fwp->ihf);
      fwp->iwf = ceil(fwp->iwf);
      fwp->ihf = ceil(fwp->ihf);
    } break;
    case 2: {	/* cm */
      fwp->iwf = cm_to_fig * fwp->imw;
      fwp->ihf = cm_to_fig * fwp->imh;
      fwp->xsh = 0.5 * (ceil(fwp->iwf) - fwp->iwf);
      fwp->ysh = 0.5 * (ceil(fwp->ihf) - fwp->ihf);
      fwp->iwf = ceil(fwp->iwf);
      fwp->ihf = ceil(fwp->ihf);
    } break;
    default: {	/* Fig units */
      fwp->iwf = fwp->imw; fwp->xsh = 0.0;
      fwp->ihf = fwp->imh; fwp->ysh = 0.0;
    } break;
  }
  kw_out(os, 3); kw_out(os, 0);
  /* Portrait/Landscape */
  kw_out(os, ((fwp->ori) ? 4 : 5)); kw_out(os, 0);
  /* Center */
  kw_out(os, ((fwp->jus) ? 7 : 6)); kw_out(os, 0);
  /* Metric */
  kw_out(os, ((fwp->fun) ? 9 : 8)); kw_out(os, 0);
  /* A4 */
  u = (unsigned)(fwp->pap);
  u = TO_RANGE(u,0,14);
  dkstream_puts(os, paper_sizes[u]); kw_out(os, 0);
  /* 100.0 */
  kw_out(os, 10); kw_out(os, 0);
  /* Single */
  kw_out(os, 11); kw_out(os, 0);
  /* -2 */
  sprintf(buffer, "%d", (int)(fwp->tco));
  dkstream_puts(os, buffer); kw_out(os, 0);
  /* Control comments on file level. */
  kw_out(os, 16); kw_out(os, 0);
  kw_out(os, 17); kw_out(os, 0);
  if(fwp->utf) {
    kw_out(os, 20); kw_out(os, 0);
  }
  /* 1200 2 */
  kw_out(os, 13); kw_out(os, 0);
  /* Color cells */
  
  if((fwp->ccs) && (fwp->cci))  {
    
    dksto_it_reset(fwp->cci);
    
    while((ccptr = (dk_fig_cc_t *)dksto_it_next(fwp->cci)) != NULL) {
      
      if((ccptr->ccn >= 32) && (ccptr->ccn <=543)) {	
        put_int(os, 0), kw_out(os, 1);
	put_unsigned(os, (unsigned)(ccptr->ccn)); kw_out(os, 1);
	sprintf(
	  buffer, "#%02x%02x%02x",
	  TO_RANGE(ccptr->r,0,255),
	  TO_RANGE(ccptr->g,0,255),
	  TO_RANGE(ccptr->b,0,255)
	); 
	dkstream_puts(os, buffer); kw_out(os, 0);
      }
    }
  }
  /* Background rectangle */
  kw_out(os, 15); kw_out(os, 0);
  kw_out(os, 14);
  /* 0 0 */
  put_int(os, 0); kw_out(os, 1); put_int(os, 0); kw_out(os, 1);
  /* w 0 */
  put_long(os, dkma_double_to_l(fwp->iwf)); kw_out(os, 1);
  put_int(os, 0); kw_out(os, 1);
  /* w h */
  put_long(os, dkma_double_to_l(fwp->iwf)); kw_out(os, 1);
  put_long(os, dkma_double_to_l(fwp->ihf)); kw_out(os, 1);
  /* 0 h */
  put_int(os, 0); kw_out(os, 1);
  put_long(os, dkma_double_to_l(fwp->ihf)); kw_out(os, 1);
  /* 0 0 */
  put_int(os, 0); kw_out(os, 1); put_int(os, 0); kw_out(os, 0);
  
  /* Graphics elements */
  if((fwp->ost) && (fwp->osi)) {
    dksto_it_reset(fwp->osi);
    while((objptr = (dk_fig_object_t *)dksto_it_next(fwp->osi)) != NULL) {
      print_object(os, fwp, objptr);
    }
  } 
  return back;
}



/**	Write Fig file to named file.
	@param	fn	File name to write to.
	@param	fwp	Fig structure.
	@return	1 on success, 0 on error.
*/
int
dkfigw_file DK_P2(char *,fn, dk_fig_writer_t *,fwp) {
  int		back = 0;
  char		*sf = NULL;
  dk_stream_t	*os = NULL;
  
  if(fn) {
  if(fwp) {
    sf = dksf_get_file_type_dot(fn);
    if(sf) {
      switch(dkstr_array_index(suffixes, sf, 0)) {
        case 0: {
	  os = dkstream_opengz(fn, kw[2], 0, NULL);
	} break;
	case 1: {
	  os = dkstream_openbz2(fn, kw[2], 0, NULL);
	} break;
	default: {
	  os = dkstream_openfile(fn, kw[2], 0, NULL);
	} break;
      }
    } else {
      os = dkstream_openfile(fn, kw[2], 0, NULL);
    }
    if(os) {
      back = dkfigw_write(os, fwp);
      dkstream_close(os);
    }
  }
  } 
  return back;
}



/**	Define a new color.
	@param	fwp	Fig writer structure.
	@param	r	Red.
	@param	g	Green.
	@param	b	Blue.
	@return	The new color cell number on success, 0 on error.
*/
int
dkfigw_define_color DK_P4(dk_fig_writer_t *,fwp, int,r, int,g, int,b) {
  int back = 0;
  dk_fig_cc_t	*cc;
  if((fwp->ccs) && (fwp->cci)) {
    cc = dk_new(dk_fig_cc_t,1);
    if(cc) {
      cc->ccn = fwp->ncc;
      fwp->ncc += 1;
      cc->r = r; cc->g = g; cc->b = b;
      if(cc->r < 0) cc->r = 0;
      if(cc->r > 255) cc->r = 255;
      if(cc->g < 0) cc->g = 0;
      if(cc->g > 255) cc->g = 255;
      if(cc->b < 0) cc->b = 0;
      if(cc->b > 255) cc->b = 255;
      if(dksto_add(fwp->ccs, (void *)cc)) {
        back = cc->ccn;
      } else {
        dk_delete(cc); cc = NULL;
      }
    }
  }
  return back;
}



/**	Enable/disable usage of UTF-8.
	When creating a new Fig writer structure the LANG
	environment variable is inspected to set the default
	value.
	@param	fwp	Fig writer structure.
	@param	fl	Flag to enable/disable.
*/
void
dkfigw_set_utf8 DK_P2(dk_fig_writer_t *,fwp, int,fl) {
  if(fwp) {
    fwp->utf = (fl ? 0x01 : 0x00);
  }
}



/**	Enable/disable verbose mode.
	In verbose mode the module prints warnings to stderr
	for illegal names in dkfigw_set_xxx_by_name() functions
	and suggests correct value.
	Verbose mode is turned off by default for new Fig
	writer structures.
	@param	fwp	Fig writer structure.
	@param	fl	Flag to enable/disable.
*/
void
dkfigw_set_verbose DK_P2(dk_fig_writer_t *,fwp, int,fl) {
  if(fwp) {
    fwp->ver = (fl ? 0x01 : 0x00);
  }
}



