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