使用 C++ 中的 SetWinMetaFileBits api 将 wmf 文件转换为 emf 文件

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

我正在尝试将 wmf 文件转换为 emf 文件。根据我在网上了解到的,最好的解决方案是这样的。

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);

这里的思路是使用CreateMetaFileA和CloseMetaFile来获取HMETAFILE hmf。 然后我尝试了我的代码,奇怪的事情来了。手柄 hmf 总是指向 ???在内存中,metasize 始终是 24 和不同的图片。 hEMF 总是 None.

这真的很难过,因为我整晚都在弄清楚如何让代码工作。

我确实阅读了很多资料,包括

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

有人可以帮我吗?谢谢

您需要初始化 METAFILEPICT 结构。

最小示例:

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 );
            }     

我的测试代码:

    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);

调试:

可以看到nSize = 114

我怀疑您使用 CreateMetaFileACloseMetaFile 直接加载文件名和 return Windows 格式图元文件的句柄是错误的方法。

更新:

您可以通过其他方式获取WMF文件的句柄。

#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;
}

这对我有用

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);