/*
   drvwmf.cpp : This file is part of pstoedit
	 Backend for Windows Meta File (WMF/EMF)

   Copyright (C) 1996,1997 Jens Weber, rz47b7_AT_PostAG.DE
   Copyright (C) 1998 Thorsten Behrens and Bjoern Petersen
   Copyright (C) 1998 - 2023 Wolfgang Glunz
   Copyright (C) 2000 Thorsten Behrens

    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.

*/
#include "drvwmf.h"

#include I_string_h
#include I_fstream
#include I_stdio
#include I_stdlib

#include <math.h>
#include <algorithm>

#ifndef M_PI
#define M_PI		3.14159265358979323846
#endif

#if defined(_BIG_ENDIAN)
WORD LittleEndian_Word(WORD w)
{
	union {
		WORD data;
		BYTE b[2];
	} u1, u2;

	u1.data = w;
	u2.b[0] = u1.b[1];
	u2.b[1] = u1.b[0];
	return u2.data;
}

DWORD32 LittleEndian_Dword32(DWORD32 dw)
{
	union {
		DWORD32 data;
		BYTE b[4];
	} u1, u2;

	u1.data = dw;
	u2.b[0] = u1.b[3];
	u2.b[1] = u1.b[2];
	u2.b[2] = u1.b[1];
	u2.b[3] = u1.b[0];
	return u2.data;
}

#else
constexpr WORD LittleEndian_Word(WORD w)
{
	return w;
}

constexpr DWORD32 LittleEndian_Dword32(DWORD32 dw)
{
	return dw;
}
#endif /* defined(_BIG_ENDIAN) */


//
// recognized options for WMF/EMF driver:
// ======================================
//
// m :  maps all fonts in the document to Arial (should be available on every Windows installation)
// n :  emulate narrow fonts by shrinking fonts horizontally (sometimes does not look that good, but 
//      it's the only chance, when requested font weight is not available. And this is quite common 
//      for off-the-shelf Windows installations)
// b :  DON'T draw two white border pixel (upper left and lower right corner). They are normally 
//      drawn to keep content always within bounding box (is sometimes clipped otherwise, i.e. Windows 
//      doesn't respect pen thickness or rotated text extents).
//      This could be done more smarter for EMF, have to figure out...
// l :  prune lineends from text output (lineends are mapped to '#' in isolatin1)
//
// NOTE: old option 'e' (to generate enhanced metafile) removed, use driver emf instead!
//       old option 'p' (to generate palette) removed, wasn't documented and therefore not used,
//       and seems pretty unnecessary nowadays... 
//
// NOTE: there's now experimental image support for WMF. Current deficiencies: as the WMF/EMF format
//       cannot account for transparencies, the whole bounding box of an image is white. this
//       might cause trouble if the image is rotated/sheared, because that enlarges the bounding
//       beyond the actual image. 
//


// windows structure for standard metafile
// placeable header (aka Aldus Metafile)
constexpr DWORD32 PLACEABLEKEY = 0x9AC6CDD7L;
constexpr short PLACEABLESIZE = 22;
// see also http://premium.microsoft.com/msdn/library/techart/html/emfdcode.htm
// regarding alignment (Wo Gl)
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms534075(v=vs.85).aspx
#pragma pack(2)
struct PLACEABLEHEADER {
	DWORD32 key;
	WORD hmf;
	WORD left;
	WORD top;
	WORD right;
	WORD bottom;
	WORD inch;
	DWORD32 reserved;
	WORD checksum;
};
#pragma pack()

static const char description[] = "generated by WMF/EMF backend of pstoedit\0input\0\0"; //lint !e840 //usage of nul char in string


inline float drvWMF::scale() const
{
	const float scalef =  20.0f;
	if (options->OpenOfficeMode)
		return 1.0f;
	else
		return scalef;
}


inline long drvWMF::transx(float x) const
{
	if (options->OpenOfficeMode)
		return l_transX(x);
	else
		return (long)(scale() * (x));
}

inline long drvWMF::transy(float y) const
{
	if (options->OpenOfficeMode)
		return l_transY(y);
	else
		return (long)(scale() * (currentDeviceHeight - y));
}

void drvWMF::initMetaDC(HDC hdc){


#ifdef OLDCODE
// temporary - testing
			// set bounding box
//          SetWindowOrgEx(hdc, minX, minY, nullptr);
			SetWindowOrgEx(hdc, minX, maxY, nullptr);

//			SetViewportOrgEx(hdc, 0, 0, nullptr);

			SetWindowExtEx(hdc, maxX - minX, maxY - minY, nullptr);
			SetViewportExtEx(hdc, maxX - minX, maxY - minY, nullptr);
//          SetViewportExtEx(hdc,
//                           (long)((float)(maxX - minX) * (float)GetDeviceCaps(desktopDC,HORZRES) / 
//                                  (float)GetDeviceCaps(desktopDC,HORZSIZE) + .5),
//                           (long)((float)(maxY - minY) * (float)GetDeviceCaps(desktopDC,VERTRES) /
//                                  (float)GetDeviceCaps(desktopDC,VERTSIZE) + .5), nullptr);

#else
		
			// set reasonable mapping mode (don't want distortion)
			//SetMapMode(hdc, MM_ISOTROPIC);
			SetMapMode(hdc, MM_ANISOTROPIC);


	if (!options->OpenOfficeMode) {
		if (1){
			const int result = SetMapMode(hdc, MM_ANISOTROPIC);
			assert(result != 0);
		}
		SIZE oldWinExt;
		{
			const BOOL result = SetWindowExtEx(hdc, 1440, 1440, &oldWinExt);
//      const BOOL result = SetWindowExtEx(pd->hdcEMF,144,144,&oldWinExt);
			assert(result != 0);
		}
		SIZE oldViewPortExt;
		{
			const int result = SetViewportExtEx(hdc,
												GetDeviceCaps(metaDC, LOGPIXELSX),
												GetDeviceCaps(metaDC, LOGPIXELSY),
												&oldViewPortExt);
			assert(result != 0);
		}
	}
#endif


}

#define Hnullptr 0

drvWMF::derivedConstructor(drvWMF):
	constructBase,
	oldColoredPen(Hnullptr),
	oldColoredBrush(Hnullptr),
	enhanced(false),
	outFile(nullptr)
{
 	// some basic inits / could also be done in init list
	// put here because static analysis tools find a lot of uninitialized usage
	// in case we do a premature return from the ctor (see below)
	y_offset = 0;
	x_offset = 0;

	// in maxY and maxX are the highest values of the drawing
	maxY = 0;
	maxX = 0;
	maxStatus = false;				// 0, until first value set in maxY and maxX

	// in minX und minY are the lowest values of the drawing
	minX = 0;
	minY = 0;
	minStatus = false;				// 0, until first value set in minX and minY

	// setup pen for drawing functions
	const POINT PenWidth = { 0, 0 };
	penData.lopnStyle = PS_SOLID;	// solid pen
	penData.lopnWidth = PenWidth;	// width of pen
	penData.lopnColor = RGB(0, 0, 0);	// color of pen: black
	coloredPen = Hnullptr;

	// setup brush for drawing functions
	brushData.lbStyle = BS_SOLID;	// solid brush
	brushData.lbColor = RGB(0, 0, 0);	// color of brush (black)
	brushData.lbHatch = 0L;		// no pattern
	coloredBrush = Hnullptr;

	// set default font
	if (options->mapToArial) {
		const char *const defaultfontname = "Arial";
		setCurrentFontName(defaultfontname, false /* is standard font */ );
	} else {
		const char *const defaultfontname = "System";
		setCurrentFontName(defaultfontname, false /* is standard font */ );
	}
	myFont = Hnullptr;
	oldFont = Hnullptr;

	constexpr bool size_check = (sizeof(PLACEABLEHEADER) == PLACEABLESIZE) || (sizeof(PLACEABLEHEADER) == (PLACEABLESIZE + 2));
	// do some consistency checking
	if (!size_check) {
		errf <<
			"WARNING: structure size mismatch. Please contact author. Expecting :"
			<< PLACEABLESIZE << " found " << sizeof(PLACEABLEHEADER) << endl;
		errf << "sizeof(WORD)    = " << sizeof(WORD) << endl;	// MSVC:  2
		errf << "sizeof(DWORD32) = " << sizeof(DWORD32) << endl;// MSVC:  4
		errf << "sizeof(RECT)    = " << sizeof(RECT) << endl;	// MSVC: 16
		errf << "sizeof(HANDLE)  = " << sizeof(HANDLE) << endl;	// MSVC:  4
 		ctorOK = false;
 		return;
		// now the write below writes PLACEABLESIZE and no longer sizeof(PLACEABLEHEADER)
		// this should be independent then of the packing
		// the 2 bytes saved are those at the end of the struct
	}

	// desktopDC = GetDC(GetDesktopWindow());

	if (options->OpenOfficeMode) {
		desktopDC = GetDC(GetDesktopWindow());
	} else {
		desktopDC = 0;
	}

	// which output format?
	if (strcmp(driverdesc.symbolicname, "emf") == 0) {
		enhanced = true;
	}

	if (enhanced) {
		// 
		// enhanced-Metafile (memory based) for W95/98 or NT
		// if -nb is set, then narrowbox = false , -nb means no bounding box 
		if (options->winbb) {
			if (Verbose()) errf << " Windows will calculate BB " << endl;
			metaDC = CreateEnhMetaFile(desktopDC, nullptr, nullptr, nullptr);
		} else {
			if (Verbose()) errf << " Will use the PS BB " << endl;
			metaDC = CreateEnhMetaFile(desktopDC, nullptr, nullptr, nullptr);
		}

		if (!metaDC) {
			errf << "ERROR: could not create enhanced metafile" << endl;
			ctorOK = false;
			return;
		} 
		
		initMetaDC(metaDC);

	} else {
		// take temp file for standard WMF, to prepend placeable header
		tempName = full_qualified_tempnam("wmftemp");

		// standard metafile for Win16
                // coverity[tainted_string]
		metaDC = CreateMetaFile(tempName.c_str());
		if (!metaDC) {
			errf << "ERROR: could not open temporary metafile: " << tempName << endl;
			ctorOK = false;
			return;
		}
		const auto outFile_err = fopen_s(&outFile, outFileName.c_str(), "wb");
		if (outFile_err) {
			errf << "ERROR: cannot open final metafile " << outFileName << endl;
			ctorOK = false;
			return;
		}
	}


	// set reasonable text drawing mode
	SetBkMode(metaDC, TRANSPARENT);

	// set Postscript-like text alignment
	SetTextAlign(metaDC, TA_BASELINE);
}

#ifdef _WIN32
static void writeErrorCause(const char * mess)  
{
	const DWORD ec = GetLastError(); 
	LPVOID lpMsgBuf; 
	(void)FormatMessage( 
		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 
		nullptr, 
		ec, 
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
		(LPTSTR) &lpMsgBuf,	
		0,	
		nullptr	
	);	
	cerr << "Error Code for "  << mess << " ec: " << ec << " " << (char*) lpMsgBuf << endl; 
	LocalFree( lpMsgBuf ); 
}
#else
static inline void writeErrorCause(const char * )  {}
#endif

const int WMF_short_max = 32767;
const int WMF_short_min = -32768;

static constexpr short int_to_WMFRange(int i) {
	return i <  WMF_short_min
		     ? WMF_short_min
			 : ((i > WMF_short_max)
			     ? WMF_short_max
			     : (unsigned short) i);
}

drvWMF::~drvWMF()
{
	const BBox & psBBox = getCurrentBBox();

	minX = transx(psBBox.ll.x());
	minY = transy(psBBox.ur.y());
	maxX = transx(psBBox.ur.x());
	maxY = transy(psBBox.ll.y());

	if (Verbose()) errf << "original PostScript Bounding Box   : " << psBBox.ll.x()  << " " << psBBox.ll.y() << " " << psBBox.ur.x() << " " << psBBox.ur.y() << endl;
	if (Verbose()) errf << "transformed PostScript Bounding Box: " << minX << " " << minY << " " << maxX << " " << maxY << endl;

	
	// is the output within signed integer boundaries?
	if (minX < WMF_short_min || minX > WMF_short_max ||
		minY < WMF_short_min || minY > WMF_short_max || 
		maxX < WMF_short_min || maxX > WMF_short_max || 
		maxY < WMF_short_min || maxY > WMF_short_max) {
		// issue warning, coordinate overflow possible !
		errf << "Possible coordinate overflow, reduce scale! " << endl;
		errf << "  Origin: " << minX << " , " << minY << endl;
		errf << "  Size: " << maxX - minX << " , " << maxY - minY << endl;
	}
	// truncate to unsigned short range
	minX=int_to_WMFRange(minX);
	minY=int_to_WMFRange(minY);
	maxX=int_to_WMFRange(maxX);
	maxY=int_to_WMFRange(maxY);

	// set dummy pixel, if not EMF
//  if( !enhanced && drawBoundingBox )
	if (options->drawBoundingBox) {
		// standard metafile does not care much about user space.
		// so we have to draw two white dummy points at the pic's boundaries
		if (Verbose()) {
			// in verbose mode use a non with pixel
			SetPixel(metaDC, minX, minY, RGB(0, 120, 0));
			SetPixel(metaDC, maxX, maxY, RGB(0, 120, 120));
		} else {
			SetPixel(metaDC, minX, minY, RGB(255, 255, 255));
			SetPixel(metaDC, maxX, maxY, RGB(255, 255, 255));
		}
	}
	// free any allocated objects
	if (coloredPen) {
		SelectObject(metaDC, oldColoredPen);
		DeleteObject(coloredPen);
	}

	if (coloredBrush) {
		SelectObject(metaDC, oldColoredBrush);
		DeleteObject(coloredBrush);
	}

	if (myFont) {
		SelectObject(metaDC, oldFont);
		DeleteObject(myFont);
		myFont = Hnullptr;
	}
	// close and destroy metafile
	if (enhanced) {
		// get conversion figures
		LONG mmx, mmy, devx, devy;
		mmx = GetDeviceCaps(metaDC, HORZSIZE);
		mmy = GetDeviceCaps(metaDC, VERTSIZE);
		devx = GetDeviceCaps(metaDC, HORZRES);
		devy = GetDeviceCaps(metaDC, VERTRES);

		// close memory-based metafile (we're done drawing)
		HENHMETAFILE hMeta = CloseEnhMetaFile(metaDC);

		// create our final metafile (metaDC is reused)
		// need to have two metafiles, because we know the output dimension just now
		if (options->winbb) {
			// const RECT bbox = { minX, minY, maxX, maxY };
			if (Verbose()) cout << "creating final metafile" << endl;
			// cout << "creating with bounding box 2: " << minX << "," << minY<< "," << maxX<< "," << maxY << endl;
			// don't need a BB here - Windows will calculate it by itself (that is the whole purpose of the later replay)
			metaDC = CreateEnhMetaFile(desktopDC, outFileName.c_str(), nullptr, description);
			initMetaDC(metaDC);
		} else {
			// use the PS bounding box
			const RECT bbox = {
				minX * (mmx*100) / (devx),
				minY * (mmy*100) / (devy),
				maxX * (mmx*100) / (devx),
				maxY * (mmy*100) / (devy)
			};
			if (Verbose()) errf << "creating final metafile" << endl;
			metaDC = CreateEnhMetaFile(desktopDC, outFileName.c_str(), &bbox , description);
		}
		if (metaDC) {
			const RECT bbox = { minX, minY, maxX, maxY };

			// replay recorded metafile (hMeta -> metaDC)
			if (Verbose()) errf << "Info: replaying hMeta to metaDC with bounding box : " << minX << "," << minY<< "," << maxX<< "," << maxY << endl;

			if (!PlayEnhMetaFile(metaDC, hMeta, &bbox)) {
				writeErrorCause("PlayEnhMetaFile");
				errf << "ERROR: cannot replay created metafile" << endl;
			} else {
				if (Verbose())	errf << "Info: replayed metafile" << endl;
			}
			// close and commit metafile
			(void)DeleteEnhMetaFile(CloseEnhMetaFile(metaDC));
		} else {
			errf << "ERROR: could not open metafile for replay: " << outFileName << endl;
		}

		(void)DeleteEnhMetaFile(hMeta);
	} else {
		(void)DeleteMetaFile(CloseMetaFile(metaDC));

		// add placeable header to standard metafile

		PLACEABLEHEADER pHd;
		FILE *inFile = nullptr;
		WORD checksum;
		const auto inFile_err = fopen_s(&inFile, tempName.c_str(), "rb");
		if (!inFile_err) {
			if (outFile != nullptr) {
				// setup header
				pHd.key = LittleEndian_Dword32(PLACEABLEKEY);
				pHd.hmf = LittleEndian_Word(0);
				pHd.left = LittleEndian_Word((WORD) minX);
				pHd.top = LittleEndian_Word((WORD) minY);
				pHd.right = LittleEndian_Word((WORD) maxX);
				pHd.bottom = LittleEndian_Word((WORD) maxY);
				pHd.inch = LittleEndian_Word(12);
				pHd.reserved = LittleEndian_Dword32(0);

				// calculate checksum
				checksum = 0;
				WORD *checksumField = (WORD *) & pHd;
				for (unsigned int i = 0; i < 10; i++)
					checksum ^= checksumField[i];

				pHd.checksum = LittleEndian_Word(checksum);

				// write header
				if (fwrite(&pHd, PLACEABLESIZE , 1, outFile) != 1) {
					errf << "Can't write final metafile" << endl;
				}
				// append metafile data
				do {
					const int BUFSIZE = 1024;
					char buf[BUFSIZE];
					const size_t read = fread(buf, 1, BUFSIZE, inFile);

					if (read > 0) {
						if (fwrite(buf, 1, read, outFile) != read) {
							errf << "Can't write final metafile" << endl;
						}
					}
				}
				while (!feof(inFile));

				fclose(outFile);
			}

			fclose(inFile);
		}
		(void)remove(tempName.c_str());
		// cout << "tmp name " << tempName.c_str() << endl;

	}

	// delete desktop DC (might need it above)
	ReleaseDC(GetDesktopWindow(), desktopDC);
	desktopDC=Hnullptr;
	options=nullptr;
	metaDC=Hnullptr;
}


// the attributes for pens and brushes are set here.
// new Pen/Brush is selected into the DC
void drvWMF::setDrawAttr()
{
	// specify in RGB values
	penData.lopnColor =
		RGB((BYTE) (edgeR() * 255 + .5), (BYTE) (edgeG() * 255 + .5), (BYTE) (edgeB() * 255 + .5));
	brushData.lbColor =
		RGB((BYTE) (fillR() * 255 + .5), (BYTE) (fillG() * 255 + .5), (BYTE) (fillB() * 255 + .5));

	switch (currentLineType()) {
	case dashed:
		penData.lopnStyle = PS_DASH;
		break;

	case dotted:
		penData.lopnStyle = PS_DOT;
		break;

	case dashdot:
		penData.lopnStyle = PS_DASHDOT;
		break;

	case dashdotdot:
		penData.lopnStyle = PS_DASHDOTDOT;
		break;

	case solid:
		penData.lopnStyle = PS_SOLID;
		break;
	default: ; // not expected
	}

	const POINT PenWidth = { (int) (currentLineWidth() + .5), 0 };	// rounded int
	penData.lopnWidth = PenWidth;

	if (coloredPen) {
		SelectObject(metaDC, oldColoredPen);
		DeleteObject(coloredPen);
		coloredPen = Hnullptr;
	}

	coloredPen = CreatePenIndirect(&penData);
	if (!coloredPen) {
		errf << "ERROR: setDrawAttr: could not create pen !" << endl;
	} else {
		oldColoredPen = (HPEN) SelectObject(metaDC, coloredPen);
	}

	if (coloredBrush) {
		SelectObject(metaDC, oldColoredBrush);
		DeleteObject(coloredBrush);
		coloredBrush = Hnullptr;
	}

	coloredBrush = CreateBrushIndirect(&brushData);
	if (!coloredBrush) {
		errf << "ERROR: setDrawAttr: could not create brush !" << endl;
	} else {
		oldColoredBrush = (HBRUSH) SelectObject(metaDC, coloredBrush);
	}
}


int drvWMF::fetchFont(const TextInfo & textinfo, short int textHeight, short int textAngle)
{
	LOGFONT			theFontRec;
	theFontRec.lfHeight = -textHeight ;
	theFontRec.lfWidth = 0;		// optimal fit
	theFontRec.lfEscapement = textAngle;
	theFontRec.lfOrientation = textAngle;

	theFontRec.lfWeight = FW_DONTCARE;	// default: don't care

	if (strstr(textinfo.currentFontWeight.c_str(), "Regular"))
		theFontRec.lfWeight = FW_NORMAL;	// other values don't work  

	if (strstr(textinfo.currentFontWeight.c_str(), "Medium"))
		theFontRec.lfWeight = FW_NORMAL;	// other values don't work  

	if (strstr(textinfo.currentFontWeight.c_str(), "Normal"))
		theFontRec.lfWeight = FW_NORMAL;

	if (options->emulateNarrowFonts) {
		if (strstr(textinfo.currentFontWeight.c_str(), "Thin") ||
			strstr(textinfo.currentFontName.c_str(), "Thin") ||
			strstr(textinfo.currentFontFullName.c_str(), "Thin")) {
			theFontRec.lfWidth = textHeight / 3;	// narrow font emulation (trial and error value for Arial font)
		}

		if (strstr(textinfo.currentFontWeight.c_str(), "Extralight") ||
			strstr(textinfo.currentFontName.c_str(), "Extralight") ||
			strstr(textinfo.currentFontFullName.c_str(), "Extralight")) {
			theFontRec.lfWidth = textHeight / 4;	// narrow font emulation (trial and error value for Arial font)
		}

		if (strstr(textinfo.currentFontWeight.c_str(), "Ultralight") ||
			strstr(textinfo.currentFontName.c_str(), "Ultralight") ||
			strstr(textinfo.currentFontFullName.c_str(), "Ultralight")) {
			theFontRec.lfWidth = textHeight / 4;	// narrow font emulation (trial and error value for Arial font)
		}

		if (strstr(textinfo.currentFontWeight.c_str(), "Light") ||
			strstr(textinfo.currentFontName.c_str(), "Light") ||
			strstr(textinfo.currentFontFullName.c_str(), "Light") ||
			strstr(textinfo.currentFontWeight.c_str(), "Condensed") ||
			strstr(textinfo.currentFontName.c_str(), "Condensed") ||
			strstr(textinfo.currentFontFullName.c_str(), "Condensed")) {
			theFontRec.lfWidth = textHeight / 3;	// narrow font emulation (trial and error value for Arial font)
		}
	}

	if (strstr(textinfo.currentFontWeight.c_str(), "Semibold") ||
		strstr(textinfo.currentFontName.c_str(), "Semibold") ||
		strstr(textinfo.currentFontFullName.c_str(), "Semibold"))
		theFontRec.lfWeight = FW_BOLD;	// other values don't work

	if (strstr(textinfo.currentFontWeight.c_str(), "Demibold") ||
		strstr(textinfo.currentFontName.c_str(), "Demibold") ||
		strstr(textinfo.currentFontFullName.c_str(), "Demibold"))
		theFontRec.lfWeight = FW_BOLD;	// other values don't work

	if (strstr(textinfo.currentFontWeight.c_str(), "Bold") ||
		strstr(textinfo.currentFontName.c_str(), "Bold") ||
		strstr(textinfo.currentFontFullName.c_str(), "Bold"))
		theFontRec.lfWeight = FW_BOLD;

	if (strstr(textinfo.currentFontWeight.c_str(), "Extrabold") ||
		strstr(textinfo.currentFontName.c_str(), "Extrabold") ||
		strstr(textinfo.currentFontFullName.c_str(), "Extrabold"))
		theFontRec.lfWeight = FW_BOLD;	// other values don't work

	if (strstr(textinfo.currentFontWeight.c_str(), "Ultrabold") ||
		strstr(textinfo.currentFontName.c_str(), "Ultrabold") ||
		strstr(textinfo.currentFontFullName.c_str(), "Ultrabold"))
		theFontRec.lfWeight = FW_BOLD;	// other values don't work

	if (strstr(textinfo.currentFontWeight.c_str(), "Heavy") ||
		strstr(textinfo.currentFontName.c_str(), "Heavy") ||
		strstr(textinfo.currentFontFullName.c_str(), "Heavy"))
		theFontRec.lfWeight = FW_BOLD;	// other values don't work

	if (strstr(textinfo.currentFontWeight.c_str(), "Black") ||
		strstr(textinfo.currentFontName.c_str(), "Black") ||
		strstr(textinfo.currentFontFullName.c_str(), "Black"))
		theFontRec.lfWeight = FW_BOLD;	// other values don't work

	if ((strstr(textinfo.currentFontName.c_str(), "Italic") != nullptr) ||
		(strstr(textinfo.currentFontName.c_str(), "Oblique") != nullptr) ||
		(strstr(textinfo.currentFontFullName.c_str(), "Italic") != nullptr) ||
		(strstr(textinfo.currentFontFullName.c_str(), "Oblique") != nullptr)) {
		theFontRec.lfItalic = TRUE;
	} else {
		theFontRec.lfItalic = 0;
	}

	theFontRec.lfUnderline = 0;
	theFontRec.lfStrikeOut = 0;
	theFontRec.lfOutPrecision = OUT_DEFAULT_PRECIS;
	theFontRec.lfClipPrecision = CLIP_DEFAULT_PRECIS;
	theFontRec.lfQuality = PROOF_QUALITY;
	theFontRec.lfPitchAndFamily = VARIABLE_PITCH | FF_DONTCARE;	// let every font be possible

	if ((strstr(textinfo.currentFontFullName.c_str(), "Symbol") != nullptr) ||
		(strstr(textinfo.currentFontFullName.c_str(), "symbol") != nullptr)) {
		theFontRec.lfCharSet = SYMBOL_CHARSET;
		strcpy_s(theFontRec.lfFaceName, LF_FACESIZE, "symbol");
	} else if ((strstr(textinfo.currentFontFamilyName.c_str(), "Computer Modern") != nullptr) ) {
		// special handling for TeX Fonts - fix supplied by James F. O'Brien (job at cs.berkeley.edu)
        theFontRec.lfWeight = FW_NORMAL;
  	    theFontRec.lfItalic = 0;
  	    theFontRec.lfUnderline = 0;
	    theFontRec.lfCharSet = ANSI_CHARSET;
  	    strcpy_s(theFontRec.lfFaceName,LF_FACESIZE,  textinfo.currentFontName.c_str());
	} else 	{
		theFontRec.lfCharSet = ANSI_CHARSET;

		if (options->mapToArial)
			strcpy_s(theFontRec.lfFaceName,LF_FACESIZE, "Arial");
		else /* formerly we used currentFontFamilyName but FontName seems to be better */
			strcpy_s(theFontRec.lfFaceName,LF_FACESIZE, textinfo.currentFontName.c_str());
	}

	if (myFont) {
		SelectObject(metaDC, oldFont);
		DeleteObject(myFont);
		myFont = Hnullptr;
	}

	myFont = CreateFontIndirect(&theFontRec);
	if (!myFont) {
		errf << "ERROR: fetchFont: could not create font !" << endl;
	} else {
		oldFont = (HFONT) SelectObject(metaDC, myFont);
	}

	return 0;
}


// fill point array and draw
void drvWMF::drawPoly(polyType type)
{
	const unsigned int numOfElements = numberOfElementsInPath();
	// get us twice the number of elements in path,
	// as maybe every subpath is closed and consists of ONE point!
	auto aptlPoints = new POINT[2 * numOfElements];
	if (aptlPoints == nullptr) {
		errf << "ERROR: Cannot allocate memory for point-array" << endl;
		return;
	}
	// get us twice the number of elements in path,
	// as maybe every subpath is closed and consists of ONE point!
	auto aptlNumPts = new int[2 * numOfElements];
	if (aptlNumPts == nullptr) {
		errf << "ERROR: Cannot allocate memory for pointNum-array" << endl;
		delete [] aptlPoints;
		return;
	}

	POINT lastStart = { 0, 0 };
	bool lastWasMoveTo = false;
	bool lastWasClosePath = false;
	unsigned int numOfPolies = 0;
	unsigned int numOfPts = 0;
	unsigned int p = 0;

	for (unsigned int n = 0; n < numOfElements &&
		 p < 2 * numberOfElementsInPath() && numOfPolies < 2 * numOfElements; n++) {
		const basedrawingelement & elem = pathElement(n);

		if (elem.getType() != closepath) {
			aptlPoints[p].x = transx(elem.getPoint(0).x());
			aptlPoints[p].y = transy(elem.getPoint(0).y());
		} else {
			aptlPoints[p].x = transx(0);
			aptlPoints[p].y = transy(0);
		}

		if (elem.getType() == moveto || elem.getType() == lineto || elem.getType() == curveto) {
			const long lineWidth = (int) (currentLineWidth() + .5);	// rounded int

			// set drawing sizes
			if (minStatus) {
				if (aptlPoints[p].x - lineWidth < minX)
					minX = aptlPoints[p].x - lineWidth;

				if (aptlPoints[p].y - lineWidth < minY)
					minY = aptlPoints[p].y - lineWidth;
			} else {
				minX = aptlPoints[p].x - lineWidth;
				minY = aptlPoints[p].y - lineWidth;
				minStatus = 1;
			}

			if (maxStatus) {
				if (aptlPoints[p].x + lineWidth > maxX)
					maxX = aptlPoints[p].x + lineWidth;

				if (aptlPoints[p].y + lineWidth > maxY)
					maxY = aptlPoints[p].y + lineWidth;
			} else {
				maxX = aptlPoints[p].x + lineWidth;
				maxY = aptlPoints[p].y + lineWidth;
				maxStatus = true;
			}
		}

		switch (elem.getType()) {
		case moveto:
			if (type == TYPE_LINES) {
				if (!MoveToEx(metaDC, aptlPoints[p].x, aptlPoints[p].y, nullptr)) {
					errf << "ERROR: MoveTo: " << aptlPoints[p].x << "," << aptlPoints[p].y << endl;
				}
			} else {
				// ignore every path that is no area!
				if (numOfPts > 2) {
					// if path not closed -> do it manually!
					if (lastWasClosePath == FALSE) {
						if (p >= 2 * numberOfElementsInPath()) {
							errf << "ERROR: MoveTo: Out of array mem!" << endl;
						}
						// move this point one ahead
						aptlPoints[p + 1] = aptlPoints[p];

						// insert line to startpoint
						aptlPoints[p] = lastStart;

						p++;
						numOfPts++;
					}
					// store number of points for old subpath
					aptlNumPts[numOfPolies] = numOfPts;

					// one polygon more (for PolyPolygon)
					numOfPolies++;
				} else if (numOfPts == 2) {
					// we have a line here, so draw it!
					if (!MoveToEx(metaDC, aptlPoints[p - 2].x, aptlPoints[p - 2].y, nullptr)) {
						errf << "ERROR: MoveTo: " << aptlPoints[p -
																2].
							x << "," << aptlPoints[p - 2].y << endl;
					}

					if (!LineTo(metaDC, aptlPoints[p - 1].x, aptlPoints[p - 1].y)) {
						errf << "ERROR: LineTo: " << aptlPoints[p -
																1].
							x << "," << aptlPoints[p - 1].y << endl;
					}
				}
				// all lower numbers do not represent a line/polygon. Ignore them

				// start this subpath newly
				numOfPts = 0;

				// set flag to remove lone moveto's
				lastWasMoveTo = true;

				// clear flag to connect filled segments
				lastWasClosePath = true;
			}

			// save last starting position in case we've to close path
			lastStart = aptlPoints[p];

			break;

		case curveto:
		case lineto:
			if (type == TYPE_LINES) {
				if (!LineTo(metaDC, aptlPoints[p].x, aptlPoints[p].y)) {
					errf << "ERROR: LineTo: " << aptlPoints[p].x << "," << aptlPoints[p].y << endl;
				}
			} else {
				// clear flag to remove lone moveto's
				lastWasMoveTo = false;

				// clear flag to connect filled segments
				lastWasClosePath = false;

				// do nothing, point is already in array
			}
			break;

		case closepath:
			if (type == TYPE_LINES) {
				// close the thingy
				if (!LineTo(metaDC, lastStart.x, lastStart.y)) {
					errf << "ERROR: LineTo: " << lastStart.x << "," << lastStart.y << endl;
				}
			} else {
				// clear flag to remove lone moveto's
				lastWasMoveTo = false;

				// set flag to connect filled segments
				lastWasClosePath = true;

				// insert line to startpoint
				// (overwrite current point - closepath did not inserted points)
				aptlPoints[p] = lastStart;
			}
			break;

		default:
			errf << "\t\tFatal: unexpected case in drvwmf (line " << __LINE__ << ")" << endl;
			abort();
			break;
		}

		// next free point
		p++;

		// next point in subpath
		numOfPts++;
	}

	if (type != TYPE_LINES) {
		// remove last moveTo, as it's lonely
		if (lastWasMoveTo != FALSE)
			numOfPts--;

		// ignore every path that is no area!
		if (numOfPts > 2) {
			// if path not closed -> do it manually!
			if (lastWasClosePath == FALSE) {
				if (p >= 2 * numberOfElementsInPath()) {
					errf << "ERROR: MoveTo: Out of array mem!" << endl;
				}
				// move this point one ahead
				aptlPoints[p + 1] = aptlPoints[p];

				// insert line to startpoint
				aptlPoints[p] = lastStart;

				p++;
				numOfPts++;
			}
			// store number of points for old subpath
			aptlNumPts[numOfPolies] = numOfPts;

			// one polygon more (for PolyPolygon), as either we closed it above,
			// or closepath did it and started no new
			numOfPolies++;
		} else if (numOfPts == 2) {
			// we have a line here, so draw it!
			if (!MoveToEx(metaDC, aptlPoints[p - 2].x, aptlPoints[p - 2].y, nullptr)) {
				errf << "ERROR: MoveTo: " << aptlPoints[p -
														2].x << "," << aptlPoints[p - 2].y << endl;
			}

			if (!LineTo(metaDC, aptlPoints[p - 1].x, aptlPoints[p - 1].y)) {
				errf << "ERROR: LineTo: " << aptlPoints[p -
														1].x << "," << aptlPoints[p - 1].y << endl;
			}
		}
		// all lower numbers do not represent a line/polygon. Ignore them

		// anything to do?
		if (numOfPolies > 0) {
			if (!SetPolyFillMode(metaDC, (currentShowType() == fill) ? WINDING : ALTERNATE)) {
				errf << "ERROR: could not set fill mode" << endl;
			}

			if (!PolyPolygon(metaDC, aptlPoints, aptlNumPts, numOfPolies)) {
				const DWORD ec = GetLastError();
				errf << "ERROR: Polygon could not be drawn: (" << ec << ")" << endl;
				errf << "Number of Points: " << p << endl;
				for (unsigned int i = 0; i < p; i++) {
					errf << aptlPoints[i].x << "," << aptlPoints[i].y << endl;
				}
			}
		}
	}
	delete[]aptlPoints;
	delete[]aptlNumPts;
}



void drvWMF::close_page()
{
	// no function in drvwmf
}


void drvWMF::open_page()
{
	// no function in drvwmf
}


void drvWMF::show_path()
{	
	// update pen/brush
	setDrawAttr();

	// determine type: fill or line
	switch (currentShowType()) {
	case drvbase::stroke:
		// draw lines
		drawPoly(TYPE_LINES);
		break;

	case drvbase::fill:
	case drvbase::eofill:
		// draw filled polygon
		drawPoly(TYPE_FILL);
		break;

	default:
		// cannot happen
		errf << "unexpected ShowType " << (int) currentShowType();
		break;
	}

}


void drvWMF::show_text(const TextInfo & textinfo)
{
	// set text color
	SetTextColor(metaDC, RGB((BYTE) (textinfo.currentR * 255 + .5),
							 (BYTE) (textinfo.currentG * 255 + .5),
							 (BYTE) (textinfo.currentB * 255 + .5)));

	const short int textHeight = (short int) (textinfo.currentFontSize*scale() + .5) ;	// rounded int
	const short int textAngle = (short int) (10 * textinfo.currentFontAngle + .5);	// Windows needs 10th of degree

	// any need to change font handle?
	if (fontchanged())
		(void)fetchFont(textinfo, textHeight, textAngle);

	const long x1 = transx(textinfo.x());
	const long y1 = transy(textinfo.y());

	const long x2 = transx(textinfo.x_end());
	const long y2 = transy(textinfo.y_end());

	if (Verbose()) cout << "placing text : " << textinfo.thetext << " at " << textinfo.x() << "," << textinfo.y() << " in EMF coords: " << x1 << "," << y1 << endl;
	// calculate bounding box
	//
	// NOTE: cannot do that exactly (with ascent and descent height),
	// because DC queries are not permitted for metafile DCs.
	//
	const int xOff = abs((int)
						 (sin(textinfo.currentFontAngle * M_PI / 180) * textHeight + .5));
	const int yOff = abs((int)
						 (cos(textinfo.currentFontAngle * M_PI / 180) * textHeight + .5));

	const int xMin = (int) std::min(x1 - xOff, x2 - xOff);
	const int xMax = (int) std::max(x1 + xOff, x2 + xOff);
	const int yMin = (int) std::min(y1 - yOff, y2 - yOff);
	const int yMax = (int) std::max(y1 + yOff, y2 + yOff);

	if (minStatus) {
		if (xMin < minX)
			minX = xMin;

		if (yMin < minY)
			minY = yMin;
	} else {
		minX = xMin;
		minY = yMin;
		minStatus = 1;
	}

	if (maxStatus) {
		if (xMax > maxX)
			maxX = xMax;

		if (yMax > maxY)
			maxY = yMax;
	} else {
		maxX = xMax;
		maxY = yMax;
		maxStatus = true;
	}

	size_t textLen = strlen(textinfo.thetext.c_str());
	if (options->pruneLineEnds) {
		/* check for '#' at lineend */
		if (textLen > 0 && textinfo.thetext.c_str()[textLen - 1] == '#') {
			/* write one character less */
			textLen--;
		}
	}

#if defined(_WIN32)
	(void)TextOut(metaDC, x1, y1, textinfo.thetext.c_str(), (int) textLen);
	// TextOut(metaDC, 10, 10, "hello",4);
#else

	if (options->notforWindows) {
		// the user is aware of generating non-portable output
		TextOut(metaDC, x1, y1, textinfo.thetext.c_str(), textLen);
	} else {
		// we are not running Windows - so we need an emulation - see note below
		const long textdistance = (long) pythagoras((double)(x1-x2), (double)(y1-y2));
		const int letterspace = (textLen > 1) ? (textdistance / (textLen-1)) : 0 ;
		// if there is just one char in the text, then the inter letter spacing is 0 anyway
		auto pxDistance = new int[textLen];
		for (unsigned int letter = 0; letter < textLen; letter++) {
			pxDistance[letter] = letterspace;
		}
		const UINT fuOptions = 0;
		ExtTextOut (metaDC, x1, y1, fuOptions, 0, textinfo.thetext.c_str(), textLen, pxDistance);
		delete [] pxDistance;

		static bool warningwritten = false;
		if (textLen > 1 && !warningwritten) {
			warningwritten = true;
			errf << "Warning: Inter letter spacing is approximated by pstoedit because of problems in libemf. Use -pta option if results are not OK." << endl;
		}
	}
	
#endif

#if 0

ExtTextOut (hdc, xStart, yStart, iOptions, &rect, pString, iCount, pxDistance) ;
The fifth argument is a pointer to a rectangle structure. This is either a 
clipping rectangle, (if iOptions is set to ETO_CLIPPED, or a background 
rectangle to be filled with the current background color, 
if iOptions is set to ETO_OPAQUE. You can specify both options or neither. 

The last argument is an array of integers that specify the spacing between 
consecutive characters in the string. This allows a program to tighten or 
loosen intercharacter spacing, which is sometimes required for justifying 
a single word of text in a narrow column. 
The argument can be set to NULL for default character spacing. 

Note by Glunz: Even if the spacing argument can be set to NULL according to 
the description above, this creates a problem when libemf is used.
Libemf passes this NULL to the generated EMF file which then causes the
file to be no longer usable under newer versions of Windows.

It seems so that the Windows-API calculates this inter letter spacing automatically
before writing the record to the EMF file. But this needs access to font information
which is not easily done in libemf. 
Hence one work around is to assume a fixed width font and approximate the 
inter letter spacing by dividing the distance (P_end - P_start) by 
the number of characters in the string.

#endif




}


void drvWMF::show_rectangle(const float llx, const float lly, const float urx, const float ury)
{
	if (1) {
		show_path();
	} else {
// this code is disabled - see note below.
// needs to be fixed. 

	RECT localRect;

	// update pen/brush
	setDrawAttr();

	localRect.left = transx(llx);
	localRect.top = transy(lly);
	localRect.right = transx(urx);
	localRect.bottom = transy(ury);

	// calculate bounding box
	//
	const int xMin = (int) std::min(localRect.left, localRect.right);
	const int xMax = (int) std::max(localRect.left, localRect.right);
	const int yMin = (int) std::min(localRect.top, localRect.bottom);
	const int yMax = (int) std::max(localRect.top, localRect.bottom);

	if (minStatus) {
		if (xMin < minX)
			minX = xMin;

		if (yMin < minY)
			minY = yMin;
	} else {
		minX = xMin;
		minY = yMin;
		minStatus = 1;
	}

	if (maxStatus) {
		if (xMax > maxX)
			maxX = xMax;

		if (yMax > maxY)
			maxY = yMax;
	} else {
		maxX = xMax;
		maxY = yMax;
		maxStatus = true;
	}
#if 0
	if (currentShowType() == drvbase::stroke) {
		// wogl - this code is disabled. I don't know why this was this way. 
		// one cannot use a RECT as Point * and a polyline needs 4 points to make a RECT.
		// strange ....
		(void)Polyline(metaDC, (POINT *) & localRect, 2);
		// but also using a Rectangle isn't correct. 
	} else 
#endif
	{
		(void)Rectangle(metaDC, transx(llx), transy(lly), transx(urx), transy(ury));
	}
	}
}


void drvWMF::show_image(const PSImage & imageinfo)
{
	// first retrieve bounding box
	Point lowerLeft, upperRight;
	imageinfo.getBoundingBox(lowerLeft, upperRight);

	// not only bounding box must account for scale,
	// but also transformation matrix!

	// scale bounding box
	lowerLeft *= getScale();
	upperRight *= getScale();

	const long width  = abs(i_transX(upperRight.x()) - i_transX(lowerLeft.x()));
	const long height = abs(i_transY(upperRight.y()) - i_transY(lowerLeft.y()));

	if (Verbose()) {
		errf << "image.Width:" << imageinfo.width << " image.Height: " << imageinfo.height << endl;
		errf << "Width:" << width << " Height: " << height << endl;
	}
	// calculate bounding box
	//
	const int xMin = (int) std::min(transx(upperRight.x()), transx(lowerLeft.x()));
	const int xMax = (int) std::max(transx(upperRight.x()), transx(lowerLeft.x()));
	const int yMin = (int) std::min(transy(upperRight.y()), transy(lowerLeft.y()));
	const int yMax = (int) std::max(transy(upperRight.y()), transy(lowerLeft.y()));

	if (minStatus) {
		if (xMin < minX)
			minX = xMin;

		if (yMin < minY)
			minY = yMin;
	} else {
		minX = xMin;
		minY = yMin;
		minStatus = 1;
	}

	if (maxStatus) {
		if (xMax > maxX)
			maxX = xMax;

		if (yMax > maxY)
			maxY = yMax;
	} else {
		maxX = xMax;
		maxY = yMax;
		maxStatus = true;
	}

	// calc long-padded size of scanline 
	const long scanlineLen = ((width * 3) + 3) & ~3L;

	// now lets get some mem
	auto output = new unsigned char[scanlineLen * height];

	for (long i = 0; i < scanlineLen * height; i++)
		output[i] = 255;		// default is background (white)    

	if (!output) {
		errf << "ERROR: Cannot allocate memory for image" << endl;
		return;
	}
	// setup inverse transformation matrix (scaled, too!)
	const float matrixScale(imageinfo.normalizedImageCurrentMatrix[0] *
							imageinfo.normalizedImageCurrentMatrix[3] -
							imageinfo.normalizedImageCurrentMatrix[2] *
							imageinfo.normalizedImageCurrentMatrix[1]);
	const float inverseMatrix[] = {
		imageinfo.normalizedImageCurrentMatrix[3] / matrixScale / getScale(),
		-imageinfo.normalizedImageCurrentMatrix[1] / matrixScale / getScale(),
		-imageinfo.normalizedImageCurrentMatrix[2] / matrixScale / getScale(),
		imageinfo.normalizedImageCurrentMatrix[0] / matrixScale / getScale(),
		(imageinfo.normalizedImageCurrentMatrix[2] *
		 imageinfo.normalizedImageCurrentMatrix[5] -
		 imageinfo.normalizedImageCurrentMatrix[4] *
		 imageinfo.normalizedImageCurrentMatrix[3]) / matrixScale,
		(imageinfo.normalizedImageCurrentMatrix[4] *
		 imageinfo.normalizedImageCurrentMatrix[1] -
		 imageinfo.normalizedImageCurrentMatrix[0] *
		 imageinfo.normalizedImageCurrentMatrix[5]) / matrixScale
	};

	// now transform image
	for (long y = 0; y < height; y++) {
		// buffer current output scanline (saves us some multiplications)
		unsigned char *const currOutput = &output[scanlineLen * y];

		for (long x = 0; x < width; x++) {
			// now transform from device coordinate space to image space

			// apply transformation
			const Point currPoint = Point(x + lowerLeft.x(),
										  y + lowerLeft.y()).transform(inverseMatrix);

			// round to integers
			const long sourceX = (long) (currPoint.x() + .5);
			const long sourceY = (long) (currPoint.y() + .5);

			// is the pixel out of bounds? If yes, no further processing necessary
			if (sourceX >= 0L && (unsigned long) sourceX < imageinfo.width &&
				sourceY >= 0L && (unsigned long) sourceY < imageinfo.height) {
				// okay, fetch source pixel value into 
				// RGB triplet

				unsigned char r(255), g(255), b(255), C, M, Y, K;

				// how many components?
				switch (imageinfo.ncomp) {
				case 1:
					r = g = b = imageinfo.getComponent(sourceX, sourceY, 0);
					break;

				case 3:
					r = imageinfo.getComponent(sourceX, sourceY, 0);
					g = imageinfo.getComponent(sourceX, sourceY, 1);
					b = imageinfo.getComponent(sourceX, sourceY, 2);
					break;

				case 4:
					C = imageinfo.getComponent(sourceX, sourceY, 0);
					M = imageinfo.getComponent(sourceX, sourceY, 1);
					Y = imageinfo.getComponent(sourceX, sourceY, 2);
					K = imageinfo.getComponent(sourceX, sourceY, 3);

					// account for key
					C += K;
					M += K;
					Y += K;

					// convert color
					r = 255 - C;
					g = 255 - M;
					b = 255 - Y;
					break;

				default:
					errf << "\t\tFatal: unexpected case in drvwmf (line "
						<< __LINE__ << ")" << endl;
					delete [] output; // to make FlexeLint happier
					abort();
					return;
				}

				// set color triple
				currOutput[3 * x] = b;
				currOutput[3 * x + 1] = g;
				currOutput[3 * x + 2] = r;
			}
		}
	}

	// draw Windows DI bitmap
	BITMAPINFO bmi;

			/* setup BITMAPINFO */
		bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bmi.bmiHeader.biWidth = width;
		bmi.bmiHeader.biHeight = height;
		bmi.bmiHeader.biPlanes = 1;
		bmi.bmiHeader.biBitCount = 24;	// always truecolor output
		bmi.bmiHeader.biClrUsed = 0;
		bmi.bmiHeader.biCompression = BI_RGB;
		bmi.bmiHeader.biSizeImage = 0;	/* size not important */
		bmi.bmiHeader.biXPelsPerMeter = 0;	/* size not important */
		bmi.bmiHeader.biYPelsPerMeter = 0;	/* size not important */
		bmi.bmiHeader.biClrImportant = 0;

		if (!SetDIBitsToDevice(metaDC,
							  transx(lowerLeft.x()),
							  transy(upperRight.y()),
							  width, height, 0, 0, 0, height, output, &bmi, DIB_RGB_COLORS)) {
			errf << "ERROR: Cannot draw bitmap" << endl;
		}
	delete[]output;
}



#if defined(_WIN32)
//
// Wmf works only under Windows since the libemf has only a broken emulation of wmf.
// Under libemf, a createMetaFile effectively creates an enhMetaFile but that confuses almost all
// programs which expect to read an WMF with an aldus placeable header
// 
static DriverDescriptionT < drvWMF > D_wmf("wmf", "MS Windows Metafile", "","wmf", true,	// backend supports subpaths
										   // if subpaths are supported, the backend must deal with
										   // sequences of the following form
										   // moveto (start of subpath)
										   // lineto (a line segment)
										   // lineto 
										   // moveto (start of a new subpath)
										   // lineto (a line segment)
										   // lineto 
										   //
										   // If this argument is set to false each subpath is drawn 
										   // individually which might not necessarily represent
										   // the original drawing.
										   false,	// backend does not support curves (at least for WMF - have to take least common denominator here)
										   true,	// backend supports elements which are filled and have edges 
										   true,	// backend supports text
										   DriverDescription::imageformat::memoryeps,	// no support for PNG file images
										   DriverDescription::opentype::noopen,	// we open output file ourselves
										   false,	// if format supports multiple pages in one file (DEFINETELY not) 
										   false  /*clipping */ 
										   );
#endif

static DriverDescriptionT < drvWMF > D_emf("emf", "Enhanced MS Windows Metafile", "","emf", true,	// backend supports subpaths
										   // if subpaths are supported, the backend must deal with
										   // sequences of the following form
										   // moveto (start of subpath)
										   // lineto (a line segment)
										   // lineto 
										   // moveto (start of a new subpath)
										   // lineto (a line segment)
										   // lineto 
										   //
										   // If this argument is set to false each subpath is drawn 
										   // individually which might not necessarily represent
										   // the original drawing.
										   false,	// backend does not support curves (not yet)
										   true,	// backend supports elements which are filled and have edges 
										   true,	// backend supports text
										   DriverDescription::imageformat::memoryeps,	// no support for PNG file images
										   DriverDescription::opentype::noopen,	// we open output file ourselves
										   false,	// if format supports multiple pages in one file (DEFINETELY not) 
										   false  /*clipping */
										   );
 
