1 /*
2    drvwmf.cpp : This file is part of pstoedit
3 	 Backend for Windows Meta File (WMF/EMF)
4 
5    Copyright (C) 1996,1997 Jens Weber, rz47b7_AT_PostAG.DE
6    Copyright (C) 1998 Thorsten Behrens and Bjoern Petersen
7    Copyright (C) 1998 - 2014 Wolfgang Glunz
8    Copyright (C) 2000 Thorsten Behrens
9 
10     This program is free software; you can redistribute it and/or modify
11     it under the terms of the GNU General Public License as published by
12     the Free Software Foundation; either version 2 of the License, or
13     (at your option) any later version.
14 
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18     GNU General Public License for more details.
19 
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 
24 */
25 #include "drvwmf.h"
26 
27 #include I_string_h
28 #include I_fstream
29 #include I_stdio
30 #include I_stdlib
31 
32 #include <math.h>
33 
34 #ifndef M_PI
35 #define M_PI		3.14159265358979323846
36 #endif
37 
38 #if defined(PS_BIG_ENDIAN)
LittleEndian_Word(WORD w)39 WORD LittleEndian_Word(WORD w)
40 {
41 	union {
42 		WORD data;
43 		BYTE b[2];
44 	} u1, u2;
45 
46 	u1.data = w;
47 	u2.b[0] = u1.b[1];
48 	u2.b[1] = u1.b[0];
49 	return u2.data;
50 }
51 
LittleEndian_Dword32(DWORD32 dw)52 DWORD32 LittleEndian_Dword32(DWORD32 dw)
53 {
54 	union {
55 		DWORD32 data;
56 		BYTE b[4];
57 	} u1, u2;
58 
59 	u1.data = dw;
60 	u2.b[0] = u1.b[3];
61 	u2.b[1] = u1.b[2];
62 	u2.b[2] = u1.b[1];
63 	u2.b[3] = u1.b[0];
64 	return u2.data;
65 }
66 
67 #else
LittleEndian_Word(WORD w)68 WORD LittleEndian_Word(WORD w)
69 {
70 	return w;
71 }
72 
LittleEndian_Dword32(DWORD32 dw)73 DWORD32 LittleEndian_Dword32(DWORD32 dw)
74 {
75 	return dw;
76 }
77 #endif /* defined(PS_BIG_ENDIAN) */
78 
79 
80 //
81 // recognized options for WMF/EMF driver:
82 // ======================================
83 //
84 // m :  maps all fonts in the document to Arial (should be available on every Windows installation)
85 // n :  emulate narrow fonts by shrinking fonts horizontally (sometimes does not look that good, but
86 //      it's the only chance, when requested font weight is not available. And this is quite common
87 //      for off-the-shelf Windows installations)
88 // b :  DON'T draw two white border pixel (upper left and lower right corner). They are normally
89 //      drawn to keep content always within bounding box (is sometimes clipped otherwise, i.e. Windows
90 //      doesn't respect pen thickness or rotated text extents).
91 //      This could be done more smarter for EMF, have to figure out...
92 // l :  prune lineends from text output (lineends are mapped to '#' in isolatin1)
93 //
94 // NOTE: old option 'e' (to generate enhanced metafile) removed, use driver emf instead!
95 //       old option 'p' (to generate palette) removed, wasn't documented and therefore not used,
96 //       and seems pretty unnecessary nowadays...
97 //
98 // NOTE: there's now experimental image support for WMF. Current deficiencies: as the WMF/EMF format
99 //       cannot account for transparencies, the whole bounding box of an image is white. this
100 //       might cause trouble if the image is rotated/sheared, because that enlarges the bounding
101 //       beyond the actual image.
102 //
103 
104 
105 // windows structure for standard metafile
106 // placeable header (aka Aldus Metafile)
107 const DWORD32 PLACEABLEKEY = 0x9AC6CDD7L;
108 const short PLACEABLESIZE = 22;
109 // see also http://premium.microsoft.com/msdn/library/techart/html/emfdcode.htm
110 // regarding alignment (Wo Gl)
111 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms534075(v=vs.85).aspx
112 #pragma pack(2)
113 struct PLACEABLEHEADER {
114 	DWORD32 key;
115 	WORD hmf;
116 	WORD left;
117 	WORD top;
118 	WORD right;
119 	WORD bottom;
120 	WORD inch;
121 	DWORD32 reserved;
122 	WORD checksum;
123 };
124 #pragma pack()
125 
126 static const char description[] = "generated by WMF/EMF backend of pstoedit\0input\0\0"; //lint !e840 //usage of nul char in string
127 
128 
scale() const129 inline float drvWMF::scale() const
130 {
131 	const float scalef =  20.0f;
132 	if (options->OpenOfficeMode)
133 		return 1.0f;
134 	else
135 		return scalef;
136 }
137 
138 
transx(float x) const139 inline long drvWMF::transx(float x) const
140 {
141 	if (options->OpenOfficeMode)
142 		return l_transX(x);
143 	else
144 		return (long)(scale() * (x));
145 }
146 
transy(float y) const147 inline long drvWMF::transy(float y) const
148 {
149 	if (options->OpenOfficeMode)
150 		return l_transY(y);
151 	else
152 		return (long)(scale() * (currentDeviceHeight - y));
153 }
154 
initMetaDC(HDC hdc)155 void drvWMF::initMetaDC(HDC hdc){
156 
157 
158 #ifdef OLDCODE
159 // temporary - testing
160 			// set bounding box
161 //          SetWindowOrgEx(hdc, minX, minY, NIL);
162 			SetWindowOrgEx(hdc, minX, maxY, NIL);
163 
164 //			SetViewportOrgEx(hdc, 0, 0, NIL);
165 
166 			SetWindowExtEx(hdc, maxX - minX, maxY - minY, NIL);
167 			SetViewportExtEx(hdc, maxX - minX, maxY - minY, NIL);
168 //          SetViewportExtEx(hdc,
169 //                           (long)((float)(maxX - minX) * (float)GetDeviceCaps(desktopDC,HORZRES) /
170 //                                  (float)GetDeviceCaps(desktopDC,HORZSIZE) + .5),
171 //                           (long)((float)(maxY - minY) * (float)GetDeviceCaps(desktopDC,VERTRES) /
172 //                                  (float)GetDeviceCaps(desktopDC,VERTSIZE) + .5), NIL);
173 
174 #else
175 
176 			// set reasonable mapping mode (don't want distortion)
177 			//SetMapMode(hdc, MM_ISOTROPIC);
178 			SetMapMode(hdc, MM_ANISOTROPIC);
179 
180 
181 	if (!options->OpenOfficeMode) {
182 		if (1){
183 			const int result = SetMapMode(hdc, MM_ANISOTROPIC);
184 			assert(result != 0);
185 		}
186 		SIZE oldWinExt;
187 		{
188 			const BOOL result = SetWindowExtEx(hdc, 1440, 1440, &oldWinExt);
189 //      const BOOL result = SetWindowExtEx(pd->hdcEMF,144,144,&oldWinExt);
190 			assert(result != 0);
191 		}
192 		SIZE oldViewPortExt;
193 		{
194 			const int result = SetViewportExtEx(hdc,
195 												GetDeviceCaps(metaDC, LOGPIXELSX),
196 												GetDeviceCaps(metaDC, LOGPIXELSY),
197 												&oldViewPortExt);
198 			assert(result != 0);
199 		}
200 	}
201 #endif
202 
203 
204 }
205 
derivedConstructor(drvWMF)206 drvWMF::derivedConstructor(drvWMF):
207 	constructBase,
208 	oldColoredPen(NIL),
209 	oldColoredBrush(NIL),
210 	enhanced(false),
211 	outFile(0L)
212 {
213  	// some basic inits / could also be done in init list
214 	// put here because static analysis tools find a lot of uninitialized usage
215 	// in case we do a premature return from the ctor (see below)
216 	y_offset = 0;
217 	x_offset = 0;
218 
219 	// in maxY and maxX are the highest values of the drawing
220 	maxY = 0;
221 	maxX = 0;
222 	maxStatus = false;				// 0, until first value set in maxY and maxX
223 
224 	// in minX und minY are the lowest values of the drawing
225 	minX = 0;
226 	minY = 0;
227 	minStatus = false;				// 0, until first value set in minX and minY
228 
229 	// setup pen for drawing functions
230 	const POINT PenWidth = { 0, 0 };
231 	penData.lopnStyle = PS_SOLID;	// solid pen
232 	penData.lopnWidth = PenWidth;	// width of pen
233 	penData.lopnColor = RGB(0, 0, 0);	// color of pen: black
234 	coloredPen = 0L;
235 
236 	// setup brush for drawing functions
237 	brushData.lbStyle = BS_SOLID;	// solid brush
238 	brushData.lbColor = RGB(0, 0, 0);	// color of brush (black)
239 	brushData.lbHatch = 0L;		// no pattern
240 	coloredBrush = 0L;
241 
242 	// set default font
243 	if (options->mapToArial) {
244 		const char *const defaultfontname = "Arial";
245 		setCurrentFontName(defaultfontname, false /* is standard font */ );
246 	} else {
247 		const char *const defaultfontname = "System";
248 		setCurrentFontName(defaultfontname, false /* is standard font */ );
249 	}
250 	myFont = 0L;
251 	oldFont = 0L;
252 
253 
254 	// do some consistency checking
255 	if (! ( (sizeof(PLACEABLEHEADER) == PLACEABLESIZE) || (sizeof(PLACEABLEHEADER) == (PLACEABLESIZE+2)) ) ) {
256 		errf <<
257 			"WARNING: structure size mismatch. Please contact author. Expecting :"
258 			<< PLACEABLESIZE << " found " << sizeof(PLACEABLEHEADER) << endl;
259 		errf << "sizeof(WORD)    = " << sizeof(WORD) << endl;	// MSVC:  2
260 		errf << "sizeof(DWORD32) = " << sizeof(DWORD32) << endl;// MSVC:  4
261 		errf << "sizeof(RECT)    = " << sizeof(RECT) << endl;	// MSVC: 16
262 		errf << "sizeof(HANDLE)  = " << sizeof(HANDLE) << endl;	// MSVC:  4
263  		ctorOK = false;
264  		return;
265 		// now the write below writes PLACEABLESIZE and no longer sizeof(PLACEABLEHEADER)
266 		// this should be independent then of the packing
267 		// the 2 bytes saved are those at the end of the struct
268 	}
269 
270 	// desktopDC = GetDC(GetDesktopWindow());
271 
272 	if (options->OpenOfficeMode) {
273 		desktopDC = GetDC(GetDesktopWindow());
274 	} else {
275 		desktopDC = NIL;
276 	}
277 
278 	// which output format?
279 	if (strcmp(driverdesc.symbolicname, "emf") == 0) {
280 		enhanced = true;
281 	}
282 
283 	if (enhanced) {
284 #if 1
285 		const BBox & psBBox = getCurrentBBox();
286 
287 		minX = transx(psBBox.ll.x_);
288 		minY = transy(psBBox.ur.y_);
289 		maxX = transx(psBBox.ur.x_);
290 		maxY = transy(psBBox.ll.y_);
291 
292 //		const RECT bbox = { minX, minY, maxX, maxY };
293 		if (Verbose()) errf << "calculated Bounding Box: " << minX << " " << minY << " " << maxX << " " << maxY << endl;
294 		// cout << "PostScript Bounding Box: " << psBBox.ll.x_  << " " << psBBox.ll.y_ << " " << psBBox.ur.x_ << " " << psBBox.ur.y_ << endl;
295 #endif
296 
297 		//
298 		// enhanced-Metafile (memory based) for W95/98 or NT
299 		// if -nb is set, then narrowbox = false , -nb means no bounding box
300 		if (options->winbb) {
301 			if (Verbose()) errf << " Windows will calculate BB " << endl;
302 			metaDC = CreateEnhMetaFile(desktopDC, NIL, NIL, NIL);
303 		} else {
304 	// under non Windows systems we cannot use PlayEnhMetafile
305 			if (Verbose()) errf << " not creating with bounding box " << endl;
306 			// metaDC = CreateEnhMetaFile(desktopDC, outFileName.c_str(), &bbox , description);
307 			metaDC = CreateEnhMetaFile(desktopDC, outFileName.c_str(), NIL, description);
308 		}
309 
310 		if (!metaDC) {
311 			errf << "ERROR: could not create enhanced metafile" << endl;
312 			ctorOK = false;
313 			return;
314 		}
315 
316 		initMetaDC(metaDC);
317 
318 	} else {
319 		// take temp file for standard WMF, to prepend placeable header
320 		tempName = full_qualified_tempnam("wmftemp");
321 
322 		// standard metafile for Win16
323                 // coverity[tainted_string]
324 		metaDC = CreateMetaFile(tempName.c_str());
325 		if (!metaDC) {
326 			errf << "ERROR: could not open temporary metafile: " << tempName << endl;
327 			ctorOK = false;
328 			return;
329 		}
330 
331 		if ((outFile = fopen(outFileName.c_str(), "wb")) == 0L) {
332 			errf << "ERROR: cannot open final metafile " << outFileName << endl;
333 			ctorOK = false;
334 			return;
335 		}
336 	}
337 
338 
339 	// set reasonable text drawing mode
340 	SetBkMode(metaDC, TRANSPARENT);
341 
342 	// set Postscript-like text alignment
343 	SetTextAlign(metaDC, TA_BASELINE);
344 }
345 
346 #ifdef _WIN32
writeErrorCause(const char * mess)347 static void writeErrorCause(const char * mess)
348 {
349 	DWORD ec = GetLastError();
350 	LPVOID lpMsgBuf;
351 	(void)FormatMessage(
352 		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
353 		NULL,
354 		ec,
355 		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
356 		(LPTSTR) &lpMsgBuf,
357 		0,
358 		NULL
359 	);
360 	cerr << "Error Code for "  << mess << " ec: " << ec << " " << (char*) lpMsgBuf << endl;
361 	LocalFree( lpMsgBuf );
362 }
363 #else
writeErrorCause(const char *)364 static inline void writeErrorCause(const char * )  {}
365 #endif
366 
367 const int WMF_short_max = 32767;
368 const int WMF_short_min = -32768;
369 
int_to_WMFRange(int i)370 static short int_to_WMFRange(int i) {
371 	return i <  WMF_short_min
372 		     ? WMF_short_min
373 			 : ((i > WMF_short_max)
374 			     ? WMF_short_max
375 			     : (unsigned short) i);
376 }
377 
~drvWMF()378 drvWMF::~drvWMF()
379 {
380 	const BBox & psBBox = getCurrentBBox();
381 
382 	minX = transx(psBBox.ll.x_);
383 	minY = transy(psBBox.ur.y_);
384 	maxX = transx(psBBox.ur.x_);
385 	maxY = transy(psBBox.ll.y_);
386 
387 	if (Verbose()) errf << "original PostScript Bounding Box   : " << psBBox.ll.x_  << " " << psBBox.ll.y_ << " " << psBBox.ur.x_ << " " << psBBox.ur.y_ << endl;
388 	if (Verbose()) errf << "transformed PostScript Bounding Box: " << minX << " " << minY << " " << maxX << " " << maxY << endl;
389 
390 
391 	// is the output within signed integer boundaries?
392 	if (minX < WMF_short_min || minX > WMF_short_max ||
393 		minY < WMF_short_min || minY > WMF_short_max ||
394 		maxX < WMF_short_min || maxX > WMF_short_max ||
395 		maxY < WMF_short_min || maxY > WMF_short_max) {
396 		// issue warning, coordinate overflow possible !
397 		errf << "Possible coordinate overflow, reduce scale! " << endl;
398 		errf << "  Origin: " << minX << " , " << minY << endl;
399 		errf << "  Size: " << maxX - minX << " , " << maxY - minY << endl;
400 	}
401 	// truncate to unsigned short range
402 	minX=int_to_WMFRange(minX);
403 	minY=int_to_WMFRange(minY);
404 	maxX=int_to_WMFRange(maxX);
405 	maxY=int_to_WMFRange(maxY);
406 
407 	// set dummy pixel, if not EMF
408 //  if( !enhanced && drawBoundingBox )
409 	if (options->drawBoundingBox) {
410 		// standard metafile does not care much about user space.
411 		// so we have to draw two white dummy points at the pic's boundaries
412 		if (Verbose()) {
413 			// in verbose mode use a non with pixel
414 			SetPixel(metaDC, minX, minY, RGB(0, 120, 0));
415 			SetPixel(metaDC, maxX, maxY, RGB(0, 120, 120));
416 		} else {
417 			SetPixel(metaDC, minX, minY, RGB(255, 255, 255));
418 			SetPixel(metaDC, maxX, maxY, RGB(255, 255, 255));
419 		}
420 	}
421 	// free any allocated objects
422 	if (coloredPen) {
423 		SelectObject(metaDC, oldColoredPen);
424 		DeleteObject(coloredPen);
425 	}
426 
427 	if (coloredBrush) {
428 		SelectObject(metaDC, oldColoredBrush);
429 		DeleteObject(coloredBrush);
430 	}
431 
432 	if (myFont) {
433 		SelectObject(metaDC, oldFont);
434 		DeleteObject(myFont);
435 		myFont = 0L;
436 	}
437 	// close and destroy metafile
438 	if (enhanced) {
439 		// close memory-based metafile (we're done drawing)
440 		HENHMETAFILE hMeta = CloseEnhMetaFile(metaDC);
441 
442 #if 0
443 		// query bounding informations
444 		ENHMETAHEADER enhHeader;
445 		GetEnhMetaFileHeader(hMeta, sizeof(ENHMETAHEADER), &enhHeader);
446 
447 //
448 // under construction. should somehow be possible to abandon dummy pixel drawing for
449 // EMF, by setting bounding box and friends appropriately
450 //
451 		errf << "bbox: " << minX << " " << minY << " " << maxX << " " << maxY << endl;
452 
453 		const RECT dimension = { 0, 0,
454 			enhHeader.rclFrame.right - enhHeader.rclFrame.left,
455 			enhHeader.rclFrame.bottom - enhHeader.rclFrame.top
456 		};
457 		const RECT dimension = { 0, 0,
458 			(long) (((float) (maxX - minX) * (float) GetDeviceCaps(desktopDC,
459 																   HORZRES) /
460 					 (float) GetDeviceCaps(desktopDC,
461 										   HORZSIZE)) * 10.0 + .5),
462 			(long) (((float) (maxY - minY) * (float) GetDeviceCaps(desktopDC,
463 																   VERTRES) /
464 					 (float) GetDeviceCaps(desktopDC,
465 										   VERTSIZE)) * 10.0 + .5)
466 		};
467 #endif
468 
469 
470 		// create our final metafile (metaDC is reused)
471 		// need to have two metafiles, because we know the output dimension just now
472 		if (options->winbb) {
473 			// const RECT bbox = { minX, minY, maxX, maxY };
474 			if (Verbose()) cout << "creating final metafile" << endl;
475 			// cout << "creating with bounding box 2: " << minX << "," << minY<< "," << maxX<< "," << maxY << endl;
476 			// don't need a BB here - Windows will calculate it by itself (that is the whole purpose of the later replay)
477 			metaDC = CreateEnhMetaFile(desktopDC, outFileName.c_str(), NIL, description);
478 			initMetaDC(metaDC);
479 		}
480 		if (metaDC) {
481 
482 			if (options->winbb) {
483 				const RECT bbox = { minX, minY, maxX, maxY };
484 //          const RECT  bbox = {minX, minY, maxX-minX, maxY-minY};
485 //          const RECT  bbox = {minX, maxY, maxX-minX, maxY-minY};
486 //          const RECT  bbox = {minX, maxY, maxX+2*minX, -minY-maxY};
487 
488 			// replay recorded metafile (hMeta -> metaDC)
489 				if (Verbose()) errf << "Info: replaying hMeta to metaDC with bounding box : " << minX << "," << minY<< "," << maxX<< "," << maxY << endl;
490 
491 				if (!PlayEnhMetaFile(metaDC, hMeta, &bbox)) {
492 					writeErrorCause("PlayEnhMetaFile");
493 					errf << "ERROR: cannot replay created metafile" << endl;
494 				} else {
495 					if (Verbose())	errf << "Info: replayed metafile" << endl;
496 				}
497 			}
498 			// close and commit metafile
499 			(void)DeleteEnhMetaFile(CloseEnhMetaFile(metaDC));
500 		} else {
501 			errf << "ERROR: could not open metafile for replay: " << outFileName << endl;
502 		}
503 
504 		(void)DeleteEnhMetaFile(hMeta);
505 	} else {
506 		(void)DeleteMetaFile(CloseMetaFile(metaDC));
507 
508 		// add placeable header to standard metafile
509 
510 		PLACEABLEHEADER pHd;
511 		FILE *inFile;
512 		WORD checksum;
513 
514 
515 		if ((inFile = fopen(tempName.c_str(), "rb")) != 0L) {
516 			if (outFile != 0L) {
517 				// setup header
518 				pHd.key = LittleEndian_Dword32(PLACEABLEKEY);
519 				pHd.hmf = LittleEndian_Word(0);
520 				pHd.left = LittleEndian_Word((WORD) minX);
521 				pHd.top = LittleEndian_Word((WORD) minY);
522 				pHd.right = LittleEndian_Word((WORD) maxX);
523 				pHd.bottom = LittleEndian_Word((WORD) maxY);
524 				pHd.inch = LittleEndian_Word(12);
525 				pHd.reserved = LittleEndian_Dword32(0);
526 
527 				// calculate checksum
528 				checksum = 0;
529 				WORD *checksumField = (WORD *) & pHd;
530 				for (unsigned int i = 0; i < 10; i++)
531 					checksum ^= checksumField[i];
532 
533 				pHd.checksum = LittleEndian_Word(checksum);
534 
535 				// write header
536 				if (fwrite(&pHd, PLACEABLESIZE , 1, outFile) != 1) {
537 					errf << "Can't write final metafile" << endl;
538 				}
539 				// append metafile data
540 				do {
541 					const int BUFSIZE = 1024;
542 					char buf[BUFSIZE];
543 					size_t read = fread(buf, 1, BUFSIZE, inFile);
544 
545 					if (read > 0) {
546 						if (fwrite(buf, 1, read, outFile) != read) {
547 							errf << "Can't write final metafile" << endl;
548 						}
549 					}
550 				}
551 				while (!feof(inFile));
552 
553 				fclose(outFile);
554 			}
555 
556 			fclose(inFile);
557 		}
558 		(void)remove(tempName.c_str());
559 		// cout << "tmp name " << tempName.c_str() << endl;
560 
561 	}
562 
563 	// delete desktop DC (might need it above)
564 	ReleaseDC(GetDesktopWindow(), desktopDC);
565 	desktopDC=0;
566 	options=0;
567 	metaDC=0;
568 }
569 
570 
571 // the attributes for pens and brushes are set here.
572 // new Pen/Brush is selected into the DC
setDrawAttr()573 void drvWMF::setDrawAttr()
574 {
575 	// specify in RGB values
576 	penData.lopnColor =
577 		RGB((BYTE) (edgeR() * 255 + .5), (BYTE) (edgeG() * 255 + .5), (BYTE) (edgeB() * 255 + .5));
578 	brushData.lbColor =
579 		RGB((BYTE) (fillR() * 255 + .5), (BYTE) (fillG() * 255 + .5), (BYTE) (fillB() * 255 + .5));
580 
581 	switch (currentLineType()) {
582 	case dashed:
583 		penData.lopnStyle = PS_DASH;
584 		break;
585 
586 	case dotted:
587 		penData.lopnStyle = PS_DOT;
588 		break;
589 
590 	case dashdot:
591 		penData.lopnStyle = PS_DASHDOT;
592 		break;
593 
594 	case dashdotdot:
595 		penData.lopnStyle = PS_DASHDOTDOT;
596 		break;
597 
598 	case solid:
599 		penData.lopnStyle = PS_SOLID;
600 		break;
601 	default: ; // not expected
602 	}
603 
604 	const POINT PenWidth = { (int) (currentLineWidth() + .5), 0 };	// rounded int
605 	penData.lopnWidth = PenWidth;
606 
607 	if (coloredPen) {
608 		SelectObject(metaDC, oldColoredPen);
609 		DeleteObject(coloredPen);
610 		coloredPen = 0L;
611 	}
612 
613 	coloredPen = CreatePenIndirect(&penData);
614 	if (!coloredPen) {
615 		errf << "ERROR: setDrawAttr: could not create pen !" << endl;
616 	} else {
617 		oldColoredPen = (HPEN) SelectObject(metaDC, coloredPen);
618 	}
619 
620 	if (coloredBrush) {
621 		SelectObject(metaDC, oldColoredBrush);
622 		DeleteObject(coloredBrush);
623 		coloredBrush = 0L;
624 	}
625 
626 	coloredBrush = CreateBrushIndirect(&brushData);
627 	if (!coloredBrush) {
628 		errf << "ERROR: setDrawAttr: could not create brush !" << endl;
629 	} else {
630 		oldColoredBrush = (HBRUSH) SelectObject(metaDC, coloredBrush);
631 	}
632 }
633 
634 
fetchFont(const TextInfo & textinfo,short int textHeight,short int textAngle)635 int drvWMF::fetchFont(const TextInfo & textinfo, short int textHeight, short int textAngle)
636 {
637 	LOGFONT			theFontRec;
638 	theFontRec.lfHeight = -textHeight ;
639 	theFontRec.lfWidth = 0;		// optimal fit
640 	theFontRec.lfEscapement = textAngle;
641 	theFontRec.lfOrientation = textAngle;
642 
643 	theFontRec.lfWeight = FW_DONTCARE;	// default: don't care
644 
645 	if (strstr(textinfo.currentFontWeight.c_str(), "Regular"))
646 		theFontRec.lfWeight = FW_NORMAL;	// other values don't work
647 
648 	if (strstr(textinfo.currentFontWeight.c_str(), "Medium"))
649 		theFontRec.lfWeight = FW_NORMAL;	// other values don't work
650 
651 	if (strstr(textinfo.currentFontWeight.c_str(), "Normal"))
652 		theFontRec.lfWeight = FW_NORMAL;
653 
654 	if (options->emulateNarrowFonts) {
655 		if (strstr(textinfo.currentFontWeight.c_str(), "Thin") ||
656 			strstr(textinfo.currentFontName.c_str(), "Thin") ||
657 			strstr(textinfo.currentFontFullName.c_str(), "Thin")) {
658 			theFontRec.lfWidth = textHeight / 3;	// narrow font emulation (trial and error value for Arial font)
659 		}
660 
661 		if (strstr(textinfo.currentFontWeight.c_str(), "Extralight") ||
662 			strstr(textinfo.currentFontName.c_str(), "Extralight") ||
663 			strstr(textinfo.currentFontFullName.c_str(), "Extralight")) {
664 			theFontRec.lfWidth = textHeight / 4;	// narrow font emulation (trial and error value for Arial font)
665 		}
666 
667 		if (strstr(textinfo.currentFontWeight.c_str(), "Ultralight") ||
668 			strstr(textinfo.currentFontName.c_str(), "Ultralight") ||
669 			strstr(textinfo.currentFontFullName.c_str(), "Ultralight")) {
670 			theFontRec.lfWidth = textHeight / 4;	// narrow font emulation (trial and error value for Arial font)
671 		}
672 
673 		if (strstr(textinfo.currentFontWeight.c_str(), "Light") ||
674 			strstr(textinfo.currentFontName.c_str(), "Light") ||
675 			strstr(textinfo.currentFontFullName.c_str(), "Light") ||
676 			strstr(textinfo.currentFontWeight.c_str(), "Condensed") ||
677 			strstr(textinfo.currentFontName.c_str(), "Condensed") ||
678 			strstr(textinfo.currentFontFullName.c_str(), "Condensed")) {
679 			theFontRec.lfWidth = textHeight / 3;	// narrow font emulation (trial and error value for Arial font)
680 		}
681 	}
682 
683 	if (strstr(textinfo.currentFontWeight.c_str(), "Semibold") ||
684 		strstr(textinfo.currentFontName.c_str(), "Semibold") ||
685 		strstr(textinfo.currentFontFullName.c_str(), "Semibold"))
686 		theFontRec.lfWeight = FW_BOLD;	// other values don't work
687 
688 	if (strstr(textinfo.currentFontWeight.c_str(), "Demibold") ||
689 		strstr(textinfo.currentFontName.c_str(), "Demibold") ||
690 		strstr(textinfo.currentFontFullName.c_str(), "Demibold"))
691 		theFontRec.lfWeight = FW_BOLD;	// other values don't work
692 
693 	if (strstr(textinfo.currentFontWeight.c_str(), "Bold") ||
694 		strstr(textinfo.currentFontName.c_str(), "Bold") ||
695 		strstr(textinfo.currentFontFullName.c_str(), "Bold"))
696 		theFontRec.lfWeight = FW_BOLD;
697 
698 	if (strstr(textinfo.currentFontWeight.c_str(), "Extrabold") ||
699 		strstr(textinfo.currentFontName.c_str(), "Extrabold") ||
700 		strstr(textinfo.currentFontFullName.c_str(), "Extrabold"))
701 		theFontRec.lfWeight = FW_BOLD;	// other values don't work
702 
703 	if (strstr(textinfo.currentFontWeight.c_str(), "Ultrabold") ||
704 		strstr(textinfo.currentFontName.c_str(), "Ultrabold") ||
705 		strstr(textinfo.currentFontFullName.c_str(), "Ultrabold"))
706 		theFontRec.lfWeight = FW_BOLD;	// other values don't work
707 
708 	if (strstr(textinfo.currentFontWeight.c_str(), "Heavy") ||
709 		strstr(textinfo.currentFontName.c_str(), "Heavy") ||
710 		strstr(textinfo.currentFontFullName.c_str(), "Heavy"))
711 		theFontRec.lfWeight = FW_BOLD;	// other values don't work
712 
713 	if (strstr(textinfo.currentFontWeight.c_str(), "Black") ||
714 		strstr(textinfo.currentFontName.c_str(), "Black") ||
715 		strstr(textinfo.currentFontFullName.c_str(), "Black"))
716 		theFontRec.lfWeight = FW_BOLD;	// other values don't work
717 
718 	if ((strstr(textinfo.currentFontName.c_str(), "Italic") != NIL) ||
719 		(strstr(textinfo.currentFontName.c_str(), "Oblique") != NIL) ||
720 		(strstr(textinfo.currentFontFullName.c_str(), "Italic") != NIL) ||
721 		(strstr(textinfo.currentFontFullName.c_str(), "Oblique") != NIL)) {
722 		theFontRec.lfItalic = TRUE;
723 	} else {
724 		theFontRec.lfItalic = 0;
725 	}
726 
727 	theFontRec.lfUnderline = 0;
728 	theFontRec.lfStrikeOut = 0;
729 	theFontRec.lfOutPrecision = OUT_DEFAULT_PRECIS;
730 	theFontRec.lfClipPrecision = CLIP_DEFAULT_PRECIS;
731 	theFontRec.lfQuality = PROOF_QUALITY;
732 	theFontRec.lfPitchAndFamily = VARIABLE_PITCH | FF_DONTCARE;	// let every font be possible
733 
734 	if ((strstr(textinfo.currentFontFullName.c_str(), "Symbol") != NIL) ||
735 		(strstr(textinfo.currentFontFullName.c_str(), "symbol") != NIL)) {
736 		theFontRec.lfCharSet = SYMBOL_CHARSET;
737 		strcpy_s(theFontRec.lfFaceName, LF_FACESIZE, "symbol");
738 	} else if ((strstr(textinfo.currentFontFamilyName.c_str(), "Computer Modern") != NIL) ) {
739 		// special handling for TeX Fonts - fix supplied by James F. O'Brien (job at cs.berkeley.edu)
740         theFontRec.lfWeight = FW_NORMAL;
741   	    theFontRec.lfItalic = 0;
742   	    theFontRec.lfUnderline = 0;
743 	    theFontRec.lfCharSet = ANSI_CHARSET;
744   	    strcpy_s(theFontRec.lfFaceName,LF_FACESIZE,  textinfo.currentFontName.c_str());
745 	} else 	{
746 		theFontRec.lfCharSet = ANSI_CHARSET;
747 
748 		if (options->mapToArial)
749 			strcpy_s(theFontRec.lfFaceName,LF_FACESIZE, "Arial");
750 		else /* formerly we used currentFontFamilyName but FontName seems to be better */
751 			strcpy_s(theFontRec.lfFaceName,LF_FACESIZE, textinfo.currentFontName.c_str());
752 	}
753 
754 	if (myFont) {
755 		SelectObject(metaDC, oldFont);
756 		DeleteObject(myFont);
757 		myFont = 0L;
758 	}
759 
760 	myFont = CreateFontIndirect(&theFontRec);
761 	if (!myFont) {
762 		errf << "ERROR: fetchFont: could not create font !" << endl;
763 	} else {
764 		oldFont = (HFONT) SelectObject(metaDC, myFont);
765 	}
766 
767 	return 0;
768 }
769 
770 
771 // fill point array and draw
drawPoly(polyType type)772 void drvWMF::drawPoly(polyType type)
773 {
774 	const unsigned int numOfElements = numberOfElementsInPath();
775 	// get us twice the number of elements in path,
776 	// as maybe every subpath is closed and consists of ONE point!
777 	POINT *aptlPoints = new POINT[2 * numOfElements];
778 	if (aptlPoints == 0) {
779 		errf << "ERROR: Cannot allocate memory for point-array" << endl;
780 		return;
781 	}
782 	// get us twice the number of elements in path,
783 	// as maybe every subpath is closed and consists of ONE point!
784 	int *aptlNumPts = new int[2 * numOfElements];
785 	if (aptlNumPts == 0) {
786 		errf << "ERROR: Cannot allocate memory for pointNum-array" << endl;
787 		delete [] aptlPoints;
788 		return;
789 	}
790 
791 	POINT lastStart = { 0, 0 };
792 	bool lastWasMoveTo = false;
793 	bool lastWasClosePath = false;
794 	unsigned int numOfPolies = 0;
795 	unsigned int numOfPts = 0;
796 	unsigned int p = 0;
797 
798 	for (unsigned int n = 0; n < numOfElements &&
799 		 p < 2 * numberOfElementsInPath() && numOfPolies < 2 * numOfElements; n++) {
800 		const basedrawingelement & elem = pathElement(n);
801 
802 		if (elem.getType() != closepath) {
803 			aptlPoints[p].x = transx(elem.getPoint(0).x_);
804 			aptlPoints[p].y = transy(elem.getPoint(0).y_);
805 		} else {
806 			aptlPoints[p].x = transx(0);
807 			aptlPoints[p].y = transy(0);
808 		}
809 
810 		if (elem.getType() == moveto || elem.getType() == lineto || elem.getType() == curveto) {
811 			const long lineWidth = (int) (currentLineWidth() + .5);	// rounded int
812 
813 			// set drawing sizes
814 			if (minStatus) {
815 				if (aptlPoints[p].x - lineWidth < minX)
816 					minX = aptlPoints[p].x - lineWidth;
817 
818 				if (aptlPoints[p].y - lineWidth < minY)
819 					minY = aptlPoints[p].y - lineWidth;
820 			} else {
821 				minX = aptlPoints[p].x - lineWidth;
822 				minY = aptlPoints[p].y - lineWidth;
823 				minStatus = 1;
824 			}
825 
826 			if (maxStatus) {
827 				if (aptlPoints[p].x + lineWidth > maxX)
828 					maxX = aptlPoints[p].x + lineWidth;
829 
830 				if (aptlPoints[p].y + lineWidth > maxY)
831 					maxY = aptlPoints[p].y + lineWidth;
832 			} else {
833 				maxX = aptlPoints[p].x + lineWidth;
834 				maxY = aptlPoints[p].y + lineWidth;
835 				maxStatus = true;
836 			}
837 		}
838 
839 		switch (elem.getType()) {
840 		case moveto:
841 			if (type == TYPE_LINES) {
842 				if (!MoveToEx(metaDC, aptlPoints[p].x, aptlPoints[p].y, NIL)) {
843 					errf << "ERROR: MoveTo: " << aptlPoints[p].x << "," << aptlPoints[p].y << endl;
844 				}
845 			} else {
846 				// ignore every path that is no area!
847 				if (numOfPts > 2) {
848 					// if path not closed -> do it manually!
849 					if (lastWasClosePath == FALSE) {
850 						if (p >= 2 * numberOfElementsInPath()) {
851 							errf << "ERROR: MoveTo: Out of array mem!" << endl;
852 						}
853 						// move this point one ahead
854 						aptlPoints[p + 1] = aptlPoints[p];
855 
856 						// insert line to startpoint
857 						aptlPoints[p] = lastStart;
858 
859 						p++;
860 						numOfPts++;
861 					}
862 					// store number of points for old subpath
863 					aptlNumPts[numOfPolies] = numOfPts;
864 
865 					// one polygon more (for PolyPolygon)
866 					numOfPolies++;
867 				} else if (numOfPts == 2) {
868 					// we have a line here, so draw it!
869 					if (!MoveToEx(metaDC, aptlPoints[p - 2].x, aptlPoints[p - 2].y, NIL)) {
870 						errf << "ERROR: MoveTo: " << aptlPoints[p -
871 																2].
872 							x << "," << aptlPoints[p - 2].y << endl;
873 					}
874 
875 					if (!LineTo(metaDC, aptlPoints[p - 1].x, aptlPoints[p - 1].y)) {
876 						errf << "ERROR: LineTo: " << aptlPoints[p -
877 																1].
878 							x << "," << aptlPoints[p - 1].y << endl;
879 					}
880 				}
881 				// all lower numbers do not represent a line/polygon. Ignore them
882 
883 				// start this subpath newly
884 				numOfPts = 0;
885 
886 				// set flag to remove lone moveto's
887 				lastWasMoveTo = true;
888 
889 				// clear flag to connect filled segments
890 				lastWasClosePath = true;
891 			}
892 
893 			// save last starting position in case we've to close path
894 			lastStart = aptlPoints[p];
895 
896 			break;
897 
898 		case curveto:
899 		case lineto:
900 			if (type == TYPE_LINES) {
901 				if (!LineTo(metaDC, aptlPoints[p].x, aptlPoints[p].y)) {
902 					errf << "ERROR: LineTo: " << aptlPoints[p].x << "," << aptlPoints[p].y << endl;
903 				}
904 			} else {
905 				// clear flag to remove lone moveto's
906 				lastWasMoveTo = false;
907 
908 				// clear flag to connect filled segments
909 				lastWasClosePath = false;
910 
911 				// do nothing, point is already in array
912 			}
913 			break;
914 
915 		case closepath:
916 			if (type == TYPE_LINES) {
917 				// close the thingy
918 				if (!LineTo(metaDC, lastStart.x, lastStart.y)) {
919 					errf << "ERROR: LineTo: " << lastStart.x << "," << lastStart.y << endl;
920 				}
921 			} else {
922 				// clear flag to remove lone moveto's
923 				lastWasMoveTo = false;
924 
925 				// set flag to connect filled segments
926 				lastWasClosePath = true;
927 
928 				// insert line to startpoint
929 				// (overwrite current point - closepath did not inserted points)
930 				aptlPoints[p] = lastStart;
931 			}
932 			break;
933 
934 		default:
935 			errf << "\t\tFatal: unexpected case in drvwmf (line " << __LINE__ << ")" << endl;
936 			abort();
937 			break;
938 		}
939 
940 		// next free point
941 		p++;
942 
943 		// next point in subpath
944 		numOfPts++;
945 	}
946 
947 	if (type != TYPE_LINES) {
948 		// remove last moveTo, as it's lonely
949 		if (lastWasMoveTo != FALSE)
950 			numOfPts--;
951 
952 		// ignore every path that is no area!
953 		if (numOfPts > 2) {
954 			// if path not closed -> do it manually!
955 			if (lastWasClosePath == FALSE) {
956 				if (p >= 2 * numberOfElementsInPath()) {
957 					errf << "ERROR: MoveTo: Out of array mem!" << endl;
958 				}
959 				// move this point one ahead
960 				aptlPoints[p + 1] = aptlPoints[p];
961 
962 				// insert line to startpoint
963 				aptlPoints[p] = lastStart;
964 
965 				p++;
966 				numOfPts++;
967 			}
968 			// store number of points for old subpath
969 			aptlNumPts[numOfPolies] = numOfPts;
970 
971 			// one polygon more (for PolyPolygon), as either we closed it above,
972 			// or closepath did it and started no new
973 			numOfPolies++;
974 		} else if (numOfPts == 2) {
975 			// we have a line here, so draw it!
976 			if (!MoveToEx(metaDC, aptlPoints[p - 2].x, aptlPoints[p - 2].y, NIL)) {
977 				errf << "ERROR: MoveTo: " << aptlPoints[p -
978 														2].x << "," << aptlPoints[p - 2].y << endl;
979 			}
980 
981 			if (!LineTo(metaDC, aptlPoints[p - 1].x, aptlPoints[p - 1].y)) {
982 				errf << "ERROR: LineTo: " << aptlPoints[p -
983 														1].x << "," << aptlPoints[p - 1].y << endl;
984 			}
985 		}
986 		// all lower numbers do not represent a line/polygon. Ignore them
987 
988 		// anything to do?
989 		if (numOfPolies > 0) {
990 			if (!SetPolyFillMode(metaDC, (currentShowType() == fill) ? WINDING : ALTERNATE)) {
991 				errf << "ERROR: could not set fill mode" << endl;
992 			}
993 
994 			if (!PolyPolygon(metaDC, aptlPoints, aptlNumPts, numOfPolies)) {
995 				DWORD ec = GetLastError();
996 				errf << "ERROR: Polygon could not be drawn: (" << ec << ")" << endl;
997 				errf << "Number of Points: " << p << endl;
998 				for (unsigned int i = 0; i < p; i++) {
999 					errf << aptlPoints[i].x << "," << aptlPoints[i].y << endl;
1000 				}
1001 			}
1002 		}
1003 	}
1004 	delete[]aptlPoints;
1005 	delete[]aptlNumPts;
1006 }
1007 
1008 
1009 
close_page()1010 void drvWMF::close_page()
1011 {
1012 	// no function in drvwmf
1013 }
1014 
1015 
open_page()1016 void drvWMF::open_page()
1017 {
1018 	// no function in drvwmf
1019 }
1020 
1021 
show_path()1022 void drvWMF::show_path()
1023 {
1024 	// update pen/brush
1025 	setDrawAttr();
1026 
1027 	// determine type: fill or line
1028 	switch (currentShowType()) {
1029 	case drvbase::stroke:
1030 		// draw lines
1031 		drawPoly(TYPE_LINES);
1032 		break;
1033 
1034 	case drvbase::fill:
1035 	case drvbase::eofill:
1036 		// draw filled polygon
1037 		drawPoly(TYPE_FILL);
1038 		break;
1039 
1040 	default:
1041 		// cannot happen
1042 		errf << "unexpected ShowType " << (int) currentShowType();
1043 		break;
1044 	}
1045 
1046 }
1047 
1048 
show_text(const TextInfo & textinfo)1049 void drvWMF::show_text(const TextInfo & textinfo)
1050 {
1051 	// set text color
1052 	SetTextColor(metaDC, RGB((BYTE) (textinfo.currentR * 255 + .5),
1053 							 (BYTE) (textinfo.currentG * 255 + .5),
1054 							 (BYTE) (textinfo.currentB * 255 + .5)));
1055 
1056 	const short int textHeight = (short int) (textinfo.currentFontSize*scale() + .5) ;	// rounded int
1057 	const short int textAngle = (short int) (10 * textinfo.currentFontAngle + .5);	// Windows needs 10th of degree
1058 
1059 	// any need to change font handle?
1060 	if (fontchanged())
1061 		(void)fetchFont(textinfo, textHeight, textAngle);
1062 
1063 	const long x1 = transx(textinfo.x);
1064 	const long y1 = transy(textinfo.y);
1065 
1066 	const long x2 = transx(textinfo.x_end);
1067 	const long y2 = transy(textinfo.y_end);
1068 
1069 	if (Verbose()) cout << "placing text : " << textinfo.thetext << " at " << textinfo.x << "," << textinfo.y<< " in EMF coords: " << x1 << "," << y1 << endl;
1070 	// calculate bounding box
1071 	//
1072 	// NOTE: cannot do that exactly (with ascent and descent height),
1073 	// because DC queries are not permitted for metafile DCs.
1074 	//
1075 	const int xOff = abs((int)
1076 						 (sin(textinfo.currentFontAngle * M_PI / 180) * textHeight + .5));
1077 	const int yOff = abs((int)
1078 						 (cos(textinfo.currentFontAngle * M_PI / 180) * textHeight + .5));
1079 
1080 	const int xMin = (int) min(x1 - xOff, x2 - xOff);
1081 	const int xMax = (int) max(x1 + xOff, x2 + xOff);
1082 	const int yMin = (int) min(y1 - yOff, y2 - yOff);
1083 	const int yMax = (int) max(y1 + yOff, y2 + yOff);
1084 
1085 	if (minStatus) {
1086 		if (xMin < minX)
1087 			minX = xMin;
1088 
1089 		if (yMin < minY)
1090 			minY = yMin;
1091 	} else {
1092 		minX = xMin;
1093 		minY = yMin;
1094 		minStatus = 1;
1095 	}
1096 
1097 	if (maxStatus) {
1098 		if (xMax > maxX)
1099 			maxX = xMax;
1100 
1101 		if (yMax > maxY)
1102 			maxY = yMax;
1103 	} else {
1104 		maxX = xMax;
1105 		maxY = yMax;
1106 		maxStatus = true;
1107 	}
1108 
1109 	size_t textLen = strlen(textinfo.thetext.c_str());
1110 	if (options->pruneLineEnds) {
1111 		/* check for '#' at lineend */
1112 		if (textLen > 0 && textinfo.thetext.c_str()[textLen - 1] == '#') {
1113 			/* write one character less */
1114 			textLen--;
1115 		}
1116 	}
1117 
1118 #if defined(_WIN32)
1119 	(void)TextOut(metaDC, x1, y1, textinfo.thetext.c_str(), (int) textLen);
1120 	// TextOut(metaDC, 10, 10, "hello",4);
1121 #else
1122 
1123 	if (options->notforWindows) {
1124 		// the user is aware of generating non-portable output
1125 		TextOut(metaDC, x1, y1, textinfo.thetext.c_str(), textLen);
1126 	} else {
1127 		// we are not running Windows - so we need an emulation - see note below
1128 		const long textdistance = (long) pythagoras((double)(x1-x2), (double)(y1-y2));
1129 		const int letterspace = (textLen > 1) ? (textdistance / (textLen-1)) : 0 ;
1130 		// if there is just one char in the text, then the inter letter spacing is 0 anyway
1131 		int * pxDistance = new int[textLen];
1132 		for (unsigned int letter = 0; letter < textLen; letter++) {
1133 			pxDistance[letter] = letterspace;
1134 		}
1135 		const UINT fuOptions = 0;
1136 		ExtTextOut (metaDC, x1, y1, fuOptions, 0, textinfo.thetext.c_str(), textLen, pxDistance);
1137 		delete [] pxDistance;
1138 
1139 		static bool warningwritten = false;
1140 		if (textLen > 1 && !warningwritten) {
1141 			warningwritten = true;
1142 			errf << "Warning: Inter letter spacing is approximated by pstoedit because of problems in libemf. Use -pta option if results are not OK." << endl;
1143 		}
1144 	}
1145 
1146 #endif
1147 
1148 #if 0
1149 
1150 ExtTextOut (hdc, xStart, yStart, iOptions, &rect, pString, iCount, pxDistance) ;
1151 The fifth argument is a pointer to a rectangle structure. This is either a
1152 clipping rectangle, (if iOptions is set to ETO_CLIPPED, or a background
1153 rectangle to be filled with the current background color,
1154 if iOptions is set to ETO_OPAQUE. You can specify both options or neither.
1155 
1156 The last argument is an array of integers that specify the spacing between
1157 consecutive characters in the string. This allows a program to tighten or
1158 loosen intercharacter spacing, which is sometimes required for justifying
1159 a single word of text in a narrow column.
1160 The argument can be set to NULL for default character spacing.
1161 
1162 Note by Glunz: Even if the spacing argument can be set to NULL according to
1163 the description above, this creates a problem when libemf is used.
1164 Libemf passes this NULL to the generated EMF file which then causes the
1165 file to be no longer usable under newer versions of Windows.
1166 
1167 It seems so that the Windows-API calculates this inter letter spacing automatically
1168 before writing the record to the EMF file. But this needs access to font information
1169 which is not easily done in libemf.
1170 Hence one work around is to assume a fixed width font and approximate the
1171 inter letter spacing by dividing the distance (P_end - P_start) by
1172 the number of characters in the string.
1173 
1174 #endif
1175 
1176 
1177 
1178 
1179 }
1180 
1181 
show_rectangle(const float llx,const float lly,const float urx,const float ury)1182 void drvWMF::show_rectangle(const float llx, const float lly, const float urx, const float ury)
1183 {
1184 	if (1) {
1185 		show_path();
1186 	} else {
1187 // this code is disabled - see note below.
1188 // needs to be fixed.
1189 
1190 	RECT localRect;
1191 
1192 	// update pen/brush
1193 	setDrawAttr();
1194 
1195 	localRect.left = transx(llx);
1196 	localRect.top = transy(lly);
1197 	localRect.right = transx(urx);
1198 	localRect.bottom = transy(ury);
1199 
1200 	// calculate bounding box
1201 	//
1202 	const int xMin = (int) min(localRect.left, localRect.right);
1203 	const int xMax = (int) max(localRect.left, localRect.right);
1204 	const int yMin = (int) min(localRect.top, localRect.bottom);
1205 	const int yMax = (int) max(localRect.top, localRect.bottom);
1206 
1207 	if (minStatus) {
1208 		if (xMin < minX)
1209 			minX = xMin;
1210 
1211 		if (yMin < minY)
1212 			minY = yMin;
1213 	} else {
1214 		minX = xMin;
1215 		minY = yMin;
1216 		minStatus = 1;
1217 	}
1218 
1219 	if (maxStatus) {
1220 		if (xMax > maxX)
1221 			maxX = xMax;
1222 
1223 		if (yMax > maxY)
1224 			maxY = yMax;
1225 	} else {
1226 		maxX = xMax;
1227 		maxY = yMax;
1228 		maxStatus = true;
1229 	}
1230 
1231 	if (0 && currentShowType() == drvbase::stroke) {
1232 		// wogl - this code is disabled. I don't know why this was this way.
1233 		// one cannot use a RECT as Point * and a polyline needs 4 points to make a RECT.
1234 		// strange ....
1235 		(void)Polyline(metaDC, (POINT *) & localRect, 2);
1236 		// but also using a Rectangle isn't correct.
1237 	} else {
1238 		(void)Rectangle(metaDC, transx(llx), transy(lly), transx(urx), transy(ury));
1239 	}
1240 	}
1241 }
1242 
1243 
show_image(const PSImage & image)1244 void drvWMF::show_image(const PSImage & image)
1245 {
1246 	// first retrieve bounding box
1247 	Point lowerLeft, upperRight;
1248 	image.getBoundingBox(lowerLeft, upperRight);
1249 
1250 	// not only bounding box must account for scale,
1251 	// but also transformation matrix!
1252 
1253 	// scale bounding box
1254 	lowerLeft.x_ *= getScale();
1255 	lowerLeft.y_ *= getScale();
1256 	upperRight.x_ *= getScale();
1257 	upperRight.y_ *= getScale();
1258 
1259 	const long width  = abs(i_transX(upperRight.x_) - i_transX(lowerLeft.x_));
1260 	const long height = abs(i_transY(upperRight.y_) - i_transY(lowerLeft.y_));
1261 
1262 	if (Verbose()) {
1263 		errf << "image.Width:" << image.width << " image.Height: " << image.height << endl;
1264 		errf << "Width:" << width << " Height: " << height << endl;
1265 	}
1266 	// calculate bounding box
1267 	//
1268 	const int xMin = (int) min(transx(upperRight.x_), transx(lowerLeft.x_));
1269 	const int xMax = (int) max(transx(upperRight.x_), transx(lowerLeft.x_));
1270 	const int yMin = (int) min(transy(upperRight.y_), transy(lowerLeft.y_));
1271 	const int yMax = (int) max(transy(upperRight.y_), transy(lowerLeft.y_));
1272 
1273 	if (minStatus) {
1274 		if (xMin < minX)
1275 			minX = xMin;
1276 
1277 		if (yMin < minY)
1278 			minY = yMin;
1279 	} else {
1280 		minX = xMin;
1281 		minY = yMin;
1282 		minStatus = 1;
1283 	}
1284 
1285 	if (maxStatus) {
1286 		if (xMax > maxX)
1287 			maxX = xMax;
1288 
1289 		if (yMax > maxY)
1290 			maxY = yMax;
1291 	} else {
1292 		maxX = xMax;
1293 		maxY = yMax;
1294 		maxStatus = true;
1295 	}
1296 
1297 	// calc long-padded size of scanline
1298 	const long scanlineLen = ((width * 3) + 3) & ~3L;
1299 
1300 	// now lets get some mem
1301 	unsigned char *const output = new unsigned char[scanlineLen * height];
1302 
1303 	for (long i = 0; i < scanlineLen * height; i++)
1304 		output[i] = 255;		// default is background (white)
1305 
1306 	if (!output) {
1307 		errf << "ERROR: Cannot allocate memory for image" << endl;
1308 		return;
1309 	}
1310 	// setup inverse transformation matrix (scaled, too!)
1311 	const float matrixScale(image.normalizedImageCurrentMatrix[0] *
1312 							image.normalizedImageCurrentMatrix[3] -
1313 							image.normalizedImageCurrentMatrix[2] *
1314 							image.normalizedImageCurrentMatrix[1]);
1315 	const float inverseMatrix[] = {
1316 		image.normalizedImageCurrentMatrix[3] / matrixScale / getScale(),
1317 		-image.normalizedImageCurrentMatrix[1] / matrixScale / getScale(),
1318 		-image.normalizedImageCurrentMatrix[2] / matrixScale / getScale(),
1319 		image.normalizedImageCurrentMatrix[0] / matrixScale / getScale(),
1320 		(image.normalizedImageCurrentMatrix[2] *
1321 		 image.normalizedImageCurrentMatrix[5] -
1322 		 image.normalizedImageCurrentMatrix[4] *
1323 		 image.normalizedImageCurrentMatrix[3]) / matrixScale,
1324 		(image.normalizedImageCurrentMatrix[4] *
1325 		 image.normalizedImageCurrentMatrix[1] -
1326 		 image.normalizedImageCurrentMatrix[0] *
1327 		 image.normalizedImageCurrentMatrix[5]) / matrixScale
1328 	};
1329 
1330 	// now transform image
1331 	for (long y = 0; y < height; y++) {
1332 		// buffer current output scanline (saves us some multiplications)
1333 		unsigned char *const currOutput = &output[scanlineLen * y];
1334 
1335 		for (long x = 0; x < width; x++) {
1336 			// now transform from device coordinate space to image space
1337 
1338 			// apply transformation
1339 			const Point currPoint = Point(x + lowerLeft.x_,
1340 										  y + lowerLeft.y_).transform(inverseMatrix);
1341 
1342 			// round to integers
1343 			const long sourceX = (long) (currPoint.x_ + .5);
1344 			const long sourceY = (long) (currPoint.y_ + .5);
1345 
1346 			// is the pixel out of bounds? If yes, no further processing necessary
1347 			if (sourceX >= 0L && (unsigned long) sourceX < image.width &&
1348 				sourceY >= 0L && (unsigned long) sourceY < image.height) {
1349 				// okay, fetch source pixel value into
1350 				// RGB triplet
1351 
1352 				unsigned char r(255), g(255), b(255), C, M, Y, K;
1353 
1354 				// how many components?
1355 				switch (image.ncomp) {
1356 				case 1:
1357 					r = g = b = image.getComponent(sourceX, sourceY, 0);
1358 					break;
1359 
1360 				case 3:
1361 					r = image.getComponent(sourceX, sourceY, 0);
1362 					g = image.getComponent(sourceX, sourceY, 1);
1363 					b = image.getComponent(sourceX, sourceY, 2);
1364 					break;
1365 
1366 				case 4:
1367 					C = image.getComponent(sourceX, sourceY, 0);
1368 					M = image.getComponent(sourceX, sourceY, 1);
1369 					Y = image.getComponent(sourceX, sourceY, 2);
1370 					K = image.getComponent(sourceX, sourceY, 3);
1371 
1372 					// account for key
1373 					C += K;
1374 					M += K;
1375 					Y += K;
1376 
1377 					// convert color
1378 					r = 255 - C;
1379 					g = 255 - M;
1380 					b = 255 - Y;
1381 					break;
1382 
1383 				default:
1384 					errf << "\t\tFatal: unexpected case in drvwmf (line "
1385 						<< __LINE__ << ")" << endl;
1386 					delete [] output; // to make FlexeLint happier
1387 					abort();
1388 					return;
1389 				}
1390 
1391 				// set color triple
1392 				currOutput[3 * x] = b;
1393 				currOutput[3 * x + 1] = g;
1394 				currOutput[3 * x + 2] = r;
1395 			}
1396 		}
1397 	}
1398 
1399 	// draw Windows DI bitmap
1400 	BITMAPINFO bmi;
1401 
1402 			/* setup BITMAPINFO */
1403 		bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1404 		bmi.bmiHeader.biWidth = width;
1405 		bmi.bmiHeader.biHeight = height;
1406 		bmi.bmiHeader.biPlanes = 1;
1407 		bmi.bmiHeader.biBitCount = 24;	// always truecolor output
1408 		bmi.bmiHeader.biClrUsed = 0;
1409 		bmi.bmiHeader.biCompression = BI_RGB;
1410 		bmi.bmiHeader.biSizeImage = 0;	/* size not important */
1411 		bmi.bmiHeader.biXPelsPerMeter = 0;	/* size not important */
1412 		bmi.bmiHeader.biYPelsPerMeter = 0;	/* size not important */
1413 		bmi.bmiHeader.biClrImportant = 0;
1414 
1415 		if (!SetDIBitsToDevice(metaDC,
1416 							  transx(lowerLeft.x_),
1417 							  transy(upperRight.y_),
1418 							  width, height, 0, 0, 0, height, output, &bmi, DIB_RGB_COLORS)) {
1419 			errf << "ERROR: Cannot draw bitmap" << endl;
1420 		}
1421 	delete[]output;
1422 }
1423 
1424 
1425 
1426 #if defined(_WIN32)
1427 //
1428 // Wmf works only under Windows since the libemf has only a broken emulation of wmf.
1429 // Under libemf, a createMetaFile effectively creates an enhMetaFile but that confuses almost all
1430 // programs which expect to read an WMF with an aldus placeable header
1431 //
1432 static DriverDescriptionT < drvWMF > D_wmf("wmf", "MS Windows Metafile", "","wmf", true,	// backend supports subpathes
1433 										   // if subpathes are supported, the backend must deal with
1434 										   // sequences of the following form
1435 										   // moveto (start of subpath)
1436 										   // lineto (a line segment)
1437 										   // lineto
1438 										   // moveto (start of a new subpath)
1439 										   // lineto (a line segment)
1440 										   // lineto
1441 										   //
1442 										   // If this argument is set to false each subpath is drawn
1443 										   // individually which might not necessarily represent
1444 										   // the original drawing.
1445 										   false,	// backend does not support curves (at least for WMF - have to take least common denominator here)
1446 										   true,	// backend supports elements which are filled and have edges
1447 										   true,	// backend supports text
1448 										   DriverDescription::memoryeps,	// no support for PNG file images
1449 										   DriverDescription::noopen,	// we open output file ourselves
1450 										   false,	// if format supports multiple pages in one file (DEFINETELY not)
1451 										   false  /*clipping */
1452 										   );
1453 #endif
1454 
1455 static DriverDescriptionT < drvWMF > D_emf("emf", "Enhanced MS Windows Metafile", "","emf", true,	// backend supports subpathes
1456 										   // if subpathes are supported, the backend must deal with
1457 										   // sequences of the following form
1458 										   // moveto (start of subpath)
1459 										   // lineto (a line segment)
1460 										   // lineto
1461 										   // moveto (start of a new subpath)
1462 										   // lineto (a line segment)
1463 										   // lineto
1464 										   //
1465 										   // If this argument is set to false each subpath is drawn
1466 										   // individually which might not necessarily represent
1467 										   // the original drawing.
1468 										   false,	// backend does not support curves (not yet)
1469 										   true,	// backend supports elements which are filled and have edges
1470 										   true,	// backend supports text
1471 										   DriverDescription::memoryeps,	// no support for PNG file images
1472 										   DriverDescription::noopen,	// we open output file ourselves
1473 										   false,	// if format supports multiple pages in one file (DEFINETELY not)
1474 										   false  /*clipping */
1475 										   );
1476 
1477