/*
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	dkfigto2.c	Tool module 2.
*/



/**	Inside the dkfigto2 module.
*/
#define DKFIGTO2_C 1



#include "dkfig.h"




#line 54 "dkfigto2.ctr"




/**	File open mode: Read binary.
*/
static char str_mode_read_binary[] = { "rb" };

/**	File open mode: Read.
*/
static char str_mode_read_text[] = { "r" };

/**	Detect encoding: auto (use LANG environment variable.
*/
static char str_auto[] = { "auto" };

/**	Detect encoding: Keyword UTF-8.
*/
static char str_utf8[] = { ".UTF-8" };

/**	Sequence to open math mode in LaTeX.
*/
static char str_open_math_mode[] = { "\\(" };

/**	Sequence to close math mode in LaTeX.
*/
static char str_close_math_mode[] = { "\\)" };

/**	Text to search for bounding box.
*/
static char bb_search[] = { "%%BoundingBox:" };

/**	File open mode.
*/
static char str_mode_open_read[] = { "r" };

/**	Suffixes for compressed files.
*/
static char *suffix_types[] = {
  (char *)".gz",
  (char *)".bz2",
  NULL
};


/**	Compare to image width information structures.
	@param	l	Pointer to left structure.
	@param	r	Pointer to right structure.
	@param	c	Comparison criteria (ignored).
	@return	The comparison result.
*/
int
dkfig_tool2_compare_image_width DK_P3(void *,l, void *,r, int,c)
{
  int back = 0;
  dkfig_bitmap_width *wl, *wr;
  
  if(l) {
    if(r) {
      wl = (dkfig_bitmap_width *)l; wr = (dkfig_bitmap_width *)r;
      if(wl->width > wr->width) {
        back = 1;
      } else {
        if(wl->width < wr->width) {
	  back = -1;
	}
      }
      if(back == 0) {
        if(wl->colored) {
	  if(!(wr->colored)) {
	    back = 1;
	  }
	} else {
	  if(wr->colored) {
	    back = -1;
	  }
	}
      }
      if(back == 0) {
        if(wl->seprgb) {
	  if(wr->seprgb) {
	    if(wl->height > wr->height) {
	      back = 1;
	    } else {
	      if(wl->height < wr->height) {
	        back = -1;
	      }
	    }
	  } else {
	    back = -1;
	  }
	} else {
	  if(wr->seprgb) {
	    back = -1;
	  }
	}
      }
      /*
      if(back == 0) {
        if(wl->fl == 2) {
	  if(wr->fl != 2) {
	    back = 1;
	  }
	} else {
	  if(wr->fl == 2) {
	    back = -1;
	  }
	}
      } 
      */
    } else {
      back = 1;
    }
  } else {
    if(r) {
      back = -1;
    }
  } 
  return back;
}


/**	Create storage container to store image width data.
	@return	Pointer to new storage on success, NULL on error.
*/
dk_storage_t *
dkfig_tool2_image_width_storage DK_P0()
{
  dk_storage_t *back = NULL;
  back = dksto_open(0);
  if(back) {
    dksto_set_comp(back, dkfig_tool2_compare_image_width, 1);
  }
  return back;
}



/**	Find width structure for given image attributes.
	@param	it	Image width data storage iterator.
	@param	width	Image width.
	@param	height	Image height.
	@param	colored	Flag: Colored.
	@param	seprgb	Flag: Separated color channels.
	@param	fl	Flip information.
	@return	Pointer to image width structure on success, NULL on error.
*/
dkfig_bitmap_width *
dkfig_tool2_find_entry_for_width DK_P6(dk_storage_iterator_t *,it, long,width, long,height, int,colored, int,seprgb,int,fl)
{
  dkfig_bitmap_width w, *back = NULL;
  
  if(it) {
    w.width = width;
    w.height = height;
    if(fl == 2) {	
      w.width = height;
      w.height = width;
    }
    w.colored = colored;
    w.seprgb = seprgb;
    back = (dkfig_bitmap_width *)dksto_it_find_like(it, (void *)(&w), 0);
  }
  
  return back;
}



/**	Add image width structure to storage.
	@param	st	Image width data storage.
	@param	it	Image width data storage iterator.
	@param	width	Image width.
	@param	height	Image height.
	@param	colored	Flag: Colored.
	@param	seprgb	Flag: Separated color channels.
	@param	fl	Flip information.
	@return	1 on success, 0 on error.
*/
int
dkfig_tool2_add_image_width DK_P7(dk_storage_t *,st, dk_storage_iterator_t *,it, long,width, long,height, int,colored, int,seprgb, int,fl)
{
  int back = 0;
  dkfig_bitmap_width w, *ptr;
  
  if(st && it) {
    w.width = width;
    if(dkfig_tool2_find_entry_for_width(
      it, width, height,colored,seprgb,fl)
    )
    {
      back = 1;		
    } else {		
      ptr = dk_new(dkfig_bitmap_width,1);
      if(ptr) {
        ptr->width = width; ptr->height = height;
	ptr->colored = colored; ptr->seprgb = seprgb;
	/* ptr->fl = fl; */
	if(fl == 2) {	
	  ptr->width = height;
	  ptr->height = width;
	} 
	if(dksto_add(st, (void *)ptr)) {
	  back = 1;	
	} else {	
	  dk_delete(ptr);
	}
      } else {		
      }
    }
  } else {		
  }
  
  return back;
}



/**	Error message: One constant text.
	@param	c	Conversion job structure.
	@param	i	Text index.
*/
void
dkfig_tool2_simple_error_message DK_P2(dk_fig_conversion *,c, size_t,i)
{
  dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, i);
}



/**	Error output function for the EPS driver.
	@param	oi	EPS output instruction structure.
	@param	i	Index of error message.
*/
void
dkfig_tool2_eps_error_message DK_P2(dkfig_eps_output_instruction *,oi, size_t,i)
{
  if(oi) {
    dkfig_tool2_msg1(oi->c, DK_LOG_LEVEL_ERROR, i);
  }
}



/**	Error output function for the SVG driver.
	@param	oi	SVG output instruction structure.
	@param	i	Index of error message.
*/
void
dkfig_tool2_svg_error_message DK_P2(dkfig_svg_output_instruction *,oi, size_t,i)
{
  if(oi) {
    dkfig_tool2_msg1(oi->c, DK_LOG_LEVEL_ERROR, i);
  }
}



/**	Error message: constant text, variable text and constant text.
	@param	c	Conversion job structure.
	@param	i1	First constant text index.
	@param	i2	Second constant text index.
	@param	fn	Variable text.
*/
void
dkfig_tool2_combined_error_message DK_P4(dk_fig_conversion *,c, size_t,i1, size_t,i2, char *,fn)
{
  dkfig_tool2_msg3(c, DK_LOG_LEVEL_ERROR, i1, i2, fn);
}



/**	Progress message.
	@param	c	Conversion job structure.
	@param	i	Message index.
*/
void
dkfig_tool2_simple_progress_message DK_P2(dk_fig_conversion *,c, size_t,i)
{
  dkfig_tool2_msg1(c, DK_LOG_LEVEL_PROGRESS, i);
}



/**	Report why the special comment can not be used.
	@param	c	Conversion job structure.
	@param	speccom	Special comment/option structure.
	@param	i	Failure reason.
*/
void
dkfig_tool2_report_special_comment DK_P3(dk_fig_conversion *,c,dk_fig_opt *,speccom,int,i)
{
  if((c) && (speccom)) {
    if((c->app) && (c->msg1)) {
      switch(i) {
	case 0: {	/* unknown key */
	  dkapp_set_source_lineno(c->app, speccom->number);
	  dkfig_tool2_msg3(c, DK_LOG_LEVEL_WARNING, 86, 92, speccom->name);
	  dkapp_set_source_lineno(c->app, 0UL);
	} break;
	case -1: {	/* wrong driver */
	  dkapp_set_source_lineno(c->app, speccom->number);
	  dkfig_tool2_msg3(c, DK_LOG_LEVEL_WARNING, 87, 92, speccom->name);
	  dkapp_set_source_lineno(c->app, 0UL);
	} break;
	case -2: {	/* not per-instance */
	  dkapp_set_source_lineno(c->app, speccom->number);
	  dkfig_tool2_msg3(c, DK_LOG_LEVEL_WARNING, 88, 92, speccom->name);
	  dkapp_set_source_lineno(c->app, 0UL);
	} break;
	case -3: {	/* other error */
	  dkapp_set_source_lineno(c->app, speccom->number);
	  dkfig_tool2_msg3(c, DK_LOG_LEVEL_WARNING, 89, 92, speccom->name);
	  dkapp_set_source_lineno(c->app, 0UL);
	} break;
      }
    }
  }
}




/**	Report unused options.
	@param	c	Conversion job structure.
*/
void
dkfig_tool2_report_unused_options DK_P1(dk_fig_conversion *,c)
{
  dk_fig_opt *op;
  if(c) {
  if((c->app) && (c->msg1)) {
    char *oldname; unsigned long oldno;
    oldname = dkapp_get_source_filename(c->app);
    oldno = dkapp_get_source_lineno(c->app);
    dkapp_set_source_filename(c->app, NULL);
    dkapp_set_source_lineno(c->app, 0UL);
    /* command line options */
    if((c->opt) && (c->opti)) {
      dksto_it_reset(c->opti);
      while((op = (dk_fig_opt *)dksto_it_next(c->opti)) != NULL) {
        if(!(op->used)) {
	  dkfig_tool2_msg3(c, DK_LOG_LEVEL_WARNING, 90, 92, op->name);
	}
      }
    }
    /* config file */
    if(c->cfgfn) {
      dkapp_set_source_filename(c->app, c->cfgfn);
    }
    /* configuration section */
    if((c->optd) && (c->optdi)) {
      dksto_it_reset(c->optdi);
      while((op = (dk_fig_opt *)dksto_it_next(c->optdi)) != NULL) {
        if(!(op->used)) {
	  dkapp_set_source_lineno(c->app, op->number);
	  dkfig_tool2_msg3(c, DK_LOG_LEVEL_WARNING, 91, 92, op->name);
	}
      }
    }
    /* driver section */
    if((c->optb) && (c->optbi)) {
      dksto_it_reset(c->optbi);
      while((op = (dk_fig_opt *)dksto_it_next(c->optbi)) != NULL) {
        if(!(op->used)) {
	  dkapp_set_source_lineno(c->app, op->number);
	  dkfig_tool2_msg3(c, DK_LOG_LEVEL_WARNING, 91, 92, op->name);
	}
      }
    }
    /* global section */
    if((c->optg) && (c->optgi)) {
      dksto_it_reset(c->optgi);
      while((op = (dk_fig_opt *)dksto_it_next(c->optgi)) != NULL) {
        if(!(op->used)) {
	  dkapp_set_source_lineno(c->app, op->number);
	  dkfig_tool2_msg3(c, DK_LOG_LEVEL_WARNING, 91, 92, op->name);
	}
      }
    }
    dkapp_set_source_filename(c->app, oldname);
    dkapp_set_source_lineno(c->app, oldno);
  }
  }
}



/**	Read NetPBM image size to w and h.
	@param	c	Conversion job structure.
	@param	fn	File name.
	@param	w	Pointer to variable for image width.
	@param	h	Pointer to variable for image height.
	@return	1 on success, 0 on error.
*/
static
int
pbm_image_size DK_P4(dk_fig_conversion *,c, char *,fn, unsigned long *,w, unsigned long *,h)
{
  int back = 0;
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
  FILE *f = NULL;
  xelval maxxelval = 0;
  int cols = 0, rows = 0, format = 0;
  xel **array;
  
  if(c->app) {
    f = dkapp_fopen(c->app, fn, str_mode_read_binary);
  } else {
    f = dksf_fopen(fn, str_mode_read_binary);
  }
  if(f) {
    array = pnm_readpnm(f, &cols, &rows, &maxxelval, &format);
    if(array) {
      *w = (unsigned long)cols;
      *h = (unsigned long)rows;
      back = 1;
      
      pnm_freearray(array, rows);
    }
    fclose(f); f = NULL;
  }
  
#endif
  return back;
}



/**	Read PNG image size to w and h.
	@param	c	Conversion job structure.
	@param	fn	File name.
	@param	w	Pointer to variable for image width.
	@param	h	Pointer to variable for image height.
	@return	1 on success, 0 on error.
*/
static
int
png_image_size DK_P4(dk_fig_conversion *,c, char *,fn, unsigned long *,w, unsigned long *,h)
{
  int back = 0;
#if DK_HAVE_ZLIB_H
#if DK_HAVE_PNG_H
  FILE *f = NULL;
  png_uint_32 ui32 = 0, width = 0, height = 0;
  int bd = 0, ct = 0, zt = 0, it = 0, ft = 0;
  png_structp pp = NULL; png_infop pi = NULL;
  
  if(c->app) {
    f = dkapp_fopen(c->app, fn, str_mode_read_binary);
  } else {
    f = dksf_fopen(fn, str_mode_read_binary);
  }
  if(f) {
    pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL ,NULL, NULL);
    if(pp) {
      pi = png_create_info_struct(pp);
      if(pi) {
#if DK_HAVE_SETJMP_H
        if(setjmp(pp->jmpbuf) == 0) {
#endif
          png_init_io(pp, f);
	  png_read_info(pp, pi);
	  ui32 = png_get_IHDR(pp, pi, &width, &height, &bd, &ct, &it, &zt, &ft);
	  if(width && height) {
	    *w = (unsigned long)width;
	    *h = (unsigned long)height;
	    back = 1;
	    
	  } else {
	    dkfig_tool2_combined_error_message(c, 96, 97, fn);
	  }
#if DK_HAVE_SETJMP_H
	} else {
	  
	  dkfig_tool2_combined_error_message(c, 96, 97, fn);
	}
#endif
        png_destroy_info_struct(pp, &pi); pi = NULL;
      }
      png_destroy_read_struct(&pp, NULL, NULL); pp = NULL;
    }
    fclose(f); f = NULL;
  }
  
#endif
#endif
  return back;
}


#if DK_HAVE_JPEGLIB_H

/**	Flag: Already an error while reading JPEG file.
*/
static int had_error = 0;

/**	Do not exit on errors, set flag instead.
*/
static void error_exit_replacement DK_P1(j_common_ptr,cinfo)
{
  had_error = 1;
}
#endif



/**	Read JPEG image size to w and h.
	@param	c	Conversion job structure.
	@param	fn	File name.
	@param	w	Pointer to variable for image width.
	@param	h	Pointer to variable for image height.
	@return	1 on success, 0 on error.
*/
static
int
jpg_image_size DK_P4(dk_fig_conversion *,c, char *,fn, unsigned long *,w, unsigned long *,h)
{
  int back = 0;
#if DK_HAVE_JPEGLIB_H
  FILE *f = NULL;
  JDIMENSION width, height;
  struct jpeg_decompress_struct cinfo;
  struct jpeg_error_mgr jerr;
  
  had_error = 0;
  cinfo.err = jpeg_std_error(&jerr);
  jerr.error_exit = error_exit_replacement;
  jpeg_create_decompress(&cinfo);
  if(!had_error) {
    if(c->app) {
      f = dkapp_fopen(c->app, fn, str_mode_read_binary);
    } else {
      f = dksf_fopen(fn, str_mode_read_binary);
    }
    if(f) {
      jpeg_stdio_src(&cinfo, f);
      if(!had_error) {
        jpeg_read_header(&cinfo, TRUE);
	if(!had_error) {
	  width = cinfo.output_width;
	  height = cinfo.output_height;
	  if(width && height) {
	    *w = (unsigned long)width;
	    *h = (unsigned long)height;
	    back = 1;
	    
	  }
	}
      }
      jpeg_abort((j_common_ptr)(&cinfo));
      fclose(f); f = NULL;
    }
  }
  
#endif
  return back;
}



/**	Read EPS image size to w and h.
	@param	c	Conversion job structure.
	@param	fn	File name.
	@param	w	Pointer to variable for image width.
	@param	h	Pointer to variable for image height.
	@return	1 on success, 0 on error.
*/
static
int
eps_image_size DK_P4(dk_fig_conversion *,c, char *,fn, unsigned long *,w, unsigned long *,h)
{
  int back = 0;
  int state = 0;
  long llx, lly, urx, ury, deltax, deltay;
  char inputline[1024];
  FILE *f = NULL;
  size_t minlgt; char *ptr;
  
  llx = lly = urx = ury = deltax = deltay = 0L;
  if(c->app) {
    f = dkapp_fopen(c->app, fn, str_mode_read_text);
  } else {
    f = dksf_fopen(fn, str_mode_read_text);
  }
  if(f) {
    minlgt = strlen(bb_search);
    while(state < 2) {
      if(fgets(inputline, sizeof(inputline), f)) {
        if(strlen(inputline) > minlgt) {
	  if(strncmp(inputline, bb_search, minlgt) == 0) {
	    ptr = dkstr_start(&(inputline[minlgt]), NULL);
	    if(ptr) {
	      if(sscanf(ptr, "%ld %ld %ld %ld", &llx, &lly, &urx, &ury) == 4) {
	        back = 1;
		deltax = urx - llx;
		deltay = ury - lly;
		if(deltax < 0L) { deltax = 0L - deltax; }
		if(deltay < 0L) { deltay = 0L - deltay; }
		*w = (unsigned long)deltax;
		*h = (unsigned long)deltay;
		
		if(state == 0) {
		  state = 2;
		}
	      } else {
	        state = 1;
	      }
	    }
	  }
	}
      } else {
        state = 3;
      }
    }
    fclose(f); f = NULL;
  }
  
  return back;
}



/**	Retrieve image size.
	@param	c	Conversion job structure.
	@param	fn	Image file name.
	@param	w	Pointer to width result variable.
	@param	h	Pointer to height result variable.
	@return	1 on success, 0 on error.
*/
int
dkfig_tool2_get_image_size DK_P4(dk_fig_conversion *,c, char *,fn, unsigned long *,w, unsigned long *,h)
{
  int back = 0;
  int ft;
  if((fn) && (w) && (h)) {
    *w = 0UL; *h = 0UL;
    ft = dkfig_ei_get_image_type(fn);
    if(ft >= 0) {
      switch(ft) {
        case 0: {	/* PS/EPS */
	  back = eps_image_size(c, fn, w, h);
	} break;
	case 1: {	/* PNG */
	  back = png_image_size(c, fn, w, h);
	} break;
	case 2: {	/* JPEG */
	  back = jpg_image_size(c, fn, w, h);
	} break;
	case 3: {	/* NetPBM */
	  back = pbm_image_size(c, fn, w, h);
	} break;
      }
    }
  }
  return back;
}



/**	Check whether or not an object is a background rectangle.
	@param	o	Object to inspect.
	@param	wbgr	Flag: Background rectangle uses color white instead of default.
	@return	1=true, 0=false.
*/
int
dkfig_tool2_obj_is_bg_rect DK_P2(dk_fig_object *,o, int,wbgr)
{
  int back = 0;
  
  if(o->layer == 999L) {
    if(o->objtype == DK_FIG_OBJ_POLYLINE) {
      if(o->subtype == 2) {
        if((o->fpd).lt == 0L) {
	  if((o->fpd).af == -1) {
	    if(wbgr) {
	      if((o->fpd).pc == 7) {
	        back = 1;
	      }
	    } else  {
              if((o->fpd).pc == -1) {
	        back = 1;
	      }
	    }
	  }
	}
      }
    }
  } 
  return back;
}



/**	Double round with specified number of digits.
	@param	v	Value to round.
	@param	c	Conversion job structure.
	@param	w	Rounding type (1=coordinate, 2=coordinate obtained from trigonometric calculation, default=color).
*/
double
dkfig_tool2_drd DK_P3(double,v, dk_fig_conversion *,c, int,w)
{
  double back;
  back = v;
  if(c) {
  switch(w) {
    case 1: {
      back = dkma_double_restrict_digits(back, c->nodcoord);
    } break;
    case 2: {
      back = dkma_double_restrict_digits(back, (c->nodcoord + c->nodtrigo));
    } break;
    default: {
      back = dkma_double_restrict_digits(back, c->nodcolor);
    } break;
  }
  }
  return back;
}



/**	Retrieve UTF-8 enabled/disabled setting from LANG environment variable.
	@param	c	Conversion job structure.
	@return	1 on success (LANG environment var found), 0 on error.
*/
int
dkfig_tool2_utf8_auto DK_P1(dk_fig_conversion *,c)
{
  int back = 0;
  char *ptr1, *ptr2;
  
  if(c) {
    c->opt2 |= DKFIG_OPT_UTF_AUTO;
    ptr1 = getenv("LANG");
    if(ptr1) {
      back = 1;
      ptr2 = strchr(ptr1, '.');
      if(ptr2) {
        if(dkstr_casecmp(ptr2, str_utf8) == 0) {
	  c->opt2 |= DKFIG_OPT_UTF_8;	
	} else {			
	  c->opt2 &= (~(DKFIG_OPT_UTF_8));
	}
      } else {				
        c->opt2 &= (~(DKFIG_OPT_UTF_8));
      }
    } else {				
      c->opt2 &= (~(DKFIG_OPT_UTF_8));
    }
  } 
  return back;
}



/**	Enable/disable UTF-8.
	@param	c	Conversion job structure.
	@param	v	Text containing either a boolean or "auto".
	@param	allow_auto	Flag: Allow automatic detection of UTF-8 setting.
	@return	1 on success (tests found a result), 0 on error.
*/
int
dkfig_tool2_set_utf8 DK_P3(dk_fig_conversion *,c, char *,v, int,allow_auto)
{
  int back = 0;
  char *ptr1;
  
  if((c) && (v)) {
    ptr1 = dkstr_start(v, NULL);
    if(ptr1) {
      dkstr_chomp(ptr1, NULL);
      if(dkstr_is_bool(ptr1)) {
        if(dkstr_is_on(ptr1)) {	
	  c->opt2 |= DKFIG_OPT_UTF_8;
	} else {		
	  c->opt2 &= (~(DKFIG_OPT_UTF_8));
	}
	c->opt2 &= (~(DKFIG_OPT_UTF_AUTO));
	back = 1;
      } else {
	if(dkstr_casecmp(ptr1, str_auto) == 0) {
	  
	  back = dkfig_tool2_utf8_auto(c);
	}
      }
    }
  } 
  return back;
}



/**	Log message consisting of 2 constant parts and a variable one.
	@param	c	Conversion job structure.
	@param	l	Log level (DK_LOG_LEVEL_xxx).
	@param	s1	Index of first (constant) part.
	@param	s2	Index of third (constant) part.
	@param	t	Second (variable) part of the message.
*/
void
dkfig_tool2_msg3 DK_P5(dk_fig_conversion *,c, int,l, size_t,s1, size_t,s2, char *,t)
{
  char *msgs[3];
  if((c) && (t)) {
  if(c->msg1) {
    if(c->app) {
      msgs[0] = (c->msg1)[s1];
      msgs[1] = t;
      msgs[2] = (c->msg1)[s2];
      dkapp_log_msg(c->app, l, msgs, 3);
    } else {
    }
  }
  }
}



/**	Log message.
	@param	c	Conversion job structure.
	@param	l	Log level (DK_LOG_LEVEL_xxx).
	@param	s1	Index of message text.
*/
void
dkfig_tool2_msg1 DK_P3(dk_fig_conversion *,c, int,l, size_t,s1)
{
  if(c) {
  if(c->msg1) {
    if(c->app) {
      dkapp_log_msg(c->app, l, &((c->msg1)[s1]), 1);
    } else {
    }
  }
  }
}



/**	Report error if no encoding table was found for a
	32-bit character.
	@param	c	Conversion job structure.
	@param	ucb	The 32-bit character.
*/
static
void
error_no_table_found DK_P2(dk_fig_conversion *,c, dk_udword,ucb)
{
  char buffer[32];
  sprintf(buffer, "0x%08lx", ucb);
  dkfig_tool2_msg3(c, DK_LOG_LEVEL_WARNING, 123, 124, buffer);
}



/**	Report error if no encoding was found for a 32-bit character.
	@param	c	Conversion job structure.
	@param	ucb	The 32-bit character.
*/
static
void
error_no_encoding_found DK_P2(dk_fig_conversion *,c, dk_udword,ucb)
{
  char buffer[32];
  sprintf(buffer, "0x%08lx", ucb);
  dkfig_tool2_msg3(c, DK_LOG_LEVEL_WARNING, 125, 126, buffer);
}



/**	Write LaTeX representation of UTF-8 encoded string to
	output stream.
	@param	os	Output stream.
	@param	c	Conversion job structure.
	@param	t	UTF-8 encoded string.
*/
void
dkfig_tool2_utf8_to_latex DK_P3(dk_stream_t *,os, dk_fig_conversion *,c, char *,t)
{
  dk_udword ucb;
  int cc, mm, must_issue;
  size_t max, used, avail, step, u_obuffer;
  char obuffer[16], *found;
  
  cc = 1; max = strlen(t); used = 0; avail = max;
  mm = 0;	/* not in math mode by default */
  while(cc) {
    cc = 0; must_issue = 0;
    if(avail > 0) {
      step = 0;
      cc = dkenc_utf82uc(&ucb, (unsigned char *)(&(t[used])), avail, &step);
      if(cc) {
	found = NULL;
	if(dkle_load(c->uc2lat, ucb)) {
	  
	  found = dkle_get_encoding(c->uc2lat, ucb, 0);
	  if(found) {		
	    if(mm) { dkstream_puts(os, str_close_math_mode); mm = 0; }
	    dkstream_puts(os, found);
	  } else {		
	    found = dkle_get_encoding(c->uc2lat, ucb, 1);
	    if(found) {		
	      if(mm == 0) { dkstream_puts(os, str_open_math_mode); mm = 1; }
	      dkstream_puts(os, found);
	    } else {
	      if(ucb < 256UL) {	
	        found = dk_l2l_encoding((unsigned char)ucb);
		if(found) {	
		  if(mm) { dkstream_puts(os, str_close_math_mode); mm = 0; }
		  dkstream_puts(os, found);
		} else {
		  must_issue = 1; error_no_encoding_found(c, ucb);
		}
	      } else {
		must_issue = 1; error_no_encoding_found(c, ucb);
	      }
	    }
	  }
	} else {		
	  if(ucb < 256UL) {	
	    found = dk_l2l_encoding((unsigned char)ucb);
	    if(found) {		
	      if(mm) { dkstream_puts(os, str_close_math_mode); mm = 0; }
	      dkstream_puts(os, found);
	    } else {
	      must_issue = 1; error_no_encoding_found(c, ucb);
	    }
	  } else {
	    must_issue = 1; error_no_table_found(c, ucb);
	  }
	}
	if(must_issue) {
	  if(mm) { dkstream_puts(os, str_close_math_mode); mm = 0; }
	  /* Convert back and issue UTF-8 */
	  u_obuffer = dkenc_uc2utf8(ucb, (dk_ubyte *)obuffer, sizeof(obuffer));
	  if(u_obuffer > 0) {
	    obuffer[u_obuffer] = '\0';
	    dkstream_write(os, obuffer, u_obuffer);
	  }
	}
	used = used + step;
	if(avail > step) {
	  avail = avail - step;
	} else {
	  avail = 0;
	}
      } else {			
      }
    }
  }
  if(mm) {
    dkstream_puts(os, str_close_math_mode); mm = 0;
  } 
}



/**	Find compression type for file name.
	@param	n	File name.
	@return	-1=not compressed, 0=gzip, 1=bzip2.
*/
static
int
compression_type DK_P1(char *,n)
{
  int back = -1;
  char *suffixptr;
  suffixptr = dksf_get_file_type_dot(n);
  if(n) {
    back = dkstr_array_index(suffix_types, suffixptr, 0);
  }
  return back;
}



/**	Open the font mapping file.
	@param	c	Conversion structure.
	@param	n	File name of font mapping file.
	@return	Pointer to stream structure to read file on success,
	NULL on error.
*/
static
dk_stream_t *
open_the_mapping_file DK_P2(dk_fig_conversion *,c, char *,n) {
  dk_stream_t *back = NULL;
  switch(compression_type(n)) {
    case 0: {
#if DK_HAVE_ZLIB_H
      if(c->app) {
        back = dkapp_stream_opengz(c->app, n, str_mode_open_read);
      } else {
        back = dkstream_opengz(n, str_mode_open_read, 0, NULL);
      }
#else
      if(c->app) dkapp_err_no_zlib_support_for(c->app, n);
#endif
    } break;
    case 1: {
#if DK_HAVE_BZLIB_H
      if(c->app) {
        back = dkapp_stream_openbz2(c->app, n, str_mode_open_read);
      } else {
        back = dkstream_openbz2(n, str_mode_open_read, 0, NULL);
      }
#else
      if(c->app) dkapp_err_no_bzlib_support_for(c->app, n);
#endif
    } break;
    default: {
      if(c->app) {
        back = dkapp_stream_openfile(c->app, n, str_mode_open_read);
      } else {
        back = dkstream_openfile(n, str_mode_open_read, 0, NULL);
      }
    } break;
  }
  return back;
}



/**	Create font mapping from file.
	@param	c	Conversion structure.
	@return	Pointer to font mapping on success, NULL on error.
*/
dk_font_mapping_t *
dkfig_tool2_read_font_mapping DK_P1(dk_fig_conversion *,c) {
  dk_font_mapping_t		*back = NULL;	/* Function result. */
  char				*n;		/* File name buffer. */
  size_t			szn;		/* Size of n. */
  char				*ofn;		/* Old error file name. */
  unsigned long			oln;		/* Old error line number. */
  dk_stream_t			*st;		/* Stream to read file. */
  
  if(c->fcfg) {				
    back = dkfont_mapping_open();
    if(back) {
      if(c->app) {			
        szn = (size_t)dksf_get_maxpathlen();
	n = dk_new(char,szn);
	if(n) {
	  if(dkapp_find_file(c->app, c->fcfg, n, szn)) {
	    st = open_the_mapping_file(c, n);
	    if(st) {
	      if(!dkfont_mapping_add_stream(back, st)) {
	        ofn = dkapp_get_source_filename(c->app);
		oln = dkapp_get_source_lineno(c->app);
		dkapp_set_source_filename(c->app, n);
		dkapp_set_source_lineno(c->app, dkfont_get_error_lineno(back));
		switch(dkfont_get_error_code(back)) {
		  case DK_ERR_NOMEM: {
		    dkfig_tool2_simple_error_message(c, 11);
		  } break;
		  default: {
		    dkfig_tool2_simple_error_message(c, 12);
		  } break;
		}
		dkapp_set_source_lineno(c->app, oln);
		dkapp_set_source_filename(c->app, ofn);
	        dkfont_mapping_close(back); back = NULL;
	      }
	      dkstream_close(st);
	    } else {
	      dkfont_mapping_close(back); back = NULL;
	      dkapp_err_fopenr(c->app, n);
	    }
	  } else {
	    dkfont_mapping_close(back); back = NULL;
	    dkapp_err_matchfile(c->app, c->fcfg);
	  }
	  dk_delete(n);
	} else {
	  dkfont_mapping_close(back); back = NULL;
	  dkapp_err_memory(c->app, 1, szn);
	}
      } else {				
        st = open_the_mapping_file(c, c->fcfg);
	if(st) {
	  if(!dkfont_mapping_add_stream(back, st)) {
	    dkfont_mapping_close(back); back = NULL;
	    /* ##### ERROR: Failed to use font file. */
	  }
	  dkstream_close(st);
	} else {
	  dkfont_mapping_close(back); back = NULL;
	  /* ##### ERROR: Failed to read file. */
	}
      }
    } else {				
      /* ##### ERROR: Memory. */
    }
  } else {				
    /* ##### ERROR: No file specified. */
  } 
  return back;
}



