PlayEnhMetaFile fails, without any GetLastError response

radarhere-3869 40 Reputation points
2024-10-09T07:55:21.8833333+00:00

Over at https://github.com/python-pillow/Pillow/issues/6980, a user has posted a zip containing an EMF file - https://github.com/python-pillow/Pillow/files/10852640/test_libuemf_ref.zip

PlayEnhMetaFile fails to process the file. I attempted find out why with GetLastError, but it returned 0 (aka ERROR_SUCCESS, indicating that there is no problem).

HDC dc = CreateCompatibleDC(NULL);
BITMAPINFO info;
memset(&info, 0, sizeof(BITMAPINFO));
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = 14030;
info.bmiHeader.biHeight = 9920;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = 24;
info.bmiHeader.biCompression = BI_RGB;
void *ptr;
bitmap = CreateDIBSection(dc, &info, DIB_RGB_COLORS, &ptr, NULL, 0);
SelectObject(dc, bitmap);
HENHMETAFILE hemf = GetEnhMetaFile("test.emf");
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = 14030;
rect.bottom = 9920;
if (!PlayEnhMetaFile(dc, hemf, &rect)) {
	printf("error %d\n", GetLastError());
}

Is anyone able to help me figure out why this file is failing? Is it corrupted? Is there something that would persuade PlayEnhMetaFile to accept it?

Thanks.

Windows development | Windows API - Win32
{count} votes

Accepted answer
  1. Viorel 122.6K Reputation points
    2024-10-09T10:51:23.1133333+00:00

    The file seems to contain records that are invalid intentionally. The purpose of the file is probably to check the rendering engines, which should not crash in case of complex or bad files. To find such records, try this code:

    BOOL b = EnumEnhMetaFile( dc, hemf, 
       []( HDC hdc, HANDLETABLE FAR* lpht, CONST ENHMETARECORD* lpmr, int nHandles, LPARAM data ) -> BOOL
       {
          if( !PlayEnhMetaFileRecord( hdc, lpht, lpmr, nHandles ) )
          {
    
          }
    
          return TRUE;
       }, 
       NULL, &rect );
    

    Use it instead of PlayEnhMetaFile. It will draw the good records, and b will be TRUE. Inside the if, you can see the unexpected records that cannot be drawn. (The details of ENHMETARECORD can be found in documentation).

    The rendering programs seems to give different results in case of such special test files.

    1 person found this answer helpful.
    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. Castorix31 90,686 Reputation points
    2024-10-09T10:04:41.6533333+00:00

    This test works on my Windows 10 22H2 OS, by drawing your .emf in the client area of the main window (also works on the screen DC with nWidth, nHeight, but larger than screen) :

    HENHMETAFILE hEmf = GetEnhMetaFile(TEXT("test_libuemf_ref.emf"));					
    if (hEmf)
    {
    				ENHMETAHEADER pEmfHeader;
    				if (GetEnhMetaFileHeader(hEmf, sizeof(pEmfHeader), &pEmfHeader) != 0)
    				{
    					HDC hDC = GetDC(hWnd);
    
    					// For test
    					RECT rcBounds;
    					rcBounds.left = MulDiv(pEmfHeader.rclFrame.left, pEmfHeader.szlDevice.cx, pEmfHeader.szlMillimeters.cx * 100);
    					rcBounds.top = MulDiv(pEmfHeader.rclFrame.top, pEmfHeader.szlDevice.cy, pEmfHeader.szlMillimeters.cy * 100);
    					rcBounds.right = MulDiv(pEmfHeader.rclFrame.right, pEmfHeader.szlDevice.cx, pEmfHeader.szlMillimeters.cx * 100);
    					rcBounds.bottom = MulDiv(pEmfHeader.rclFrame.bottom, pEmfHeader.szlDevice.cy, pEmfHeader.szlMillimeters.cy * 100);
    					int nWidth = rcBounds.right - rcBounds.left;
    					int nHeight = rcBounds.bottom - rcBounds.top;
    
    					BITMAPINFO BitmapInfo;
    					ZeroMemory(&BitmapInfo, sizeof(BITMAPINFO));
    					BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    					BitmapInfo.bmiHeader.biWidth = nWidth;
    					BitmapInfo.bmiHeader.biHeight = nHeight;
    					BitmapInfo.bmiHeader.biPlanes = 1;
    					BitmapInfo.bmiHeader.biBitCount = 32;
    					BitmapInfo.bmiHeader.biCompression = BI_RGB;
    					HBITMAP hBitmap = CreateDIBSection(hDC, &BitmapInfo, DIB_RGB_COLORS, NULL, NULL, 0);
    					if (hBitmap != NULL)
    					{								
    						HBITMAP hBitmapOld = (HBITMAP)SelectObject(hDC, hBitmap);
    						/*RECT rect = { 0, 0, nWidth, nHeight };*/
    						RECT rect;
    						GetClientRect(hWnd, &rect);
    						BOOL bRet = PlayEnhMetaFile(hDC, hEmf, &rect);
    						SelectObject(hDC, hBitmapOld);
    						DeleteObject(hBitmap);
    					}
    					ReleaseDC(hWnd, hDC);
    				}
    				DeleteEnhMetaFile(hEmf);
    }  
    
    
    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.