How to Create & Play Enhanced Metafiles in Win32

ID: Q145999


The information in this article applies to:


SUMMARY

The Win32 SDK introduces a new type of metafile known as an enhanced metafile. These new metafiles address developer's need for device independence without requiring a separate code path, which was a requirement of the older style metafiles.

This article and the accompanying sample (ENMETA.EXE) show you how to properly create and play enhanced metafiles scaled or to original size. The sample also supports the clipboard and reads and writes Aldus placeable metafiles as well as regular 16-bit Windows metafiles.


MORE INFORMATION

The following file is available for download from the Microsoft Software Library:

~ Enmeta.exe
For more information about downloading files from the Microsoft Software Library, please see the following article in the Microsoft Knowledge Base:
Q119591 How to Obtain Microsoft Support Files from Online Services

Creating the Enhanced Metafile

An enhanced metafile is created with a call to CreateEnhMetaFile(). CreateEnhMetaFile() is declared as follows:

   HDC CreateEnhMetaFile( HDC hdcRef, LPCTSTR lpFilename,

                       CONST RECT *lpRect, LPCTSTR lpDescription ); 
Following are the parameters to this call:

hdcRef is a reference DC and provides some crucial reference information for the construction of the metafile. The resolutions in both pixels and millimeters are taken from this device, and all font metrics are based on this DC. For the best possible reproduction on all output devices, it is a good idea to choose a high resolution device for the reference device. Otherwise, some grainyness may appear if the output device is of considerably higher resolution than the reference device.

lpFileName is the name of the file that will contain the metafile. If this parameter is NULL, the metafile is a memory metafile and is not stored on disk.

lpRect is a rectangle that specifies the dimensions in 0.01 mm units of the virtual metafile device. This rectangle is stored in the metafile and can be retrieved at play time to determine the desired real size of the output. Although no clipping is performed for this rectangle, some metafile players may presume that the image in the metafile fits in the rectangle.

lpDescription simply provides a way to store the name of the application that created the metafile as well as a description of the contents in the form of two NULL-terminated strings, terminated with an additional NULL as in this example:

   App\0Description\0\0 
App is the name of the application that created the metafile and Description is a description of the image.

The Metafile Device

The metafile device has a real size defined by the lpRect parameter in 0.01 mm units. The number of device units on the metafile can be determined by using the pixel/mm ratio of the reference DC and the metafile device size given in the lpRect parameter. For example:

   MetaPixelsX = MetaWidthMM * MetaPixels / (MetaMM * 100);

   where MetaPixelsX = number of pixels on the X axis
      MetaWidthMM = metafile width in 0.01mm units
      MetaPixels  = width in pixels of the reference device
      MetaMM      = width in millimeters of the reference device 
A similar calculation can be used to determine the number of pixels in the Y direction of the metafile device.

Note that although the metafile device has a real size, it does not clip output to that region. It is entirely possible to record drawing commands that have output outside of the metafile device surface.

Playing the Enhanced Metafile

The metafile device provides only half of the mapping from metafile space to target device space. The other half is provided by the RECT passed to the PlayEnhMetaFile() call. It specifies a play rectangle in which to play the metafile and is specified in logical coordinates on the target device context. The metafile device rectangle is mapped to this play rectangle. This provides the scalability of enhanced metafiles; adjusting the play rectangle adjusts the size of the output. No clipping is performed on the play rectangle.

So, neither the metafile device nor the play rectangle perform clipping. This means that if any drawing commands were recorded to occur outside the metafile device, they will be shown outside the play rectangle when the metafile is played. This mapping is illustrated in the Sample1.emf sample enhanced metafile.

If the goal is to play the metafile at its true size, the size of the original metafile device can be determined by a call to GetEnhMetaFileHeader(). This call fills in an ENHMETAHEADER structure, of which the rclFrame member specifies the metafile device rectangle in 0.01 mm units. That rectangle can be translated into logical units for the target DC and used as the lpRect parameter for PlayEnhMetaFile(). The PlayEnhMetaFileAtOriginalSize() function in the sample demonstrates this.

Mapping Modes

During the recording of the metafile, the mapping mode, window extents and origin, and viewport extents and origin combine to map logical units to device units on the metafile device. As with a normal DC, the window extents and origin define the logical space, while the viewport extents and origin are relative to the metafile device units described above.

For example: The following code creates an enhanced metafile that is dwInchesX wide by dwInchesY tall with dwDPI logical Dots Per Inch (DPI):

   HDC MyCreateEnhMetaFile( LPTSTR szFileName,  // Metafile filename
                            DWORD dwInchesX,    // Width in inches
                            DWORD dwInchesY,    // Height in inches
                            DWORD dwDPI )       // DPI (logical units)
   {
       RECT   Rect = { 0, 0, 0, 0 };
       TCHAR   szDesc[] = "AppName\0Image Description\0\0";
       HDC      hMetaDC, hScreenDC;
       float   PixelsX, PixelsY, MMX, MMY;

       // dwInchesX x dwInchesY in .01mm units
       SetRect( &Rect, 0, 0, dwInchesX*2540, dwInchesY*2540 );

       // Get a Reference DC
       hScreenDC = GetDC( NULL );

       // Get the physical characteristics of the reference DC
       PixelsX = (float)GetDeviceCaps( hScreenDC, HORZRES );
       PixelsY = (float)GetDeviceCaps( hScreenDC, VERTRES );
       MMX = (float)GetDeviceCaps( hScreenDC, HORZSIZE );
       MMY = (float)GetDeviceCaps( hScreenDC, VERTSIZE );

       // Create the Metafile
       hMetaDC = CreateEnhMetaFile(hScreenDC, szFileName, &Rect, szDesc);
       // Release the reference DC
       ReleaseDC( NULL, hScreenDC );
       // Did you get a good metafile?
       if( hMetaDC == NULL )
           return NULL;

       // Anisotropic mapping mode
       SetMapMode( hMetaDC, MM_ANISOTROPIC );
       // Set the Windows extent
       SetWindowExtEx( hMetaDC, dwInchesX*dwDPI, dwInchesY*dwDPI, NULL );


       // Set the viewport extent to reflect
       // dwInchesX" x dwInchesY" in device units
       SetViewportExtEx( hMetaDC,
                         (int)((float)dwInchesX*25.4f*PixelsX/MMX),
                         (int)((float)dwInchesY*25.4f*PixelsY/MMY),
                         NULL );
       return hMetaDC;
   } 
Note that clipping is not performed by the window extents, viewport extents, or the metafile device. It is possible to draw beyond the window extents, which will map beyond the viewport extents. Those numbers provide only a ratio of logical units to metafile device units. Further, it is possible to have viewport extents that extend beyond the metafile surface. The drawing is not clipped to the metafile surface. This mapping is illustrated in the Sample2.emf sample enhanced metafile.

Palettes in Enhanced Metafiles

The SelectPalette() API can be used to record a palette into an enhanced metafile. That palette can then be retrieved at play-time via the GetEnhMetaFilePaletteEntries() API. This allows the player to faithfully reproduce palletized images and properly respond to palette messages. The GetEnhancedMetafilePalette() function in the sample demonstrates extracting a palette from an enhanced metafile.

Note that if multiple palettes were used in recording the metafile, the palette entries retrieved by GetEnhMetaFilePaletteEntries()includes all the entries from all the palettes. If this includes more entries than the current display can reproduce, it is the player's responsibility to choose a subset of those colors from which to create the actual palette to be used during playback.

Additional query words: kbDSupport kbdsd kbGDI kbMetafile


Keywords          : kbcode kbfile kbsample kbSDKWin32 
Version           : WINDOWS:
Platform          : WINDOWS 
Issue type        : kbinfo 

Last Reviewed: June 24, 1999