2 *********************************************************************
\r
4 * Purpose: Windows Metafile Class Loader and Writer
\r
5 * Author: Volker Horch - vhorch@gmx.de
\r
6 * created: 13-Jun-2002
\r
8 * Note: If the code below works, i wrote it.
\r
9 * If it doesn't work, i don't know who wrote it.
\r
10 *********************************************************************
\r
14 *********************************************************************
\r
16 *********************************************************************
\r
21 There are 2 kinds of Windows Metafiles:
\r
22 - Standard Windows Metafile
\r
23 - Placeable Windows Metafile
\r
25 A StandardWindows Metafile looks like:
\r
26 - Metafile Header (MEATAHEADER)
\r
29 A Placeable Metafile looks like:
\r
30 - Aldus Header (METAFILEHEADER)
\r
31 - Metafile Header (METAHEADER)
\r
34 The "Metafile Header" and the "Metafile Records" are the same
\r
35 for both formats. However, the Standard Metafile does not contain any
\r
36 information about the original dimensions or x/y ratio of the Metafile.
\r
38 I decided, to allow only placeable Metafiles here. If you also want to
\r
39 enable Standard Metafiles, you will have to guess the dimensions of
\r
42 *********************************************************************
\r
43 Limitations: see ximawmf.h
\r
44 you may configure some stuff there
\r
45 *********************************************************************
\r
48 #include "ximawmf.h"
\r
50 #if CXIMAGE_SUPPORT_WMF && CXIMAGE_SUPPORT_WINDOWS
\r
52 ////////////////////////////////////////////////////////////////////////////////
\r
53 #if CXIMAGE_SUPPORT_DECODE
\r
54 ////////////////////////////////////////////////////////////////////////////////
\r
55 bool CxImageWMF::Decode(CxFile *hFile, long nForceWidth, long nForceHeight)
\r
57 if (hFile == NULL) return false;
\r
63 //save the current position of the file
\r
64 long pos = hFile->Tell();
\r
66 // Read the Metafile and convert to an Enhanced Metafile
\r
68 hMeta = ConvertWmfFiletoEmf(hFile, &mfh);
\r
69 if (hMeta) { // ok, it's a WMF
\r
71 /////////////////////////////////////////////////////////////////////
\r
72 // We use the original WMF size information, because conversion to
\r
73 // EMF adjusts the Metafile to Full Screen or does not set rclBounds at all
\r
74 // ENHMETAHEADER emh;
\r
76 // uRet = GetEnhMetaFileHeader(hMeta, // handle of enhanced metafile
\r
77 // sizeof(ENHMETAHEADER), // size of buffer, in bytes
\r
78 // &emh); // address of buffer to receive data
\r
80 // DeleteEnhMetaFile(hMeta);
\r
83 // // calculate size
\r
84 // cx = emh.rclBounds.right - emh.rclBounds.left;
\r
85 // cy = emh.rclBounds.bottom - emh.rclBounds.top;
\r
86 /////////////////////////////////////////////////////////////////////
\r
89 // scale the metafile (pixels/inch of metafile => pixels/inch of display)
\r
90 // mfh.inch already checked to be <> 0
\r
93 int cx1 = ::GetDeviceCaps(hDC, LOGPIXELSX);
\r
94 int cy1 = ::GetDeviceCaps(hDC, LOGPIXELSY);
\r
95 ::ReleaseDC(0, hDC);
\r
97 cx = (mfh.inch/2 + (mfh.bbox.right - mfh.bbox.left) * cx1) / mfh.inch;
\r
98 cy = (mfh.inch/2 + (mfh.bbox.bottom - mfh.bbox.top) * cy1) / mfh.inch;
\r
100 } else { // maybe it's an EMF...
\r
102 hFile->Seek(pos,SEEK_SET);
\r
105 hMeta = ConvertEmfFiletoEmf(hFile, &emh);
\r
108 strcpy(info.szLastError,"corrupted WMF");
\r
109 return false; // definitively give up
\r
112 // ok, it's an EMF; calculate canvas size
\r
113 cx = emh.rclBounds.right - emh.rclBounds.left;
\r
114 cy = emh.rclBounds.bottom - emh.rclBounds.top;
\r
116 // alternative methods, sometime not so reliable... [DP]
\r
117 //cx = emh.szlDevice.cx;
\r
118 //cy = emh.szlDevice.cy;
\r
120 //hDC = ::GetDC(0);
\r
121 //float hscale = (float)GetDeviceCaps(hDC, HORZRES)/(100.0f * GetDeviceCaps(hDC, HORZSIZE));
\r
122 //float vscale = (float)GetDeviceCaps(hDC, VERTRES)/(100.0f * GetDeviceCaps(hDC, VERTSIZE));
\r
123 //::ReleaseDC(0, hDC);
\r
124 //cx = (long)((emh.rclFrame.right - emh.rclFrame.left) * hscale);
\r
125 //cy = (long)((emh.rclFrame.bottom - emh.rclFrame.top) * vscale);
\r
128 if (info.nEscape == -1) { // Check if cancelled
\r
131 info.dwType = CXIMAGE_FORMAT_WMF;
\r
132 DeleteEnhMetaFile(hMeta);
\r
133 strcpy(info.szLastError,"output dimensions returned");
\r
138 DeleteEnhMetaFile(hMeta);
\r
139 strcpy(info.szLastError,"empty WMF");
\r
143 if (nForceWidth) cx=nForceWidth;
\r
144 if (nForceHeight) cy=nForceHeight;
\r
145 ShrinkMetafile(cx, cy); // !! Otherwise Bitmap may have bombastic size
\r
147 HDC hDC0 = ::GetDC(0); // DC of screen
\r
148 HBITMAP hBitmap = CreateCompatibleBitmap(hDC0, cx, cy); // has # colors of display
\r
149 hDC = CreateCompatibleDC(hDC0); // memory dc compatible with screen
\r
150 ::ReleaseDC(0, hDC0); // don't need anymore. get rid of it.
\r
154 RECT rc = {0,0,cx,cy};
\r
155 int bpp = ::GetDeviceCaps(hDC, BITSPIXEL);
\r
157 HBITMAP hBitmapOld = (HBITMAP)SelectObject(hDC, hBitmap);
\r
159 // clear out the entire bitmap with windows background
\r
160 // because the MetaFile may not contain background information
\r
161 DWORD dwBack = XMF_COLOR_BACK;
\r
162 #if XMF_SUPPORT_TRANSPARENCY
\r
163 if (bpp == 24) dwBack = XMF_COLOR_TRANSPARENT;
\r
165 DWORD OldColor = SetBkColor(hDC, dwBack);
\r
166 ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
\r
167 SetBkColor(hDC, OldColor);
\r
169 //retrieves optional palette entries from the specified enhanced metafile
\r
170 PLOGPALETTE plogPal;
\r
173 int iEntries = GetEnhMetaFilePaletteEntries(hMeta, 0, NULL);
\r
175 if ((plogPal = (PLOGPALETTE)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
\r
176 sizeof(DWORD) + sizeof(PALETTEENTRY)*iEntries )) == NULL) {
\r
177 DeleteObject(hBitmap);
\r
179 DeleteEnhMetaFile(hMeta);
\r
180 strcpy(info.szLastError,"Cancelled");
\r
184 plogPal->palVersion = 0x300;
\r
185 plogPal->palNumEntries = (WORD) iEntries;
\r
186 pjTmp = (PBYTE) plogPal;
\r
189 GetEnhMetaFilePaletteEntries(hMeta, iEntries, (PPALETTEENTRY)pjTmp);
\r
190 hPal = CreatePalette(plogPal);
\r
191 GlobalFree(plogPal);
\r
193 SelectPalette(hDC, hPal, FALSE);
\r
194 RealizePalette(hDC);
\r
197 // Play the Metafile into Memory DC
\r
198 BOOL bRet = PlayEnhMetaFile(hDC, // handle to a device context
\r
199 hMeta, // handle to an enhanced metafile
\r
200 &rc); // pointer to bounding rectangle
\r
202 SelectObject(hDC, hBitmapOld);
\r
203 DeleteEnhMetaFile(hMeta); // we are done with this one
\r
205 if (info.nEscape) { // Check if cancelled
\r
206 DeleteObject(hBitmap);
\r
208 strcpy(info.szLastError,"Cancelled");
\r
212 // the Bitmap now has the image.
\r
213 // Create our DIB and convert the DDB into DIB
\r
214 if (!Create(cx, cy, bpp, CXIMAGE_FORMAT_WMF)) {
\r
215 DeleteObject(hBitmap);
\r
220 #if XMF_SUPPORT_TRANSPARENCY
\r
222 RGBQUAD rgbTrans = { XMF_RGBQUAD_TRANSPARENT };
\r
223 SetTransColor(rgbTrans);
\r
226 // We're finally ready to get the DIB. Call the driver and let
\r
227 // it party on our bitmap. It will fill in the color table,
\r
228 // and bitmap bits of our global memory block.
\r
229 bRet = GetDIBits(hDC, hBitmap, 0,
\r
230 (UINT)cy, GetBits(), (LPBITMAPINFO)pDib, DIB_RGB_COLORS);
\r
232 DeleteObject(hBitmap);
\r
240 if (hBitmap) DeleteObject(hBitmap);
\r
243 DeleteEnhMetaFile(hMeta);
\r
248 /**********************************************************************
\r
249 Function: CheckMetafileHeader
\r
250 Purpose: Check if the Metafileheader of a file is valid
\r
251 **********************************************************************/
\r
252 BOOL CxImageWMF::CheckMetafileHeader(METAFILEHEADER *metafileheader)
\r
259 if (metafileheader->key != 0x9ac6cdd7L) return false;
\r
261 // test checksum of header
\r
262 pw = (WORD *)metafileheader;
\r
265 for (i = 0; i < 9; i++) {
\r
270 if (cs != metafileheader->checksum) return false;
\r
272 // check resolution
\r
273 if ((metafileheader->inch <= 0) || (metafileheader->inch > 2540)) return false;
\r
278 /**********************************************************************
\r
279 Function: ConvertWmfFiletoEmf
\r
280 Purpose: Converts a Windows Metafile into an Enhanced Metafile
\r
281 **********************************************************************/
\r
282 HENHMETAFILE CxImageWMF::ConvertWmfFiletoEmf(CxFile *fp, METAFILEHEADER *metafileheader)
\r
284 HENHMETAFILE hMeta;
\r
288 METAHEADER mfHeader;
\r
293 // get length of the file
\r
294 lenFile = fp->Size();
\r
296 // a placeable metafile starts with a METAFILEHEADER
\r
297 // read it and check metafileheader
\r
298 len = fp->Read(metafileheader, 1, sizeof(METAFILEHEADER));
\r
299 if (len < sizeof(METAFILEHEADER)) return (hMeta);
\r
301 if (CheckMetafileHeader(metafileheader)) {
\r
302 // This is a placeable metafile
\r
303 // Convert the placeable format into something that can
\r
304 // be used with GDI metafile functions
\r
305 seekpos = sizeof(METAFILEHEADER);
\r
307 // Not a placeable wmf. A windows metafile?
\r
308 // at least not scaleable.
\r
309 // we could try to convert, but would loose ratio. don't allow this
\r
312 //metafileheader->bbox.right = ?;
\r
313 //metafileheader->bbox.left = ?;
\r
314 //metafileheader->bbox.bottom = ?;
\r
315 //metafileheader->bbox.top = ?;
\r
316 //metafileheader->inch = ?;
\r
319 // fp->Seek(0, SEEK_SET); // rewind
\r
322 // At this point we have a metaheader regardless of whether
\r
323 // the metafile was a windows metafile or a placeable metafile
\r
324 // so check to see if it is valid. There is really no good
\r
325 // way to do this so just make sure that the mtType is either
\r
326 // 1 or 2 (memory or disk file)
\r
327 // in addition we compare the length of the METAHEADER against
\r
328 // the length of the file. if filelength < len => no Metafile
\r
330 len = fp->Read(&mfHeader, 1, sizeof(METAHEADER));
\r
331 if (len < sizeof(METAHEADER)) return (hMeta);
\r
333 if ((mfHeader.mtType != 1) && (mfHeader.mtType != 2)) return (hMeta);
\r
335 // Length in Bytes from METAHEADER
\r
336 len = mfHeader.mtSize * 2;
\r
337 if (len > lenFile) return (hMeta);
\r
339 // Allocate memory for the metafile bits
\r
340 p = (BYTE *)malloc(len);
\r
341 if (!p) return (hMeta);
\r
343 // seek back to METAHEADER and read all the stuff at once
\r
344 fp->Seek(seekpos, SEEK_SET);
\r
345 lenFile = fp->Read(p, 1, len);
\r
346 if (lenFile != len) {
\r
351 // the following (commented code) works, but adjusts rclBound of the
\r
352 // Enhanced Metafile to full screen.
\r
353 // the METAFILEHEADER from above is needed to scale the image
\r
355 // hMeta = SetWinMetaFileBits(len, p, NULL, NULL);
\r
357 // scale the metafile (pixels/inch of metafile => pixels/inch of display)
\r
364 cx1 = ::GetDeviceCaps(hDC, LOGPIXELSX);
\r
365 cy1 = ::GetDeviceCaps(hDC, LOGPIXELSY);
\r
367 memset(&mfp, 0, sizeof(mfp));
\r
369 mfp.mm = MM_ANISOTROPIC;
\r
370 mfp.xExt = 10000; //(metafileheader->bbox.right - metafileheader->bbox.left) * cx1 / metafileheader->inch;
\r
371 mfp.yExt = 10000; //(metafileheader->bbox.bottom - metafileheader->bbox.top) * cy1 / metafileheader->inch;
\r
374 // in MM_ANISOTROPIC mode xExt and yExt are in MM_HIENGLISH
\r
375 // MM_HIENGLISH means: Each logical unit is converted to 0.001 inch
\r
376 //mfp.xExt *= 1000;
\r
377 //mfp.yExt *= 1000;
\r
379 //int k = 332800 / ::GetSystemMetrics(SM_CXSCREEN);
\r
380 //mfp.xExt *= k; mfp.yExt *= k;
\r
383 while ((mfp.xExt < 6554) && (mfp.yExt < 6554))
\r
389 hMeta = SetWinMetaFileBits(len, p, hDC, &mfp);
\r
391 if (!hMeta){ //try 2nd conversion using a different mapping
\r
393 hMeta = SetWinMetaFileBits(len, p, hDC, &mfp);
\r
396 ::ReleaseDC(0, hDC);
\r
403 /////////////////////////////////////////////////////////////////////
\r
404 HENHMETAFILE CxImageWMF::ConvertEmfFiletoEmf(CxFile *pFile, ENHMETAHEADER *pemfh)
\r
406 HENHMETAFILE hMeta;
\r
407 long iLen = pFile->Size();
\r
409 // Check the header first: <km>
\r
410 long pos = pFile->Tell();
\r
411 long iLenRead = pFile->Read(pemfh, 1, sizeof(ENHMETAHEADER));
\r
412 if (iLenRead < sizeof(ENHMETAHEADER)) return NULL;
\r
413 if (pemfh->iType != EMR_HEADER) return NULL;
\r
414 if (pemfh->dSignature != ENHMETA_SIGNATURE) return NULL;
\r
415 //if (pemfh->nBytes != (DWORD)iLen) return NULL;
\r
416 pFile->Seek(pos,SEEK_SET);
\r
418 BYTE* pBuff = (BYTE *)malloc(iLen);
\r
419 if (!pBuff) return (FALSE);
\r
421 // Read the Enhanced Metafile
\r
422 iLenRead = pFile->Read(pBuff, 1, iLen);
\r
423 if (iLenRead != iLen) {
\r
428 // Make it a Memory Metafile
\r
429 hMeta = SetEnhMetaFileBits(iLen, pBuff);
\r
431 free(pBuff); // finished with this one
\r
433 if (!hMeta) return NULL; // oops.
\r
435 // Get the Enhanced Metafile Header
\r
436 UINT uRet = GetEnhMetaFileHeader(hMeta, // handle of enhanced metafile
\r
437 sizeof(ENHMETAHEADER), // size of buffer, in bytes
\r
438 pemfh); // address of buffer to receive data
\r
441 DeleteEnhMetaFile(hMeta);
\r
447 ////////////////////////////////////////////////////////////////////////////////
\r
448 #endif //CXIMAGE_SUPPORT_DECODE
\r
449 ////////////////////////////////////////////////////////////////////////////////
\r
450 #if CXIMAGE_SUPPORT_ENCODE
\r
451 /////////////////////////////////////////////////////////////////////
\r
452 bool CxImageWMF::Encode(CxFile * hFile)
\r
454 if (hFile == NULL) return false;
\r
455 strcpy(info.szLastError, "Save WMF not supported");
\r
458 #endif // CXIMAGE_SUPPORT_ENCODE
\r
459 /////////////////////////////////////////////////////////////////////
\r
461 /**********************************************************************
\r
462 Function: ShrinkMetafile
\r
463 Purpose: Shrink the size of a metafile to be not larger than
\r
465 **********************************************************************/
\r
466 void CxImageWMF::ShrinkMetafile(int &cx, int &cy)
\r
468 int xScreen = XMF_MAXSIZE_CX;
\r
469 int yScreen = XMF_MAXSIZE_CY;
\r
472 cy = cy * xScreen / cx;
\r
477 cx = cx * yScreen / cy;
\r
482 #endif // CIMAGE_SUPPORT_WMF
\r