1

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.

Xudong Shao
  • 199
  • 3
  • 12

2 Answers2

2

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;
}
Strive Sun
  • 5,988
  • 1
  • 9
  • 26
  • Thanks for your reply. This is helpful. But I actually want to convert a wmf file to emf file so I have to load the wmf file instead of drawing one myself. And I also checked your link. In the link, it uses GetMetaFileA to get a hmf handle for wmf file. However, GetMetaFileA is no longer supported on windows 10 or 7. So this is the problem :( – Xudong Shao Oct 02 '19 at 04:28
  • @XudongShao Yes, Microsoft recommends that "Windows-format" (WMF) functions only "rarely" be used and "enhanced-format" (EMF) functions be used instead. I also spent some time doing many researching and found no feasible solution for loading wmf files. Maybe Microsoft is gradually giving up support for wmf functions. – Strive Sun Oct 03 '19 at 08:18
  • 1
    Thanks a lot! I will try to find another solutions. – Xudong Shao Nov 05 '19 at 12:37
  • @XudongShao Sorry, please help me check the return value of GetLasterror(Beacuse hEMF return None. Can you use GetLastError to etrieve the calling thread's last-error code value.Just add int error_code = GetLastError() below HENHMETAFILE hEMF = SetWinMetaFileBits(metasize, buffer, NULL, NULL) – Strive Sun Nov 06 '19 at 03:33
  • @XudongShao hi, I have updated my answer. Now the code works well. – Strive Sun Nov 26 '19 at 05:59
0

This worked for me

CStatic * m_pictCtrl = (CStatic *)this->GetDlgItem(PICT_STATIC);


LPCSTR file = filePath;
ALDUSMFHEADER aldusmfHeader;
DWORD wBytesRead;
double xOri, xExt, yOri, yExt;
HANDLE fh = CreateFileA(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
ReadFile(fh, (void *)&aldusmfHeader, ALDUSMFHEADERSIZE, &wBytesRead, NULL);

xOri = aldusmfHeader.bbox.left;
xExt = aldusmfHeader.bbox.right - xOri;
if (aldusmfHeader.bbox.bottom < aldusmfHeader.bbox.top) {
    yOri = aldusmfHeader.bbox.bottom;
    yExt = aldusmfHeader.bbox.top - aldusmfHeader.bbox.bottom;
}
else {
    yOri = aldusmfHeader.bbox.top;
    yExt = aldusmfHeader.bbox.top - aldusmfHeader.bbox.bottom;
}
if (wBytesRead == -1 || wBytesRead < ALDUSMFHEADERSIZE)
{
    AfxMessageBox(L" is not a placeable Windows metafile : it cannot be converted into EMF format.");
    CloseHandle(fh);
    return 0;
}

// Envelope in /100 cm
double Density = static_cast<double>(aldusmfHeader.inch);
double Top = static_cast<double>(aldusmfHeader.bbox.top) / Density;
double RawBottom = static_cast<double>(aldusmfHeader.bbox.bottom) / Density;
double Left = static_cast<double>(aldusmfHeader.bbox.left) / Density;
double RawRight = static_cast<double>(aldusmfHeader.bbox.right) / Density;
// In order to correctly import the EMF metafile into WORD, add one delta
double Bottom, Right, Delta, Rate = 0.1;
if (RawBottom > RawRight)
{
    Delta = Rate * RawRight;
    Right = RawRight + Delta;
    Bottom = Right * RawBottom / RawRight;
}
else
{
    Delta = Rate * RawBottom;
    Bottom = RawBottom + Delta;
    Right = Bottom * RawRight / RawBottom;
}

// Metafile header
SetFilePointer(fh, ALDUSMFHEADERSIZE, NULL, FILE_BEGIN);
METAHEADER mfHeader;
ReadFile(fh, (void *)&mfHeader, sizeof(METAHEADER), &wBytesRead, NULL);

// Allocate memory in order to save into memory bits after the Aldus header in the WMF metafile
// * 2 : 16 bits API
DWORD dwSize = mfHeader.mtSize * 2 * sizeof(BYTE);
BYTE *lpMFBits = (BYTE *)malloc(dwSize);
if (lpMFBits == nullptr)
{
    AfxMessageBox(L"nullptr lpmfbits");
    //cout << "Not enough memory to convert " << WMFFileName << " into EMF format." << endl;
    CloseHandle(fh);
    return 0;
}

// Bits after the Aldus header
SetFilePointer(fh, ALDUSMFHEADERSIZE, NULL, FILE_BEGIN);
ReadFile(fh, (void *)lpMFBits, dwSize, &wBytesRead, NULL);
if (wBytesRead == -1)
{
    //cout << "Error while reading " << WMFFileName << " : impossible to convert it into EMF format." << endl;
    free(lpMFBits);
    CloseHandle(fh);
    return 0;
}

// Save these bits into a memory enhanced metafile
// The memory enhanced metafile only contains 32 bits API functions : TextOut has been converted into ExtTextOutW,
// CreateFontIndirect has been converted into ExtCreateFontIndirectW, ...
METAFILEPICT MetaFilePict;
MetaFilePict.hMF = NULL;
MetaFilePict.mm = MM_ANISOTROPIC;
double Fact = 10.0 * Density;
MetaFilePict.xExt = static_cast<LONG>(Fact * (Right - Left));
MetaFilePict.yExt = static_cast<LONG>(Fact * (Bottom - Top));

HENHMETAFILE hMemoryEnhMetafile = SetWinMetaFileBits(dwSize, lpMFBits, NULL, &MetaFilePict);
free(lpMFBits);
CloseHandle(fh);

if (m_pictCtrl->GetEnhMetaFile() == NULL)
    m_pictCtrl->SetEnhMetaFile(hMemoryEnhMetafile);
Shanks D Shiva
  • 197
  • 1
  • 3