//Export.cpp, Copyright (c) 2002-2008 R.Lackner
//export graph files
//
//    This file is part of RLPlot.
//
//    RLPlot 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.
//
//    RLPlot 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 RLPlot; if not, write to the Free Software
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
#include "rlplot.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <fcntl.h>				//file open flags
#include <sys/stat.h>			//I/O flags
#ifdef USE_WIN_SECURE
	#include <share.h>			//I/O flags
#endif

#ifdef _WINDOWS
	#include <io.h>					//for read/write
#else
	#define O_BINARY 0x0
	#include <unistd.h>
#endif

extern char TmpTxt[];
extern Default defs;
static char *str_ind = "                              ";

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Entry point to export graph to Windows Meta File
void DoExportWmf(GraphObj *g, char *FileName, float res, DWORD flags)
{
	InfoBox("The export of Windos metafile (*.wmf)\n has been dicontinued.\n\n");
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// export to *.svg file (scalable vector graphic)
// this code is based on information from the following books
// H. Spona, 'Das Einsteigerseminar SVG-Webgrafiken mit XML',
//     vmi, ISBN 3-8266-7181-3
// M. Salathe, 'SVG Scalabe Vector Graphics ...f�r professionelle Einsteiger',
//     M&T, ISBN 3-8272-6188-0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class ExportSVG:public anyOutput {
public:
	HatchOut *hgo;

	ExportSVG(GraphObj *g, char *FileName, DWORD flags);
	~ExportSVG();
	bool SetLine(LineDEF *lDef);
	bool SetFill(FillDEF *fill);
	bool SetTextSpec(TextDEF *set);
	bool StartPage();
	bool EndPage();
	bool oCircle(int x1, int y1, int x2, int y2, char* nam = 0L);
	bool oPolyline(POINT * pts, int cp, char *nam = 0L);
	bool oRectangle(int x1, int y1, int x2, int y2, char *nam = 0L);
	bool oSolidLine(POINT *p);
	bool oTextOut(int x, int y, char *txt, int cb);
	bool oTextOutW(int x, int y, w_char *txt, int cb);
	bool oPolygon(POINT *pts, int cp, char * nam = 0L);

private:
	int iLineWidth, cb_out, out_pos, out_size, cb_ind;
	char *out_buff;
	bool bUseGroupLine, bOutputPending;
	GraphObj *go;
	char *name, output[120], tHatchStyle[80];
	DWORD flags;

	bool com_TextOut(int x, int y, char *txt, int cb);
	void Indent(bool ind);
	void AddToOutput(char *txt, int len);
	char *ColName(DWORD col);
	char *Transparency(DWORD col, int type);
};

ExportSVG::ExportSVG(GraphObj *g, char *FileName, DWORD flg)
{
	hgo =0L;
	DeskRect.left = DeskRect.top = 0;
	DeskRect.right = DeskRect.bottom = 0x4fffffff;
	dFillCol = 0xffffffffL;
	hres = vres = 1000.0f;
	go = g;
	if(FileName)name = (char*)memdup(FileName, (int)strlen(FileName)+1, 0);
	else name = 0L;
	out_buff = 0L;		out_pos = out_size = 0;
	flags = flg;		cb_ind = 3;
	rlp_strcpy(tHatchStyle, 80, "style=\"stroke:black; stroke-width:1\"");
	bUseGroupLine = false;
}

ExportSVG::~ExportSVG()
{
	if(hgo) delete hgo;
	if(name) free(name);
}

bool
ExportSVG::SetLine(LineDEF *lDef)
{
	LineWidth = lDef->width;
	if(1 >(iLineWidth  = iround(un2fix(lDef->width)))) iLineWidth = 1;
	dPattern = lDef->pattern;
	dLineCol = lDef->color;
	RLP.finc = (float)(256.0/un2fix(lDef->patlength*8.0));
	RLP.fp = 0.0;
	return true;
}

bool
ExportSVG::SetFill(FillDEF *fill)
{
	int iL; 

	if(!fill) return false;
	if((fill->type & 0xff) != FILL_NONE) {
		if(!hgo) hgo = new HatchOut(this);
		if(hgo) hgo->SetFill(fill);
		if(fill->hatch) {
			if(1 >(iL  = iround(un2fix(fill->hatch->width)))) iL = 1;
#ifdef USE_WIN_SECURE
			sprintf_s(tHatchStyle, 80, "style=\"fill:none; stroke:%s; stroke-width:%d\"",
				ColName(fill->hatch->color), iL);
#else
			sprintf(tHatchStyle, "style=\"fill:none; stroke:%s; stroke-width:%d\"",
				ColName(fill->hatch->color), iL);
#endif
			}
		}
	else {
		if(hgo) delete hgo;
		hgo = 0L;
		}
	dFillCol = fill->color;
	dFillCol2 = fill->color2;
	return true;
}

bool
ExportSVG::SetTextSpec(TextDEF *set)
{
	if(set->fSize > 0.0) {
		if((set->Style & TXS_SUPER) || (set->Style & TXS_SUB))
			set->iSize = un2iy(set->fSize * 0.71);
		else set->iSize = un2iy(set->fSize);
		}
	if(!set->iSize) return false;
	return anyOutput::SetTextSpec(set);
}

bool 
ExportSVG::StartPage()
{
	int w, h;

	if(!go) return false;
	w = un2ix(go->GetSize(SIZE_GRECT_RIGHT) - go->GetSize(SIZE_GRECT_LEFT))/10;
	h = un2iy(go->GetSize(SIZE_GRECT_BOTTOM) - go->GetSize(SIZE_GRECT_TOP))/10;
	w++; h++;
	if(flags & 0x01) add_to_buff(&out_buff, &out_pos, &out_size, "Content-Type: image/svg+xml\n\n", 0);
	VPorg.fy = -un2fiy(go->GetSize(SIZE_GRECT_TOP));
	VPorg.fx = -un2fix(go->GetSize(SIZE_GRECT_LEFT));
	add_to_buff(&out_buff, &out_pos, &out_size, "<?xml version=\"1.0\"?>\n"
		"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20001102//EN\"\n"
		"   \"http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd\">\n<svg ", 0);
	if(defs.svgAttr) add_to_buff(&out_buff, &out_pos, &out_size, defs.svgAttr, 0);
	add_to_buff(&out_buff, &out_pos, &out_size, " width=\"", 0);
	add_int_to_buff(&out_buff, &out_pos, &out_size, w, false, 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "\" height=\"", 0);
	add_int_to_buff(&out_buff, &out_pos, &out_size, h, false, 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "\" style=\"stroke-linecap:round; stroke-linejoin:round\">\n", 0);
	if(defs.svgScript) {
		add_to_buff(&out_buff, &out_pos, &out_size, "<defs>\n<script type=\"text/ecmascript\"><![CDATA[\n\n", 0);
		add_to_buff(&out_buff, &out_pos, &out_size, defs.svgScript, 0);
		add_to_buff(&out_buff, &out_pos, &out_size, "\n\n]]></script>\n</defs>\n\n", 0);
		}
	add_to_buff(&out_buff, &out_pos, &out_size, "<g transform=\"scale(0.1)\" style=\"font-family:Helvetica\">\n", 0);
	return true;
}

bool
ExportSVG::EndPage()
{
	FILE *oFile;

	add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n</svg>\n", 0);
	if(name && out_buff && out_pos > 20) {
#ifdef USE_WIN_SECURE
		fopen_s(&oFile, name, "w");
#else
		oFile = fopen(name, "w");
#endif
		if(!oFile) {
			ErrorBox("Could not open\noutput file!");
			return false;
			}
		fprintf(oFile, "%s", out_buff);
		free(out_buff);		fclose (oFile);
		}
	return true;
}

bool
ExportSVG::oCircle(int x1, int y1, int x2, int y2, char* nam)
{
	if(x1 > x2) Swap(x1, x2);
	if(y1 > y2) Swap(y1, y2);

	if(hgo){
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, "<g>  <!-- ", 0);
		add_to_buff(&out_buff, &out_pos, &out_size, (x2-x1) == (y2-y1) ? (char*)"circle" : (char*)"ellipse", 0);
		add_to_buff(&out_buff, &out_pos, &out_size, " with pattern -->\n", 0);
		Indent(true);
		}
	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
	add_to_buff(&out_buff, &out_pos, &out_size, (x2-x1) == (y2-y1) ? (char*)"<circle" : (char*)"<ellipse", 0);
	if(nam && nam[0]) {
		add_to_buff(&out_buff, &out_pos, &out_size, " name=\"", 0);
		add_to_buff(&out_buff, &out_pos, &out_size, nam, 0);
		add_to_buff(&out_buff, &out_pos, &out_size, "\"", 0);
		}
	add_to_buff(&out_buff, &out_pos, &out_size, " cx=\"", 0);
	add_int_to_buff(&out_buff, &out_pos, &out_size, (x1+x2)>>1, false, 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "\" cy=\"", 0);
	add_int_to_buff(&out_buff, &out_pos, &out_size, (y1+y2)>>1, false, 0);
	if((x2-x1)==(y2-y1)) {
		add_to_buff(&out_buff, &out_pos, &out_size, "\" r=\"", 0);
		add_int_to_buff(&out_buff, &out_pos, &out_size, (x2-x1)>>1, false, 0);
		}
	else {
		add_to_buff(&out_buff, &out_pos, &out_size, "\" rx=\"", 0);
		add_int_to_buff(&out_buff, &out_pos, &out_size, (x2-x1)>>1, false, 0);
		add_to_buff(&out_buff, &out_pos, &out_size, "\" ry=\"", 0);
		add_int_to_buff(&out_buff, &out_pos, &out_size, (y2-y1)>>1, false, 0);
		}
	add_to_buff(&out_buff, &out_pos, &out_size, "\" style=\"fill:", 0);
	add_to_buff(&out_buff, &out_pos, &out_size, ColName(dFillCol), 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "; stroke:", 0);
	add_to_buff(&out_buff, &out_pos, &out_size, ColName(dLineCol), 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-width:", 0);
	add_int_to_buff(&out_buff, &out_pos, &out_size, iLineWidth, false, 0);
	add_to_buff(&out_buff, &out_pos, &out_size, ";", 1);
	add_to_buff(&out_buff, &out_pos, &out_size, Transparency(dFillCol, 2), 0);
	add_to_buff(&out_buff, &out_pos, &out_size, Transparency(dLineCol, 1), 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "\"/>\n", 0);
	if(hgo) {
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, "<g ", 3);
		add_to_buff(&out_buff, &out_pos, &out_size, tHatchStyle, 0);
		add_to_buff(&out_buff, &out_pos, &out_size, ">  <!-- hatch -->\n", 0);
		Indent(true);		bUseGroupLine = true;
		hgo->oCircle(x1, y1, x2, y2);
		Indent(false);		bUseGroupLine = false;
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 0);
		Indent(false);
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 0);
		}
	return true;
}

bool
ExportSVG::oPolyline(POINT *pts, int cp, char *nam)
{
	int i, cb;
	char tmptxt[120];

	if(cp < 2) return false;
	if (dPattern){
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, "<g style=\"stroke:", 0);
		add_to_buff(&out_buff, &out_pos, &out_size, ColName(dLineCol), 0);
		add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-width:", 0);
		add_int_to_buff(&out_buff, &out_pos, &out_size, iLineWidth, false, 0);
		add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-linecap:round;", 0);
		add_to_buff(&out_buff, &out_pos, &out_size, Transparency(dLineCol, 1), 0);
		add_to_buff(&out_buff, &out_pos, &out_size, "\"><!-- pattern line -->\n", 0);
		Indent(true);			bUseGroupLine = true;
		for (i = 1; i < cp; i++) PatLine(pts[i-1], pts[i]);
		Indent(false);
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 0);
		bUseGroupLine = false;
		}
	else {
		if(cp == 2) return oSolidLine(pts);
		bOutputPending = false;
		cb_out = rlp_strcpy(output, 20,"<polyline points=\""); 
		for(i = 0; i < cp; i++) {
#ifdef USE_WIN_SECURE
			cb = sprintf_s(tmptxt, 120, "%d %d ", pts[i].x, pts[i].y);
#else
			cb = sprintf(tmptxt, "%d %d ", pts[i].x, pts[i].y);
#endif
			AddToOutput(tmptxt, cb);
			}
		if(cb_out) output[cb_out-1] = '"';
		if(!bUseGroupLine) {
			cb = rlp_strcpy(tmptxt, 120, " style = \"fill:none; ");
			AddToOutput(tmptxt, cb);
			cb = rlp_strcpy(tmptxt, 120, "stroke:");	//bug fixed by vefremov
			cb += rlp_strcpy(tmptxt+cb, 120-cb, ColName(dLineCol));
			cb += rlp_strcpy(tmptxt+cb, 120-cb, "; ");
			AddToOutput(tmptxt, cb);
#ifdef USE_WIN_SECURE
			cb = sprintf_s(tmptxt, 120, "stroke-width:%d;",iLineWidth);
#else
			cb = sprintf(tmptxt, "stroke-width:%d;",iLineWidth);
#endif
			AddToOutput(tmptxt, cb);
			if(cb = rlp_strcpy(tmptxt, 40, Transparency(dLineCol, 1))) AddToOutput(tmptxt, cb);
			AddToOutput("\"/>", 3); 
			}
		else AddToOutput("/>", 2);
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
		add_to_buff(&out_buff, &out_pos, &out_size, "\n", 1);
		if(bOutputPending)Indent(false);
		}
	return true;
}

bool
ExportSVG::oRectangle(int x1, int y1, int x2, int y2, char *nam)
{
	if(x1 > x2) Swap(x1, x2);
	if(y1 > y2) Swap(y1, y2);
	if(hgo){
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, "<g>  <!-- rectangle with pattern -->\n", 0);
		Indent(true);
		}
	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
	add_to_buff(&out_buff, &out_pos, &out_size, "<rect", 5);
	if(nam && nam[0]) {
		add_to_buff(&out_buff, &out_pos, &out_size, " name=\"", 7);
		add_to_buff(&out_buff, &out_pos, &out_size, nam, 0);
		add_to_buff(&out_buff, &out_pos, &out_size, "\"", 1);
		}
	add_to_buff(&out_buff, &out_pos, &out_size, " x=\"", 4);
	add_int_to_buff(&out_buff, &out_pos, &out_size, x1, false, 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "\" y=\"", 5);
	add_int_to_buff(&out_buff, &out_pos, &out_size, y1, false, 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "\" width=\"", 9);
	add_int_to_buff(&out_buff, &out_pos, &out_size, x2-x1-1, false, 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "\" height=\"", 10);
	add_int_to_buff(&out_buff, &out_pos, &out_size, y2-y1-1, false, 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "\" style=\"fill:", 0);
	add_to_buff(&out_buff, &out_pos, &out_size, ColName(dFillCol), 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "; stroke:", 0);
	add_to_buff(&out_buff, &out_pos, &out_size, ColName(dLineCol), 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-width:", 0);
	add_int_to_buff(&out_buff, &out_pos, &out_size, iLineWidth, false, 0);
	add_to_buff(&out_buff, &out_pos, &out_size, ";", 1);
	add_to_buff(&out_buff, &out_pos, &out_size, Transparency(dFillCol, 2), 0);
	add_to_buff(&out_buff, &out_pos, &out_size, Transparency(dLineCol, 1), 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "\"/>\n", 0);
	if(hgo) {
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, "<g ", 3);
		add_to_buff(&out_buff, &out_pos, &out_size, tHatchStyle, 0);
		add_to_buff(&out_buff, &out_pos, &out_size, ">  <!-- hatch -->\n", 0);
		Indent(true);				bUseGroupLine = true;
		hgo->oRectangle(x1, y1, x2, y2, 0L);
		Indent(false);				bUseGroupLine = false;
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 5);
		Indent(false);
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 5);
		}
	return true;
}

bool
ExportSVG::oSolidLine(POINT *p)
{
	if(!p) return false;
	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
	add_to_buff(&out_buff, &out_pos, &out_size, "<line x1=\"", 0);
	add_int_to_buff(&out_buff, &out_pos, &out_size, p[0].x, false, 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "\" y1=\"", 6);
	add_int_to_buff(&out_buff, &out_pos, &out_size, p[0].y, false, 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "\" x2=\"", 6);
	add_int_to_buff(&out_buff, &out_pos, &out_size, p[1].x, false, 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "\" y2=\"", 6);
	add_int_to_buff(&out_buff, &out_pos, &out_size, p[1].y, false, 0);
	if(!bUseGroupLine) {
		add_to_buff(&out_buff, &out_pos, &out_size, "\" style=\"stroke:", 0);
		add_to_buff(&out_buff, &out_pos, &out_size, ColName(dLineCol), 0);
		add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-width:", 0);
		add_int_to_buff(&out_buff, &out_pos, &out_size, iLineWidth, false, 0);
		add_to_buff(&out_buff, &out_pos, &out_size, ";", 1);
		add_to_buff(&out_buff, &out_pos, &out_size, Transparency(dLineCol, 1), 0);
		}
	add_to_buff(&out_buff, &out_pos, &out_size, "\"/>\n", 4);
	return true;
}

bool
ExportSVG::com_TextOut(int x, int y, char *txt, int cb)
{
	int c, h, ix, iy, dy;
	char tmptxt[120];

	if(!txt || !txt[0]) return false;
	else h = TxtSet.iSize;
	if(TxtSet.Align & TXA_VCENTER) iy = y + h/3;
	else if(TxtSet.Align & TXA_VBOTTOM) iy = y;
	else iy = y + iround(h * 0.8);
	ix = x;		dy = 0;
	if(TxtSet.Style & TXS_SUB) {
		if((TxtSet.Align & TXA_VCENTER) == TXA_VCENTER) dy = un2iy(TxtSet.fSize*0.4);
		else if((TxtSet.Align & TXA_VBOTTOM) == TXA_VBOTTOM) dy = un2iy(TxtSet.fSize*0.2);
		else if((TxtSet.Align & TXA_VTOP) == TXA_VTOP) dy = un2iy(TxtSet.fSize*.6);
		}
	else if(TxtSet.Style & TXS_SUPER) {
		if((TxtSet.Align & TXA_VCENTER) == TXA_VCENTER) dy = -un2iy(TxtSet.fSize*0.4);
		else if((TxtSet.Align & TXA_VBOTTOM) == TXA_VBOTTOM) dy = -un2iy(TxtSet.fSize*0.6);
		else if((TxtSet.Align & TXA_VTOP) == TXA_VTOP) dy = -un2iy(TxtSet.fSize*0.2);
		}
#ifdef USE_WIN_SECURE
	cb_out = sprintf_s(output, 120, "<text x=\"%d\" y=\"%d\" dy=\"%d\" ", ix, iy, dy);
#else
	cb_out = sprintf(output, "<text x=\"%d\" y=\"%d\" dy=\"%d\" ", ix, iy, dy);
#endif
	if(fabs(TxtSet.RotBL) >.01 || fabs(TxtSet.RotCHAR) >.01) {
#ifdef USE_WIN_SECURE
		cb_out += sprintf_s(output+cb_out, 120-cb_out, "transform=\"rotate(%.0f,%d,%d)\" ", -TxtSet.RotBL, ix, iy);
#else
		cb_out += sprintf(output+cb_out,"transform=\"rotate(%.0f,%d,%d)\" ", -TxtSet.RotBL, ix, iy);
#endif
		}
	c = rlp_strcpy(tmptxt, 140, "style=\"font-family:");
	switch(TxtSet.Font) {
	case FONT_TIMES:	c += rlp_strcpy(tmptxt+c, 120-c, "Times;");		break;
	case FONT_COURIER:	c += rlp_strcpy(tmptxt+c, 120-c, "Courier;");	break;
	default:			c += rlp_strcpy(tmptxt+c, 120-c, "Helvetica;");	break;
		}
	if(TxtSet.Style & TXS_ITALIC) c += rlp_strcpy(tmptxt+c, 120-c, " font-style:italic;");
	if(TxtSet.Style & TXS_BOLD) c += rlp_strcpy(tmptxt+c, 120-c, " font-weight:bold;");
	if(TxtSet.Style & TXS_UNDERLINE) c += rlp_strcpy(tmptxt+c, 120-c, " text-decoration:underline;");
	AddToOutput(tmptxt, c);
#ifdef USE_WIN_SECURE
	c = sprintf_s(tmptxt, 120, " fill:%s; stroke:%s;%s ", ColName(TxtSet.ColTxt), ColName(TxtSet.ColTxt),Transparency(TxtSet.ColTxt, 0));
#else
	c = sprintf(tmptxt, " fill:%s; stroke:%s;%s ", ColName(TxtSet.ColTxt), ColName(TxtSet.ColTxt),Transparency(TxtSet.ColTxt, 0));
#endif
	AddToOutput(tmptxt, c);
#ifdef USE_WIN_SECURE
	c = sprintf_s(tmptxt, 120, "font-size:%d; text-anchor:%s \">", h, 
		(TxtSet.Align & TXA_HRIGHT) ? "end" : (TxtSet.Align & TXA_HCENTER) ? "middle":"start");
#else
	c = sprintf(tmptxt, "font-size:%d; text-anchor:%s \">", h, 
		(TxtSet.Align & TXA_HRIGHT) ? "end" : (TxtSet.Align & TXA_HCENTER) ? "middle":"start");
#endif
	AddToOutput(tmptxt, c);
	if((cb_ind+strlen(txt)+cb_out) <110) cb_out += rlp_strcpy(output+cb_out, 120-cb_out, txt);
	else {
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
		add_to_buff(&out_buff, &out_pos, &out_size, "\n", 1);
		cb_out=rlp_strcpy(output, 120, txt);
		}
	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
	add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
	if((cb_ind + cb_out) <104) {
		add_to_buff(&out_buff, &out_pos, &out_size, "</text>\n", 0);
		}
	else add_to_buff(&out_buff, &out_pos, &out_size, "\n</text>\n", 0);
	return true;
}

bool
ExportSVG::oTextOut(int x, int y, char *txt, int cb)
{
	char *nt;

	if(!txt || !txt[0]) return false;
	nt = str2xml(txt, TxtSet.Font == FONT_GREEK);
	return com_TextOut(x, y, nt, cb);
}

bool
ExportSVG::oTextOutW(int x, int y, w_char *txt, int cb)
{
	int i, j;
	wchar_t wc;
	char c;

	for(i = j = 0; txt[i]; i++) {
		switch(txt[i]) {
			case '"':
				j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, "&quot;");
				break;
			case '&':
				j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, "&amp;");
				break;
			case '<':
				j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, "&lt;");
				break;
			case '>':
				j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, "&gt;");
				break;
			default:
				if(txt[i] > 255) {
#ifdef USE_WIN_SECURE
					j += sprintf_s(TmpTxt+j, TMP_TXT_SIZE-j, "&#%d;", txt[i]);
#else
					j += sprintf(TmpTxt+j, "&#%d;", txt[i]);
#endif
					}
				else if(txt[i] > 127) {
					c = (char)txt[i];
					if(mbtowc(&wc, &c, 1) >0) 
#ifdef USE_WIN_SECURE
						j += sprintf_s(TmpTxt+j, TMP_TXT_SIZE-j, "&#%d;", ((unsigned short)wc));
#else
						j += sprintf(TmpTxt+j, "&#%d;", ((unsigned short)wc));
#endif
					}
				else TmpTxt[j++] = (char)txt[i];
				break;
			}
		}
	TmpTxt[j++] = 0;
	return com_TextOut(x, y, TmpTxt, j-1);
}

bool
ExportSVG::oPolygon(POINT *pts, int cp, char *nam)
{
	int i, cb;
	char tmptxt[40];

	if(cp <3) return false;
	if(hgo){
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, "<g>  <!-- polygon with pattern -->\n", 0);
		Indent(true);
		}
	bOutputPending = false;
#ifdef USE_WIN_SECURE
	cb_out = sprintf_s(output, 120, "<polygon%s%s%s points=\"",
		nam? " name=\"" : "", nam ? nam : "", nam ? "\"" : ""); 
#else
	cb_out = sprintf(output, "<polygon%s%s%s points=\"",
		nam? " name=\"" : "", nam ? nam : "", nam ? "\"" : ""); 
#endif
	for(i = 0; i < cp; i++) {
#ifdef USE_WIN_SECURE
		cb = sprintf_s(tmptxt, 40, "%d %d ", pts[i].x, pts[i].y);
#else
		cb = sprintf(tmptxt, "%d %d ", pts[i].x, pts[i].y);
#endif
		AddToOutput(tmptxt, cb);
		}
	if(cb_out) output[cb_out-1] = '"';
#ifdef USE_WIN_SECURE
	cb = sprintf_s(tmptxt, 40, " style=\"fill:%s; ", ColName(dFillCol));
	AddToOutput(tmptxt, cb);
	cb = sprintf_s(tmptxt, 40, "stroke:%s; ", ColName(dLineCol));
	AddToOutput(tmptxt, cb);
	cb = sprintf_s(tmptxt, 40, "stroke-width:%d;",iLineWidth);
	AddToOutput(tmptxt, cb);
#else
	cb = sprintf(tmptxt, " style=\"fill:%s; ", ColName(dFillCol));
	AddToOutput(tmptxt, cb);
	cb = sprintf(tmptxt, "stroke:%s; ", ColName(dLineCol));
	AddToOutput(tmptxt, cb);
	cb = sprintf(tmptxt, "stroke-width:%d;", iLineWidth);
	AddToOutput(tmptxt, cb);
#endif
	if(cb = rlp_strcpy(tmptxt, 40, Transparency(dFillCol, 2))) AddToOutput(tmptxt, cb);
	if(cb = rlp_strcpy(tmptxt, 40, Transparency(dLineCol, 1))) AddToOutput(tmptxt, cb);
	AddToOutput("\"/>", 3); 
	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
	add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
	add_to_buff(&out_buff, &out_pos, &out_size, "\n", 1);
	if(bOutputPending)Indent(false);
	if(hgo) {
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, "<g ", 3);
		add_to_buff(&out_buff, &out_pos, &out_size, tHatchStyle, 0);
		add_to_buff(&out_buff, &out_pos, &out_size, ">  <!-- hatch -->\n", 0);
		Indent(true);				bUseGroupLine = true;
		hgo->oPolygon(pts, cp);		Indent(false);
		bUseGroupLine = false;
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 5);
		Indent(false);
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 5);
		}
	return true;
}

void
ExportSVG::Indent(bool ind)
{
	if(ind) {
		if(cb_ind < 20) cb_ind += 3;
		}
	else {
		if(cb_ind > 5) cb_ind -= 3;
		}
}

void
ExportSVG::AddToOutput(char *txt, int len)
{
	if(!txt || !txt[0]) return;
	if(!len) len = (int)strlen(txt);
	if((len + cb_out + cb_ind) < 110){
		cb_out += rlp_strcpy(output+cb_out, 120, txt);
		}
	else {
		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
		add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
		add_to_buff(&out_buff, &out_pos, &out_size, "\n", 1);
		if(!bOutputPending) Indent(true);
		bOutputPending = true;
		cb_out = rlp_strcpy(output, 120, txt);
		}
}

char *
ExportSVG::ColName(DWORD col)
{
	static char txt1[20], txt2[20];
	static int sw;
	char *ret;

	switch(col) {
	case 0x00000000:			return "black";
	case 0x000000ff:			return "red";
	case 0x0000ff00:			return "lime";
	case 0x0000ffff:			return "yellow";
	case 0x00ff0000:			return "blue";
	case 0x00ff00ff:			return "magenta";
	case 0x00ffff00:			return "cyan";
	case 0x00ffffff:			return "white";
		}
	sw = (sw+1) & 0x0f;
#ifdef USE_WIN_SECURE
	sprintf_s(ret = (sw & 0x01 ? txt1 : txt2), 20, "rgb(%d,%d,%d)", col & 0xff, (col>>8) & 0xff, (col>>16) &0xff);
#else
	sprintf(ret = (sw & 0x01 ? txt1 : txt2), "rgb(%d,%d,%d)", col & 0xff, (col>>8) & 0xff, (col>>16) &0xff);
#endif
	return ret;
}

char *
ExportSVG::Transparency(DWORD col, int type)
{
	static char txt1[30], txt2[30];
	static int sw;
	int cb;
	char *ret;
	double a;

	if((col & 0xfe000000) == 0) return "";
	a = ((double)(255-(col>>24)))/256.0;
	if(a > 0.99) return "";
	sw = (sw+1) & 0x0f;
	ret = sw & 0x01 ? txt1 : txt2;
	switch(type) {
	case 1:
		cb = rlp_strcpy(ret, 30, " stroke-opacity:");	break;
	case 2:	
		cb = rlp_strcpy(ret, 30, " fill-opacity:");	break;
	default:
		cb = rlp_strcpy(ret, 30, " opacity:");		break;
		}
#ifdef USE_WIN_SECURE
	sprintf_s(ret+cb, 30-cb, "%0.3lf;", a);
#else
	sprintf(ret+cb, "%0.3lf;", a);
#endif
	return ret;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Entry point to export graph to *.svg
void DoExportSvg(GraphObj *g, char *FileName, DWORD flags)
{
	ExportSVG *ex;
	
	ex = new ExportSVG(g, FileName, flags);
	if(ex->StartPage()) {
		g->DoPlot(ex);
		ex->EndPage();
		}
	HideTextCursor();	
	delete(ex);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// export to *.eps file (encapsulated post script).
// This code is based on information from the following book
// G. Born, 'Referenzhandbuch Dateiformate', 
//     Addison-Wesley ISBN 3-89319-815-6
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class ExportEPS:public anyOutput {
public:
	HatchOut *hgo;

	ExportEPS(GraphObj *g, char *FileName, DWORD flags);
	~ExportEPS();
	bool SetLine(LineDEF *lDef);
	bool SetFill(FillDEF *fill);
	bool SetTextSpec(TextDEF *set);
	bool StartPage();
	bool EndPage();
	bool oCircle(int x1, int y1, int x2, int y2, char* nam = 0L);
	bool oPolyline(POINT * pts, int cp, char *nam = 0L);
	bool oRectangle(int x1, int y1, int x2, int y2, char *nam = 0L);
	bool oSolidLine(POINT *p);
	bool oTextOut(int x, int y, char *txt, int cb);
	bool oPolygon(POINT *pts, int cp, char *nam = 0L);

private:
	RECT BoundBox;
	fRECT SolLines[2000];
	int nSolLines;
	GraphObj *go;
	char *name, FontName[30];
	FILE *oFile;
	DWORD CurrCol;
	bool bFontChange;

	float ix2eps(int x);
	float iy2eps(int y);
	char *col2eps(DWORD color);
	void FlushSolLines();
	void AddSolLine(float x1, float y1, float x2, float y2);
};

ExportEPS::ExportEPS(GraphObj *g, char *FileName, DWORD flags)
{
	hgo =0L;		nSolLines = 0;			DeskRect.left = DeskRect.top = 0;
	DeskRect.right = DeskRect.bottom = 0x4fffffff;
	dFillCol = 0xffffffffL;					hres = vres = 720.0f;
	go = g;			bFontChange = false;	oFile = 0L;
	if(FileName)name = (char*)memdup(FileName, (int)strlen(FileName)+1,0);
	else name = 0L;
}

ExportEPS::~ExportEPS()
{
	if(hgo) delete hgo;
	if(name) free(name);
}

bool
ExportEPS::SetLine(LineDEF *lDef)
{
	if(LineWidth != lDef->width || dLineCol != lDef->color) {
		FlushSolLines();
		LineWidth = lDef->width;
		CurrCol = dLineCol = lDef->color;
		fprintf(oFile, "\nnewpath %.1f setlinewidth %s ",
			un2fix(LineWidth)/10.0f, col2eps(dLineCol));
		}
	dPattern = lDef->pattern;
	RLP.finc = (float)(256.0/un2fix(lDef->patlength*8.0));
	RLP.fp = 0.0;
	return true;
}

bool
ExportEPS::SetFill(FillDEF *fill)
{
	if(!fill) return false;
	if((fill->type & 0xff) != FILL_NONE) {
		if(!hgo) hgo = new HatchOut(this);		if(hgo) hgo->SetFill(fill);
		}
	else {
		if(hgo) delete hgo;						hgo = 0L;
		}
	dFillCol = fill->color;						dFillCol2 = fill->color2;
	return true;
}

bool
ExportEPS::SetTextSpec(TextDEF *set)
{
	int cb;

	if(set->fSize > 0.0) {
		if((set->Style & TXS_SUPER) || (set->Style & TXS_SUB)) set->iSize = un2iy(set->fSize * 0.71);
		else set->iSize = un2iy(set->fSize);
		}
	if(!set->iSize) return false;
	anyOutput::SetTextSpec(set);
	switch(TxtSet.Font) {
	case FONT_TIMES:	cb = rlp_strcpy(FontName, 30, "(Times");		break;		//Serif
	case FONT_COURIER:	cb = rlp_strcpy(FontName, 30, "(Courier");		break;		//fixed spaced
	default:			cb = rlp_strcpy(FontName, 30, "(Helvetica");	break;		//Sans Serif	
		}
	if(TxtSet.Style & TXS_BOLD) cb += rlp_strcpy(FontName+cb, 30-cb, "-Bold");
	if(TxtSet.Style & TXS_ITALIC) cb += rlp_strcpy(FontName+cb, 30-cb, "-Italic");
	cb += rlp_strcpy(FontName+cb, 30-cb, ")");		bFontChange = true;
	return true;
}

bool 
ExportEPS::StartPage()
{
	time_t ti;
	
	if(!go) return false;
	ti = time(0L);
	BoundBox.top = BoundBox.left = 0;
	BoundBox.right = un2ix(go->GetSize(SIZE_GRECT_RIGHT) - go->GetSize(SIZE_GRECT_LEFT))/10;
	BoundBox.bottom = un2iy(go->GetSize(SIZE_GRECT_BOTTOM) - go->GetSize(SIZE_GRECT_TOP))/10;
	BoundBox.right++;	BoundBox.bottom++;
	if(name) {
#ifdef USE_WIN_SECURE
		fopen_s(&oFile, name, "w");
#else
		oFile = fopen(name, "w");
#endif
		if(!oFile) {
			ErrorBox("Could not open\noutput file!");
			return false;
			}
		}
	else oFile = stdout;
	VPorg.fy = -un2fiy(go->GetSize(SIZE_GRECT_TOP));
	VPorg.fx = -un2fix(go->GetSize(SIZE_GRECT_LEFT));
	fprintf(oFile, "%%!PS-Adobe-1.0 EPSF-3.0\n"
		"%%%%BoundingBox: %d %d %d %d\n", BoundBox.left, BoundBox.top,
		BoundBox.right, BoundBox.bottom);
	fprintf(oFile, "%%%%Title: %s\n", name);
	fprintf(oFile,"%%%%Creator: RLPlot version "SZ_VERSION"\n");
#ifdef USE_WIN_SECURE
	ctime_s(TmpTxt, 50, &ti);	TmpTxt[24] = 0;
	fprintf(oFile,"%%%%CreationDate: %s", TmpTxt);
#else
	fprintf(oFile,"%%%%CreationDate: %s", ctime(&ti));
#endif
	fprintf(oFile, "%%%%Pages: 1\n%%%%DocumentFonts: (atend)\n");
	fprintf(oFile, "%%%%EndComments\n"
		"%%%%BeginProlog\n"
		"%%%%EndProlog\n"
		"%%%%Page: 1 1");
	return true;
}

bool
ExportEPS::EndPage()
{
	fprintf(oFile, "\nshowpage\n%%%%Trailer\n");
	fprintf(oFile, "%%%%DocumentFonts: Helvetica\n");
	fprintf(oFile, "%%%%EOF\n");
	fclose (oFile);
	return true;
}

bool
ExportEPS::oCircle(int x1, int y1, int x2, int y2, char* nam)
{
	FlushSolLines();
	if((x2 - x1) == (y2 -y1)) {
		fprintf(oFile, "\nnewpath %.1f %.1f %.1f 0 360 arc ", ix2eps((x1+x2)/2), 
			iy2eps((y1+y2)/2), (float)(x2-x1)/20.0f);
		fprintf(oFile, "%s fill", col2eps(CurrCol = dFillCol));
		if(hgo){
			hgo->oCircle(x1, y1, x2, y2);
			FlushSolLines();
			}
		fprintf(oFile, "\nnewpath %.1f %.1f %.1f 0 360 arc ", ix2eps((x1+x2)/2), 
			iy2eps((y1+y2)/2), (float)(x2-x1)/20.0f);
		fprintf(oFile, "%s stroke", col2eps(CurrCol = dLineCol));
		}
	else if(x1 != x2 && y1 != y2){
		fprintf(oFile, "\ngsave %.1f %.1f translate", (ix2eps((x1+x2)>>1)),
			(iy2eps((y1+y2)>>1)));
		if(abs(x2-x1) > abs(y2-y1)) {
			fprintf(oFile, " 1 %lf scale", fabs(((double)(y2-y1))/((double)(x2-x1))));
			fprintf(oFile, " 0 0 %.1lf 0 360 arc ", fabs(((double)(x2-x1))/20.0));
			fprintf(oFile, "%s fill", col2eps(CurrCol = dFillCol));
			fprintf(oFile, "\n 0 0 %.1lf 0 360 arc ", fabs(((double)(x2-x1))/20.0));
			}
		else {
			fprintf(oFile, " %lf 1 scale", fabs(((double)(x2-x1))/((double)(y2-y1))));
			fprintf(oFile, " 0 0 %.1lf 0 360 arc ", fabs(((double)(y2-y1))/20.0));
			fprintf(oFile, "%s fill", col2eps(CurrCol = dFillCol));
			fprintf(oFile, "\n 0 0 %.1lf 0 360 arc ", fabs(((double)(y2-y1))/20.0));
			}
		fprintf(oFile, "%s stroke grestore", col2eps(CurrCol = dLineCol));
		if(hgo){
			hgo->oCircle(x1, y1, x2, y2);
			FlushSolLines();
			}
		}
	return true;
}

bool
ExportEPS::oPolyline(POINT * pts, int cp, char *nam)
{
	int i, j;

	if(cp <1) return false;
	if (dPattern){
		for (i = 1; i < cp; i++) PatLine(pts[i-1], pts[i]);
		return true;
		}
	else if(cp == 2) return oSolidLine(pts);
	FlushSolLines();
	if(CurrCol != dLineCol) {
		fprintf(oFile, "\n%s ", col2eps(CurrCol = dLineCol));
		}
	for(i = cp-1, j = 0; i >= 0; i--, j++) {
		if(!(j & 0x07)) fprintf(oFile, "\n");
		fprintf(oFile, " %.1f %.1f", ix2eps(pts[i].x), iy2eps(pts[i].y));
		}
	fprintf(oFile, " moveto %d {lineto} repeat stroke ", cp-1);
	return true;
}


bool
ExportEPS::oRectangle(int x1, int y1, int x2, int y2, char *nam)
{
	FlushSolLines();
	fprintf(oFile, "\n%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f moveto 4 {lineto}"
		" repeat %s fill", ix2eps(x1), iy2eps(y1), ix2eps(x1), iy2eps(y2), ix2eps(x2), iy2eps(y2), 
		ix2eps(x2), iy2eps(y1), ix2eps(x1), iy2eps(y1), col2eps(CurrCol = dFillCol));
	if(hgo) hgo->oRectangle(x1, y1, x2, y2, 0L);
	fprintf(oFile, "\n%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f moveto 4 {lineto}"
		" repeat %s stroke", ix2eps(x1), iy2eps(y1), ix2eps(x1), iy2eps(y2), ix2eps(x2), iy2eps(y2), 
		ix2eps(x2), iy2eps(y1), ix2eps(x1), iy2eps(y1), col2eps(CurrCol = dLineCol));
	return true;
}

bool
ExportEPS::oSolidLine(POINT *p)
{
	if(CurrCol != dLineCol) {
		FlushSolLines();
		fprintf(oFile, "\n%s ", col2eps(CurrCol = dLineCol));
		}
	AddSolLine(ix2eps(p[0].x), iy2eps(p[0].y), ix2eps(p[1].x), iy2eps(p[1].y));
	return true;
}

bool
ExportEPS::oTextOut(int x, int y, char *txt, int cb)
{
	int h, ix, iy, w;
	float fx, fy, lw;

	FlushSolLines();	if(!txt || !txt[0]) return true;
	oGetTextExtent(txt, cb, &w, &h);
	if(bFontChange)	{
		fprintf(oFile, "\n%s findfont %d scalefont setfont ", FontName, TxtSet.iSize/10);
		bFontChange = false;
		}
	if(TxtSet.Align & TXA_VCENTER) iy = y + h/3;
	else if(TxtSet.Align & TXA_VBOTTOM) iy = y;
	else iy = y + iround(h*.8);
	if(TxtSet.Align & TXA_HRIGHT) ix = x-w;
	else if(TxtSet.Align & TXA_HCENTER) ix = x-w/2;
	else ix = x;			lw = (float)(iy-y)/150.0f;
	fprintf(oFile,"\n");
	if(fabs(TxtSet.RotBL) >.01 || fabs(TxtSet.RotCHAR) >.01) {
		if(TxtSet.Style & TXS_SUB) iy += un2iy(TxtSet.fSize*0.6);
		else if(TxtSet.Style & TXS_SUPER) iy -= un2iy(TxtSet.fSize*.2);
		fprintf(oFile, "gsave %.1f %.1f translate %f rotate %.1f %.1f moveto\n",
			ix2eps(x), iy2eps(y), TxtSet.RotBL, (float)(ix-x)/10.0f, (float)(iy-y)/-10.0f);
		fprintf(oFile, "%s ", col2eps(CurrCol = TxtSet.ColTxt));
		fprintf(oFile, "(%s) show ", txt);
		if(TxtSet.Style & TXS_UNDERLINE) {
			fprintf(oFile, "\ncurrentpoint %.1f exch pop moveto", (float)(iy-y)/-10.0f - lw*1.2);
			fprintf(oFile, " 0 %.1f lineto %s %.1f setlinewidth stroke ", (float)(iy-y)/-10.0f -lw*1.2,
				col2eps(TxtSet.ColTxt), lw);
			}
		fprintf(oFile, "grestore\n");
		}
	else {
		if(TxtSet.Style & TXS_SUB) iy += un2iy(TxtSet.fSize*0.6);
		else if(TxtSet.Style & TXS_SUPER) iy -= un2iy(TxtSet.fSize*.2);
		fx = ix2eps(ix);			fy = iy2eps(iy);
		fprintf(oFile, "%s ", col2eps(CurrCol = TxtSet.ColTxt));
		fprintf(oFile,"%.1f %.1f moveto (%s) show ", fx, fy, txt);
		if(TxtSet.Style & TXS_UNDERLINE) {
			fprintf(oFile, "\ncurrentpoint %.1f exch pop moveto", fy - lw*1.2);
			fprintf(oFile, " %.1f %.1f lineto %s %.1f setlinewidth stroke\n", fx, fy - lw*1.2, 
				col2eps(TxtSet.ColTxt), lw);
			}
		}
	return true;
}

bool
ExportEPS::oPolygon(POINT *pts, int cp, char *nam)
{
	int i, j;

	if(cp <1) return false;
	if(cp == 2) return oSolidLine(pts);
	FlushSolLines();
	for(i = cp-1, j = 0; i >= 0; i--, j++) {
		if(!(j & 0x07)) fprintf(oFile, "\n");
		fprintf(oFile, " %.1f %.1f", ix2eps(pts[i].x), iy2eps(pts[i].y));
		}
	fprintf(oFile, " moveto %d {lineto} repeat %s fill ", cp-1, col2eps(CurrCol = dFillCol));
	if(hgo) hgo->oPolygon(pts, cp);
	return oPolyline(pts, cp);
}

float
ExportEPS::ix2eps(int x)
{
	return (float)x/10.0f;
}

float
ExportEPS::iy2eps(int y)
{
	return (float)y/-10.0f + (float)BoundBox.bottom;
}

char *
ExportEPS::col2eps(DWORD color)
{
	static char txt[50];
	float r, g, b;

	r = (float)(color & 0xff)/255.0f;
	g = (float)((color>>8)&0xff)/255.0f;
	b = (float)((color>>16)&0xff)/255.0f;
#ifdef USE_WIN_SECURE
	sprintf_s(txt, 50, "%g %g %g setrgbcolor", r, g, b);
#else
	sprintf(txt, "%g %g %g setrgbcolor", r, g, b);
#endif
	return txt;
}

void
ExportEPS::FlushSolLines()
{
	int i, j;
	
	if(nSolLines <1) {
		nSolLines = 0;
		return;
		}
	if(nSolLines == 1) {
		fprintf(oFile, "\n%.1f %.1f moveto %.1f %.1f lineto stroke ", 
			SolLines[0].Ymin, SolLines[0].Ymax, SolLines[0].Xmin, SolLines[0].Xmax);
		nSolLines = 0;
		return;
		}
	for(i = nSolLines-1, j = 0; i >=0; i--, j++) {
		if(!(j & 0x03)) fprintf(oFile, "\n");
		fprintf(oFile, " %.1f %.1f %.1f %.1f", SolLines[i].Xmin, SolLines[i].Xmax,
			SolLines[i].Ymin, SolLines[i].Ymax);
		}
	if(j > 8 && ((j & 0x3) >=2 || (j & 0x03) == 0)) fprintf(oFile, "\n");
	fprintf(oFile, " %d {moveto lineto} repeat stroke ", nSolLines);
	nSolLines = 0;
}

void
ExportEPS::AddSolLine(float x1, float y1, float x2, float y2)
{
	if(nSolLines >= 2000) FlushSolLines();
	SolLines[nSolLines].Ymin = x1;	SolLines[nSolLines].Ymax = y1;
	SolLines[nSolLines].Xmin = x2;	SolLines[nSolLines].Xmax = y2;
	nSolLines++;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Entry point to export graph to *.eps
void DoExportEps(GraphObj *g, char *FileName, DWORD flags)
{
	ExportEPS *ex;

	ex = new ExportEPS(g, FileName, flags);
	if(ex->StartPage()) {
		g->DoPlot(ex);
		ex->EndPage();
		}
	delete(ex);
}
