/*
   drvFIG.cpp : This file is part of pstoedit
   Based on the skeleton for the implementation of new backends

   Copyright (C) 1993 - 2014 Wolfgang Glunz, wglunz35_AT_pstoedit.net

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

/*
    FIG 3.1 driver by Ian MacPhedran (Ian_MacPhedran_AT_engr.usask.ca)
    April 1995

    Color support and conversion to use C++ streams done by Wolfgang Glunz

    Object depth support by Gerhard Kircher <kircher_AT_edvz.tuwien.ac.at>
    March 1996

    Curves and image support by Leszek Piotrowicz <zorro_AT_delta.ds2.pg.gda.pl>
    July 1997

    Support for xfig format 3.2 (X-Splines) by Wolfgang Glunz

    Support for subpaths by Burkhard Plaum
    
 Change to level calculation Ian MacPhedran
 November 2000
 Simplify - remove superfluous 3.1 code Ian MacPhedran
 December 2000
 Use quintic interpolated X-Splines Ian MacPhedran
 December 2000
 Fix for font scaling - Thanks to Robert K. Nelson - Ian MacPhedran
 April 2002

*/

#include "drvfig.h"

// for sprintf
#include I_stdio
#include I_string_h
#include I_iostream
#include I_iomanip


static const float PntFig = 1200.0f / 72.0f;


static const char *colorstring(float r, float g, float b)
{
	static char buffer[15];
	sprintf_s(TARGETWITHLEN(buffer,15), "%s%.2x%.2x%.2x", "#", (unsigned int) (r * 255 + 0.5),
			(unsigned int) (g * 255 + 0.5), (unsigned int) (b * 255 + 0.5));

	return buffer;
}

static const char *fig_default_colors[] = {
	"#000000",
	"#0000ff",
	"#00ff00",
	"#00ffff",
	"#ff0000",
	"#ff00ff",
	"#ffff00",
	"#ffffff",
	"#000090",
	"#0000b0",
	"#0000d0",
	"#87ceff",
	"#009000",
	"#00b000",
	"#00d000",
	"#009090",
	"#00b0b0",
	"#00d0d0",
	"#900000",
	"#b00000",
	"#d00000",
	"#900090",
	"#b000b0",
	"#d000d0",
	"#803000",
	"#a04000",
	"#c06000",
	"#ff8080",
	"#ffa0a0",
	"#ffc0c0",
	"#ffe0e0",
	"#ffd700"
};

const int defaults = sizeof(fig_default_colors) / sizeof(char *);
static ColorTable colorTable(fig_default_colors, defaults, colorstring);

static unsigned int registercolor(float r, float g, float b)
{
	return colorTable.getColorIndex(r, g, b);
}

static void dumpnewcolors(ostream & theoutStream)
{
	unsigned int current = defaults;
	const char *colstring = 0;
	while ((colstring = colorTable.getColorString(current)) != 0) {
		theoutStream << "0 " << current << " " << colstring << endl;
		current++;
	}
}


drvFIG::derivedConstructor(drvFIG):
constructBase,
buffer(tempFile.asOutput()),
imgcount(1),
format(32),
//startdepth(999),
//use_correct_font_size(false),
glob_min_x(0), glob_max_x(0), glob_min_y(0), glob_max_y(0),
loc_min_x(0), loc_max_x(0), loc_min_y(0), loc_max_y(0), glo_bbox_flag(0), loc_bbox_flag(0)

{
	// driver specific initializations
//	float depth_in_inches = 11;
//	bool show_usage_line = false;


#if 0
	for (unsigned int i = 0; i < d_argc; i++) {
		assert(d_argv && d_argv[i]);	//lint !e796 !e1776
		if (Verbose())
			outf << "% " << d_argv[i] << endl;
		if (strcmp(d_argv[i], "-startdepth") == 0) {
			i++;
			if (i >= d_argc) {
				errf << "-startdepth requires a depth in the range of 0-999" << endl;
				show_usage_line = true;
			} else {
				assert(d_argv && d_argv[i]);	//lint !e796 !e1776
				startdepth = (int) atoi(d_argv[i]);
			}
			//            } else if (strcmp(d_argv[i],"-f31") == 0) {
			//                        format = 31;
		} else if (strcmp(d_argv[i], "-depth") == 0) {
			i++;
			if (i >= d_argc) {
				errf << "-depth requires a depth in inches" << endl;
				show_usage_line = true;
			} else {
				assert(d_argv && d_argv[i]);	//lint !e796 !e1776
				depth_in_inches = (float) atof(d_argv[i]);
			}
		} else if (strcmp(d_argv[i], "-metric") == 0) {
			units = "Metric";
		} else if (strcmp(d_argv[i], "-help") == 0) {
			errf << "-help    Show this message" << endl;
			errf << "-depth # Set the page depth in inches" << endl;
			errf << "-metric # Set display to use centimeters" << endl;
			errf << "-startdepth # Set the initial depth (default 999)" << endl;
			show_usage_line = true;
		} else {
			errf << "Unknown fig driver option: " << d_argv[i] << endl;
			show_usage_line = true;
		}
	}

	if (show_usage_line) {
		errf << "Usage -f 'fig: [-help] [-depth #] [-startdepth #]'" << endl;
	}
#endif

	const char *units = (options->metric) ? "Metric" : "Inches";

	// Set the papersize
	const char *paper_size  = (options->depth_in_inches <= 11.0 ? "Letter" : "A4");

	// set FIG specific values
	currentDeviceHeight = options->depth_in_inches * 1200.0f ;
	// We use objectId as depth value.
	// We need this because editing will reorder objects of equal depth,
	// which has an undesireable effect if objects overlap.
	// Depth must be in the range 0..999 (FIG 3.1).
	// If we have more than 1000 objects this will get negative and
	// xfig will set negative depth values to zero.
	// This feature will thus become useless if we have more
	// than 1000 objects. This is an xfig limitation.
	objectId = options->startdepth + 1;	// +1 because a predecrement is done when used

	x_offset = 0.0;
	y_offset = currentDeviceHeight;
	// output buffer, needed because
	// color entries must be written at
	// top of output file, but are known
	// only after processing the full input

	// print the header part
	outf << "#FIG 3.2\nPortrait\nFlush left\n" << units << "\n" << paper_size <<
		"\n100.00\nSingle\n0\n1200 2\n";
}

drvFIG::~drvFIG()
{
	dumpnewcolors(outf);
	// now we can copy the buffer the output
	ifstream & inbuffer = tempFile.asInput();
	copy_file(inbuffer, outf);
	options=0;
}


unsigned int
 drvFIG::nrOfCurvetos() const
{
	unsigned int nr = 0;
	for (unsigned int n = 0; n < numberOfElementsInPath(); n++) {
		const basedrawingelement & elem = pathElement(n);
		if (elem.getType() == curveto)
			nr++;
	}
	return nr;
}

void drvFIG::prpoint(ostream & os, const Point & p, bool withspaceatend) const
{
	os << (int) (PntFig * p.x_ + 0.5f) << " " << (int) (y_offset - (PntFig * p.y_) + 0.5f);
	if (withspaceatend)
		os << " ";
}


 // static int nd_calls=0; Debug

// bounding box
void drvFIG::new_depth()
{
// errf <<  "Called #" << nd_calls++ << "\n"; Debug
	if (glo_bbox_flag == 0) {
		glob_max_y = loc_max_y;
		glob_min_y = loc_min_y;
		glob_max_x = loc_max_x;
		glob_min_x = loc_min_x;
		glo_bbox_flag = 1;
	} else {
		if ((loc_max_y <= glob_min_y) ||
			(loc_min_y >= glob_max_y) || (loc_max_x <= glob_min_x) || (loc_min_x >= glob_max_x)) {
// outside global bounds, increase global box
			if (loc_max_y > glob_max_y)
				glob_max_y = loc_max_y;
			if (loc_min_y < glob_min_y)
				glob_min_y = loc_min_y;
			if (loc_max_x > glob_max_x)
				glob_max_x = loc_max_x;
			if (loc_min_x < glob_min_x)
				glob_min_x = loc_min_x;
		} else {
// inside global bounds, decrease depth and create new bounds
//      errf << " Overlap detected\n" 
//          << " Local " << loc_min_x << ", " << loc_min_y
//          << ", " << loc_max_x << ", " << loc_max_y << "\n"
//          << "Global " << glob_min_x << ", " << glob_min_y 
//          << ", " << glob_max_x << ", " << glob_max_y << "\n"; Debug
			glob_max_y = loc_max_y;
			glob_min_y = loc_min_y;
			glob_max_x = loc_max_x;
			glob_min_x = loc_min_x;
			if (objectId)
				objectId--;		// don't let it get < 0
//      errf << " Depth increased " << objectId << "\n"; Debug
		}
	}
	loc_bbox_flag = 0;
}

void drvFIG::addtobbox(const Point & p)
{
	if (loc_bbox_flag == 0) {
		loc_max_y = p.y_;
		loc_min_y = p.y_;
		loc_max_x = p.x_;
		loc_min_x = p.x_;
		loc_bbox_flag = 1;
	} else {
		if (loc_max_y < p.y_)
			loc_max_y = p.y_;
		if (loc_min_y > p.y_)
			loc_min_y = p.y_;
		if (loc_max_x < p.x_)
			loc_max_x = p.x_;
		if (loc_min_x > p.x_)
			loc_min_x = p.x_;
	}
}


void drvFIG::print_polyline_coords()
{
	int j = 0;
	//  const Point & p;
	unsigned int last = numberOfElementsInPath() - 1;
	for (unsigned int n = 0; n <= last; n++) {
		const basedrawingelement & elem = pathElement(n);
		if (j == 0) {
			buffer << "\t";
		}
		switch (elem.getType()) {
		case lineto:
		case moveto:
			{
				const Point & p = pathElement(n).getPoint(0);
				prpoint(buffer, p, (n != last));
			}
			break;
		case closepath:
			{
				const Point & p = pathElement(0).getPoint(0);
				prpoint(buffer, p, (n != last));
			}
			break;
		case curveto:
		default:
			errf << "\t\tFatal: unexpected case in drvfig " << endl;
			abort();
			break;

		}

		j++;
		if (j == 5) {
			j = 0;
			buffer << "\n";
		}
	}
	if (j != 0) {
		buffer << "\n";
	}
}

// print edge points
void drvFIG::print_spline_coords1()
{
// IJMP - need curr_point
	Point P1;
	int j = 0;
	unsigned int last = numberOfElementsInPath() - 1;
	for (unsigned int n = 0; n <= last; n++) {
		if (j == 0) {
			buffer << "\t";
		}
		const basedrawingelement & elem = pathElement(n);
		switch (elem.getType()) {
		case moveto:
		case lineto:
			{
				const Point & p = elem.getPoint(0);
				prpoint(buffer, p, (n != last));
				P1 = p;
			}
			j++;
			if (j == 5) {
				j = 0;
				buffer << "\n";
			}
			break;
		case curveto:
			{
// IJMP - change to quintic spline
				// put all points, middle points will have control value 1
// IJMP = do bezier fit
				
				const Point & P2 = elem.getPoint(0);
				const Point & P3 = elem.getPoint(1);
				const Point & P4 = elem.getPoint(2);

				for (int cp = 1; cp <= 5; cp++) {
					const Point p = PointOnBezier((cp * 0.2f), P1, P2, P3, P4);
					// p.x_ = bezpnt((cp * 0.2f), P1.x_, P2.x_, P3.x_, P4.x_);
					// p.y_ = bezpnt((cp * 0.2f), P1.y_, P2.y_, P3.y_, P4.y_);
					prpoint(buffer, p, !((n == last) && (cp == 5)));
					j++;
					if (j == 5) {
						j = 0;
						buffer << "\n";
					}
//                      if ((j == 0) && (cp != 5) && ( n+1 != (numberOfElementsInPath()))) { buffer << "\t"; }
					if ((j == 0) && (n != (numberOfElementsInPath()))) {
						buffer << "\t";
					}
				}
				P1 = P4;
			}
			break;
		case closepath:
			{
				const Point & p = pathElement(0).getPoint(0);
				P1 = p;
				prpoint(buffer, p, (n != last));
			}
			j++;
			if (j == 5) {
				j = 0;
				buffer << "\n";
			}
			break;
		default:
			errf << "\t\tFatal: unexpected case in drvfig " << endl;
			abort();
			break;
		}
	}
	if (j != 0) {
		buffer << "\n";
	}
	buffer << "\t";
}

// print control points
void drvFIG::print_spline_coords2()
{
	int j = 0;
	Point lastp;
	int maxj = 8;

	unsigned int last = numberOfElementsInPath() - 1;
	for (unsigned int n = 0; n <= last; n++) {
		const basedrawingelement & elem = pathElement(n);
		switch (elem.getType()) {
		case moveto:
			{
				buffer << " 0";
				if (n != last)
					buffer << " ";
				j++;
				if (j == maxj) {
					j = 0;
					buffer << "\n";
					if ((n + 1) != numberOfElementsInPath()) {
						buffer << "\t";
					}
				}
				lastp = elem.getPoint(0);
			}
			break;
		case lineto:
			{
				buffer << " 0";
				if (n != last)
					buffer << " ";
				j++;
				if (j == maxj) {
					j = 0;
					buffer << "\n";
					if ((n + 1) != numberOfElementsInPath()) {
						buffer << "\t";
					}
				}
			}
			break;
		case closepath:
			{
				buffer << " 0";
				if (n != last)
					buffer << " ";
				j++;
				if (j == maxj) {
					j = 0;
					buffer << "\n";
					if ((n + 1) != numberOfElementsInPath()) {
						buffer << "\t";
					}
				}
			}
			break;
		case curveto:
			{
// IJMP - change to quintic spline
				// put all points, middle points will have control value -1
				float kp = 0.0;
				for (unsigned int i = 0; i < 5; i++) {
					if (i == 1) {
						kp = -1.0; //lint !e736
					}
					if (i == 4) {
						kp = 0.0;
					}
					buffer << " " << kp;
					if (!((n == last) && (i == 4)))
						buffer << " ";
					j++;
					if (j == maxj) {
						j = 0;
						buffer << "\n";
						if (!((i == 4)
							  && ((n + 1) == numberOfElementsInPath()))) {
							buffer << "\t";
						}
					}
				}
				lastp = elem.getPoint(2);
			}
			break;
		default:
			errf << "\t\tFatal: unexpected case in drvfig " << endl;
			abort();
			break;
		}
	}
	if (j != 0) {
		buffer << endl;
	}
}

void drvFIG::close_page()
{
// Well, since FIG doesn't support multipage output, we'll move down
// 11 inches and start again.
	y_offset += currentDeviceHeight;
	// reset depth counter
	objectId = options->startdepth + 1;	// also changed to 499
}

void drvFIG::open_page()
{
}

struct FontTableType { 
	int index; 
	const char * fontname; 
};
	//
	// FIG 3.1 uses an index for the popular fonts:
	//
	// (we cannot make this array local to drvFIG::show_textstring
	// because some CCs don't support that yet.

static const FontTableType FigPSFonts[] = {
	{0,"Times-Roman"}, 
	{1,"Times-Italic"}, 
	{2,"Times-Bold"}, 
	{3,"Times-BoldItalic"},
	{4,"AvantGarde-Book"}, 
	{5,"AvantGarde-BookOblique"}, 
	{6,"AvantGarde-Demi"},
	{7,"AvantGarde-DemiOblique"}, 
	{8,"Bookman-Light"}, 
	{9,"Bookman-LightItalic"},
	{10,"Bookman-Demi"}, 
	{11,"Bookman-DemiItalic"}, 
	{12,"Courier"}, 
	{13,"Courier-Oblique"},
	{14,"Courier-Bold"}, 
	{15,"Courier-BoldOblique"}, 
	{16,"Helvetica"},
	{17,"Helvetica-Oblique"},
	{18,"Helvetica-Bold"}, 
	{19,"Helvetica-BoldOblique"}, 
	{20,"Helvetica-Narrow"},
	{21,"Helvetica-Narrow-Oblique"}, 
	{22,"Helvetica-Narrow-Bold"},
	{23,"Helvetica-Narrow-BoldOblique"}, 
	{24,"NewCenturySchlbk-Roman"},
	{25,"NewCenturySchlbk-Italic"}, 
	{26,"NewCenturySchlbk-Bold"},
	{27,"NewCenturySchlbk-BoldItalic"}, 
	{28,"Palatino-Roman"},
	{29,"Palatino-Italic"}, 
	{30,"Palatino-Bold"}, 
	{31,"Palatino-BoldItalic"},
	{32,"Symbol"}, 
	{33,"ZapfChancery-MediumItalic"}, 
	{34,"ZapfDingbats"}
};

static const size_t MaxPSFntnum = sizeof(FigPSFonts) / sizeof(FontTableType) - 1;

/*
	For font_flags bit 2 = 0 (LaTeX fonts):

	 0	Default font
	 1	Roman
	 2	Bold
	 3	Italic
	 4	Sans Serif
	 5	Typewriter
*/
	static const FontTableType FigLaTeXFonts[] = {
		{0,"Default font"},
		{0,"Default"},
		{0,"Defaultfont"},
		{1,"Roman"},
		{2,"Bold"},
		{3,"Italic"},
		{4,"Sans Serif"},
		{4,"Sans-Serif"},
		{4,"SansSerif"},
		{4,"Sansserif"},
		{5,"Typewriter"}
	};

static const size_t MaxLaTeXFntnum = sizeof(FigLaTeXFonts) / sizeof(FontTableType) - 1;


static int getfigFontnumber(const char *fname,const FontTableType *FigFonts, unsigned int MaxFntNum)
{
	const size_t fntlength = strlen(fname);
	for ( unsigned int i = 0; i <= MaxFntNum; i++) {
		if (fntlength == strlen(FigFonts[i].fontname)) {
			if (strncmp(fname, FigFonts[i].fontname, fntlength) == 0)
				return FigFonts[i].index;
		}
	}
	return -1;
}



void drvFIG::show_text(const TextInfo & textinfo)
{
	const float toRadians = 3.14159265359f / 180.0f;

/*
	The font_flags field is defined as follows:

	 Bit	Description

	  0	Rigid text (text doesn't scale when scaling compound objects)
	  1	Special text (for LaTeX)
	  2	PostScript font (otherwise LaTeX font is used)
	  3	Hidden text

*/

/*
Notation for font names:

((LaTeX|PostScript|<empty>)(::special)::)FontName

Examples:

LaTeX::Fontname
LaTeX::special::Fontname

FontName
PostScript::special::Fontname
::special::Fontname (same as PostScript::special::Fontname)


*/

	int fontflags = 4; // fontflags (bit vector)
	// 4 is 0100 - PostScript

	int FigFontNum = 0;
	const char * const specialindex = strstr(textinfo.currentFontName.c_str(),"::special::");
	const bool special = (specialindex != 0);
	if (!strncmp(textinfo.currentFontName.c_str(),"LaTeX::",7) ) {
		// it is a LaTeX Font
		fontflags = special ? 2 : 0 ; // 0010 or 0000  - LaTeX
		const char* fontname = special ? (specialindex + 11) : ( textinfo.currentFontName.c_str() + 7);
		FigFontNum = getfigFontnumber(fontname,FigLaTeXFonts,MaxLaTeXFntnum);
		// debug cout << "LaTeX::" << (const char *) (special ? "special" : "normal") << "::"<< fontname << " " <<textinfo.currentFontName.c_str() << endl;
		if (FigFontNum == -1) {
			errf << "Warning, unsupported font " << fontname << ", using LaTeX default instead.";
			FigFontNum = 0;
		}
	} else {
		const char * fontname = textinfo.currentFontName.c_str();
		if (!strncmp(textinfo.currentFontName.c_str(),"PostScript::",12) ) {
			fontname+=12; // just skip "PostScript::"
		}
		if (special) { fontname+=11 ; } // just skip "::special::"
		fontflags = special ? 6 : 4; // 0110 or 0100 - PostScript
		// debug cout << "PostScript::" << (const char *) (special ? "special" : "normal") << "::" << fontname<< " " <<textinfo.currentFontName.c_str() << endl;
		FigFontNum = getfigFontnumber(fontname,FigPSFonts,MaxPSFntnum);
		if (FigFontNum == -1) {
			errf << "Warning, unsupported font " << fontname << ", using ";
			FigFontNum = getfigFontnumber(defaultFontName,FigPSFonts,MaxPSFntnum);
			if (FigFontNum != -1) {
				errf << defaultFontName;
			} else {
				if (strstr(fontname, "Bold") == 0) {
					if (strstr(fontname, "Italic") == 0) {
						errf << "Times-Roman";
						FigFontNum = 0;	// Times-Roman
					} else {
						FigFontNum = 1;
						errf << "Times-Italic";
					}
				} else {
					if (strstr(fontname, "Italic") == 0) {
						errf << "Times-Bold";
						FigFontNum = 2;	// Times-Bold
					} else {
						FigFontNum = 3;
						errf << "Times-BoldItalic";
					}
				}
			}
			errf << " instead." << endl;
		}
	}

	const unsigned int color = registercolor(textinfo.currentR, textinfo.currentG,
									textinfo.currentB);
	float localFontSize = textinfo.currentFontSize;
	if (localFontSize <= 0.1) {
		localFontSize = 9;
	}
	if (!options->use_correct_font_size) {
		// formerly xfig used scaled font sizes and not the X11 font sizes
		localFontSize = (((float)localFontSize * 80.0f) / 72.0f) + 0.5f; 
	}

	const float FigHeight = PntFig * localFontSize;
	const float FigLength = FigHeight * strlen(textinfo.thetext.c_str());
	const float PSHeight = localFontSize;
	const float PSLength = PSHeight * strlen(textinfo.thetext.c_str());
// Calculate BBox
	if (textinfo.currentFontAngle == 0) {
		addtobbox(Point(textinfo.x, textinfo.y));
		addtobbox(Point((textinfo.x + PSLength), (textinfo.y + PSHeight)));
	} else if (textinfo.currentFontAngle == 90) {
		addtobbox(Point(textinfo.x, textinfo.y));
		addtobbox(Point((textinfo.x - PSHeight), (textinfo.y + PSLength)));
	} else if (textinfo.currentFontAngle == 180) {
		addtobbox(Point(textinfo.x, textinfo.y));
		addtobbox(Point((textinfo.x - PSLength), (textinfo.y - PSHeight)));
	} else if (textinfo.currentFontAngle == 270) {
		addtobbox(Point(textinfo.x, textinfo.y));
		addtobbox(Point((textinfo.x + PSHeight), (textinfo.y - PSLength)));
	} else {
// To simplify this, a box of width 2*PSLength centered on textinfo.(x,y) used
		addtobbox(Point((textinfo.x - PSLength), (textinfo.y + PSLength)));
		addtobbox(Point((textinfo.x + PSLength), (textinfo.y + PSLength)));
		addtobbox(Point((textinfo.x - PSLength), (textinfo.y - PSLength)));
		addtobbox(Point((textinfo.x + PSLength), (textinfo.y - PSLength)));
	}
	buffer << "# text\n";
	new_depth();

	
	buffer << "4 0 "; // 4 means text, 0 left justified
	buffer << color;
	if (objectId)
		objectId--;				// don't let it get < 0
	buffer << " " << objectId // depth
		<< " -1 " // pen_style - not used
		<< FigFontNum << " "
		<< (int) localFontSize << " "
		<< textinfo.currentFontAngle * toRadians 
		<< " " << fontflags << " "  
		<< FigHeight << " "
		<< FigLength << " "
		<< (int) (PntFig * textinfo.x + 0.5f) << " "
		<< (int) (y_offset - (PntFig * textinfo.y) + 0.5f) << " " << textinfo.thetext.c_str() << "\\001\n";
}

void drvFIG::bbox_path()
{
	for (unsigned int i = 0; i < numberOfElementsInPath(); i++) {
		const basedrawingelement & elem = pathElement(i);
		switch (elem.getType()) {
		case curveto:
			{
				addtobbox(elem.getPoint(0));
				addtobbox(elem.getPoint(1));
				addtobbox(elem.getPoint(2));
				break;
			}
		case moveto:
		case lineto:
			{
				addtobbox(elem.getPoint(0));
				break;
			}
		case closepath:
		default:
			{					// will get caught later
				break;
			}
		}
	}
	new_depth();
}

void drvFIG::show_path()
{
	float localLineWidth = currentLineWidth();
	localLineWidth *= 80.0f/72.0f; // xfig scales width differently - added in 3.50 - wogl
	// dont know whether this should be synchronized with -usecorrectfontsize option.



	/*
	3.50:
Originally, the resolution of Fig lines was 1/80 inch, and it was thought that the line width of "1" looked too thick, so it was reduced.
Therefore, the final width of lines in PostScript/EPS is:

Figwidth == 1: 7.5
Figwidth > 1: (Figwidth-1)*15

Regards,
Brian Smith

in reverse:

l < (15+7.5)/2 : f = 1
f = (l /15) + 1

	*/

	const float boundaryforOneCase = (((15.0f + 7.5f)/2.0f) / 15.0f);  // 0.75
	// if calculated linewidth is (without "+1 correction") > 0.75 
	// then apply correction by 1 
	// for the 0.75 case itself it means - map it to 1.75 and from there to 2
	// if it is < 0.75, then leave it so and create in fig a 1 ( (int) 0.75+0.5 )
	if (Verbose() ) {
		cerr << "localLineWidth " << localLineWidth  << " b " << boundaryforOneCase << endl;
	}
	if (localLineWidth > boundaryforOneCase) { 
		localLineWidth += 1.0f; // see above
	} else {
		// line width of 0 remain 0 - everything else is at least 1 
		if ((localLineWidth < 0.0) || ((localLineWidth > 0.0) && (localLineWidth <= 1.0))) {
			localLineWidth = 1.0;
		} 
	}

	unsigned int linestyle = 0;
	switch (currentLineType()) {
	case solid:
		linestyle = 0;
		break;
	case dashed:
		linestyle = 1;
		break;
	case dotted:
		linestyle = 2;
		break;
	case dashdot:
		linestyle = 3;
		break;
	case dashdotdot:
		linestyle = 4;
		break;
	default: ; // not expected
	}

	const unsigned int linecap = currentLineCap();
	const unsigned int linejoin = currentLineJoin();
	// Calculate BBox
	bbox_path();

	const unsigned int curvetos = nrOfCurvetos();
	if (curvetos == 0)			// polyline
	{
		buffer << "# polyline\n";
		buffer << "2 1 " << linestyle << " " << (int) (localLineWidth + 0.5f) << " ";
		const unsigned int color = registercolor(currentR(), currentG(), currentB());
		const int fill_or_nofill = (currentShowType() == drvbase::stroke) ? -1 : 20;
		if (objectId)
			objectId--;			// don't let it get < 0
		buffer << color << " " << color << " " << objectId << " 0 " <<
			fill_or_nofill << " " << "4.0" << 
			" " << linejoin << " " << linecap 
			//" 0 0"
			<< " 0 0 0 ";
		// 4.0 is the gap spec. we could also derive this from the input
		buffer << (int) (numberOfElementsInPath()) << "\n";
		print_polyline_coords();
	} else						// contains at least one curveto 
	{
		buffer << "# spline\n";
		// 3 2 means "open interpolated spline"
		buffer << "3 4 " << linestyle << " " << (int) (localLineWidth + 0.5f) << " ";
		const unsigned int color = registercolor(currentR(), currentG(), currentB());
		const int fill_or_nofill = (currentShowType() == drvbase::stroke) ? -1 : 20;
		if (objectId)
			objectId--;			// don't let it get < 0
		buffer << color << " " << color << " " << objectId << " 0 " <<
			fill_or_nofill << " " << "4.0" << //" 0"
			" " << linecap 
			<< " 0 0 ";
		// 4.0 is the gap spec. we could also derive this from the input

		// IJMP - change to quintic spline - 5 pnts per spline, not 3
		buffer << numberOfElementsInPath() + 5 * curvetos - curvetos << "\n";
		print_spline_coords1();
		print_spline_coords2();

	}
}


void drvFIG::show_image(const PSImage & imageinfo)
{
	if (outBaseName == "") {
		errf << "images cannot be handled via standard output. Use an output file " << endl;
		return;
	}
	if (imageinfo.isFileImage) {
		// use imageinfo.FileName;
		/* 
		outf << "<image "		 
			<< " transform=\"matrix("
			<< imageinfo.normalizedImageCurrentMatrix[0] << ' '
			<< -imageinfo.normalizedImageCurrentMatrix[1] << ' '
			<< imageinfo.normalizedImageCurrentMatrix[2] << ' '
			<< -imageinfo.normalizedImageCurrentMatrix[3] << ' '
// transfer
			<< imageinfo.normalizedImageCurrentMatrix[4] << ' '
			<< currentDeviceHeight - imageinfo.normalizedImageCurrentMatrix[5]
			<< ")\"" 
			<< " width=\"" << imageinfo.width << "\"" 
			<< " height=\"" << imageinfo.height << "\"" 
			<< " xlink:href=\"" << imageinfo.FileName << "\"></image>" << endl;
    */

	Point ll, ur;
	imageinfo.getBoundingBox(ll, ur);
// Calculate BBox
	addtobbox(ll);
	addtobbox(ur);
	Point fig_ur(PntFig * ur.x_, y_offset - PntFig * ll.y_);
	Point fig_ll(PntFig * ll.x_, y_offset - PntFig * ur.y_);

	// first output link to an external *.eps file into *.fig file
	buffer << "# image\n";
	new_depth();
	buffer << "2 5 0 1 -1 -1 ";
	if (objectId)
		objectId--;				// don't let it get < 0
	buffer << objectId << " 0 -1 0.000 0 0 -1 0 0 5\n";
	buffer << "\t0 " << imageinfo.FileName << "\n";

	buffer << "\t" << (int) fig_ll.x_ << " " << (int) fig_ll.y_ << " "
		<< (int) fig_ur.x_ << " " << (int) fig_ll.y_ << " "
		<< (int) fig_ur.x_ << " " << (int) fig_ur.y_ << " "
		<< (int) fig_ll.x_ << " " << (int) fig_ur.y_ << " "
		<< (int) fig_ll.x_ << " " << (int) fig_ll.y_;
	buffer << "\n";



	} else {
	const size_t filenamelen = strlen(outBaseName.c_str()) + 21;
	char *EPSoutFileName = new char[filenamelen];
	const size_t fullfilenamelen = strlen(outDirName.c_str()) + strlen(outBaseName.c_str()) + 21;
	char *EPSoutFullFileName = new char[fullfilenamelen];

	sprintf_s(TARGETWITHLEN(EPSoutFileName,filenamelen), "%s%02d.eps", outBaseName.c_str(), imgcount++);
	sprintf_s(TARGETWITHLEN(EPSoutFullFileName,fullfilenamelen), "%s%s", outDirName.c_str(), EPSoutFileName);
	ofstream outi(EPSoutFullFileName);
	if (!outi) {
		errf << "Could not open file " << EPSoutFullFileName << " for output";
		exit(1);
	}
	// remember, we have to flip the image from PostScript coord to fig coords
	Point ll, ur;
	imageinfo.getBoundingBox(ll, ur);
// Calculate BBox
	addtobbox(ll);
	addtobbox(ur);
	Point fig_ur(PntFig * ur.x_, y_offset - PntFig * ll.y_);
	Point fig_ll(PntFig * ll.x_, y_offset - PntFig * ur.y_);

	// first output link to an external *.eps file into *.fig file
	buffer << "# image\n";
	new_depth();
	buffer << "2 5 0 1 -1 -1 ";
	if (objectId)
		objectId--;				// don't let it get < 0
	buffer << objectId << " 0 -1 0.000 0 0 -1 0 0 5\n";
	buffer << "\t0 " << EPSoutFileName << "\n";

	buffer << "\t" << (int) fig_ll.x_ << " " << (int) fig_ll.y_ << " "
		<< (int) fig_ur.x_ << " " << (int) fig_ll.y_ << " "
		<< (int) fig_ur.x_ << " " << (int) fig_ur.y_ << " "
		<< (int) fig_ll.x_ << " " << (int) fig_ur.y_ << " "
		<< (int) fig_ll.x_ << " " << (int) fig_ll.y_;
	buffer << "\n";



	imageinfo.writeEPSImage(outi);
	outi.close();

	delete[]EPSoutFullFileName;
	delete[]EPSoutFileName;
	}
}


static const char * const additionalDoku = 
"The xfig format driver supports special fontnames, which may be produced by using a fontmap file. "
"The following types of names are supported :BREAK"
"\n\\begin{verbatim}\n"
"General notation:\n"
"\"PostScript Font Name\" ((LaTeX|PostScript|empty)(::special)::)XFigFontName\n"
"\n"
"Examples:\n"
"\n"
"Helvetica LaTeX::SansSerif\n"
"Courier LaTeX::special::Typewriter\n"
"GillSans \"AvantGarde Demi\"\n"
"Albertus PostScript::special::\"New Century Schoolbook Italic\"\n"
"Symbol ::special::Symbol (same as PostScript::special::Symbol)\n"
"\\end{verbatim}\n"
"See also the file examplefigmap.fmp in the misc directory of the pstoedit source distribution for an example font map file for xfig. "
"Please note that the fontname has to be among those supported by xfig. "
"See - \\URL{http://www.xfig.org/userman/fig-format.html} for a list of legal font names";

static DriverDescriptionT < drvFIG > D_fig( "fig", ".fig format for xfig",  additionalDoku,"fig", false, true, true, true, DriverDescription::memoryeps,	// no support for PNG file images
										   DriverDescription::normalopen,
										   false, false /*clipping */ );

static DriverDescriptionT < drvFIG > D_xfig("xfig", ".fig format for xfig", "See fig format for more details.","fig", false, true, true, true, DriverDescription::memoryeps,	// no support for PNG file images
											DriverDescription::normalopen,
											false, false /*clipping */ );
static DriverDescriptionT < drvFIG > D_tfig("tfig", ".fig format for xfig", "Test only","fig", false, true, true, true, 
											DriverDescription::png,	
											DriverDescription::normalopen,
											false, false /*clipping */ );
