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