Search code examples
c++winapigdi+metafilewmf

Convert wmf file to emf file with SetWinMetaFileBits api in c++


I am trying to convert a wmf file to emf file. From what I've learned on Internet, the best solution looks like this.

BYTE* buffer;

HDC hdc = CreateMetaFileA(filename);

HMETAFILE hmf = CloseMetaFile(hdc);

UINT metasize = GetMetaFileBitsEx(hmf, 0, NULL);

buffer = (BYTE*)malloc(metasize);

HENHMETAFILE hEMF = SetWinMetaFileBits(metasize, buffer, NULL, NULL);

The idea here is to use CreateMetaFileA and CloseMetaFile to get HMETAFILE hmf. Then I tried my code and the weird thing came. The handle hmf always points ??? in memory and the metasize is always 24 with different pictures. And hEMF is always None.

This is really sad because I spend my whole night on figuring out how to make the code work.

I do read a lot of materials including

http://math2.org/luasearch-2/luadist-extract/cdlua-5.2.dist/src/win32/wmf_emf.c https://www-user.tu-chemnitz.de/~heha/viewzip.cgi/hs/wmfsave.zip/src/wmfsave.cpp?auto=CPP

Can anyone help me here? Thanks.


Solution

  • You need to initialize the METAFILEPICT structure.

    Minimal example:

    if (hmf) {
                DWORD nSize = GetMetaFileBitsEx( hmf, 0, NULL );
                if (nSize) {
                    BYTE *lpvData = new BYTE[nSize];
                    if (lpvData) {
                        DWORD dw = GetMetaFileBitsEx( hmf, nSize, lpvData );
                        if (dw) {
                            // Fill out a METAFILEPICT structure
                            mp.mm = MM_ANISOTROPIC;
                            mp.xExt = 1000;
                            mp.yExt = 1000;
                            mp.hMF = NULL;
                            // Get a reference DC
                            hDC = GetDC( NULL );
                            // Make an enhanced metafile from the windows metafile
                            hemf = SetWinMetaFileBits( nSize, lpvData, hDC, &mp );
                            // Clean up
                            ReleaseDC( NULL, hDC );
                        }
                        delete[] lpvData;
                    }
                    DeleteMetaFile( hmf );
                }     
    

    My test code:

        hdcMeta = CreateMetaFile(NULL);
        hBrush = CreateSolidBrush(RGB(0, 0, 255));
    
        Rectangle(hdcMeta, 0, 0, 100, 100);
    
        MoveToEx(hdcMeta, 0, 0, NULL);
        LineTo(hdcMeta, 100, 100);
        MoveToEx(hdcMeta, 0, 100, NULL);
        LineTo(hdcMeta, 100, 0);
    
        SelectObject(hdcMeta, hBrush);
        Ellipse(hdcMeta, 20, 20, 80, 80);
    
        hmf = CloseMetaFile(hdcMeta);
        UINT nSize = GetMetaFileBitsEx(hmf, 0, NULL);
    

    Debug:

    1

    You can see nSize = 114

    I suspect that you use CreateMetaFileA and CloseMetaFile to directly load the file name and return a handle to a Windows-format metafile is a wrong way.

    Updated:

    You can get the handle of WMF file in another way.

    #include <windows.h>
    #include <iostream>
    #include <vector>
    
    #pragma pack(1)
    typedef struct tagWIN16RECT
    {
        WORD left;
        WORD top;
        WORD right;
        WORD bottom;
    } WIN16RECT;
    
    typedef struct tagPLACEABLEMETAHEADER
    {
        DWORD key;
        WORD hmf;
        WIN16RECT bbox;
        WORD inch;
    
        DWORD reserved;
        WORD checksum;
    } PLACEABLEMETAHEADER;
    #pragma pack()
    
    
    
    HENHMETAFILE  WINAPI ConvertWMFToEWMF(IN LPCWSTR lpszMetaFile)
    {
        HANDLE hFile = ::CreateFileW(
            lpszMetaFile,
            GENERIC_READ,
            0,
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL);
    
        if (hFile == NULL || hFile == INVALID_HANDLE_VALUE)
            return NULL;
    
        DWORD dwSize = ::GetFileSize(hFile, NULL);
        std::vector<BYTE> data(dwSize);
    
        DWORD dwRead;
        BOOL bSuccess = ::ReadFile(hFile, &data[0], dwSize, &dwRead, NULL);
        ::CloseHandle(hFile);
    
        HENHMETAFILE hEnhMetaFile = NULL;
    
        if (bSuccess)
        {
            PLACEABLEMETAHEADER * hdr = (PLACEABLEMETAHEADER*)&data[0];
            int iPlaceableHeaderSize = sizeof(PLACEABLEMETAHEADER);
    
            int iOffset = 0;
            if (hdr->key != 0x9AC6CDD7)  //not placeable header
            {
                iOffset = 0;  //offset remains zero
            }
            else
            {
                iOffset = iPlaceableHeaderSize; //file is offset with placeable windows metafile header
            }
    
            hEnhMetaFile = ::SetWinMetaFileBits(data.size(), &data[iOffset], NULL, NULL);
            if (NULL == hEnhMetaFile)
            {
                DWORD dwError = GetLastError();
                std::cout << "Failed with error code: " << dwError;
            }
            else
            {
                std::cout << "Success!  Metafile opened  and returned as enhanced metafile";
            }
        }
    
        return hEnhMetaFile;
    }
    
    int main()
    {
    
        HENHMETAFILE hEMF = ConvertWMFToEWMF(L"C:\\Users\\strives\\Desktop\\AN00010.WMF");
    
        HENHMETAFILE newHEMF = CopyEnhMetaFile(hEMF, L"new EMF.emf");
    
        return 0;
    }