A metafile is a mechanism for storing a graphics device interface (GDI) "picture"—a series of GDI functions that are used to draw an image. A metafile consists of a series of records, each representing a GDI function. When the metafile is played back, each stored function is executed using its recorded parameters.
The mapping mode of a metafile can be altered during playback. Thus, the image can be scaled arbitrarily, with every component scaling separately, which minimizes the loss of information for the image as a whole and which is not characteristic of bitmaps. In addition, if the image is sparse, a metafile uses less memory than does a bitmap of the same image.
There are four flavors of Windows metafiles:
- standard meta file
- placeable meta file (also known as APM file)
- clipboard meta file
- enhanced meta file
Now lets go through each file format
Standard Metafile (WMF)
A standard metafile contains an 18-byte WMF header followed by one or more records of GDI commands. Windows Metafiles (Figure Microsoft Windows Metafile-1) contain a header, followed by one or more records of data. The header contains a description of the record data stored in the metafile. Each record is a binary-encoded Microsoft Windows Graphics Device Interface (GDI) function call. The GDI is used by Windows to perform all output to a window, printer, or other output device. When the metafile data is rendered (or played back, in Microsoft terminology), the data from each record is used to perform the appropriate GDI function call to render each object stored in the file. The last record in the file contains information indicating that the end of the record data has been reached.
Figure-1 : Standard Metafile (WMF)
Placeable Metafile (APM)
A placeable metafile contains a 22-byte header followed by the standard 18-byte WMF header and GDI command records. The following figure will give you more idea about placeable metafile.
Figure-2 : Placeable Metafile (APM)
Clipboard Metafile
Clipboard metafiles contains a 8-byte (Win16) or 16-byte (Win32) header that precedes the standard metafile header. The following figure will give you more idea about Clipboard metafile.
Figure-3 : Clipboard Metafile
Enhanced Metafile
Enhanced metafiles contain only EMF records, with the first record storing the header information. EMF files are not compatible in design with the other types of WMF metafiles. The following figure will give you more idea about Enhanced metafile.
Figure-4 : Enhanced Metafile (APM)
Basic Use
Creating a metafile is as simple as calling the CreateMetaFile/CreateEnhMetaFile function. An application can store a metafile in global memory or to disk; using a memory metafile is faster, but it does use up memory. The CreateMetaFile/CreateEnhMetaFile function returns a handle to a metafile device context (DC). To record any function that performs an output operation or sets a drawing attribute into a metafile, use this handle in place of a normal DC handle when calling that function. When the desired picture is stored in the metafile DC, the application calls the CloseEnhMetaFile/CloseMetaFile function. As its name implies, the CloseMetaFile/CloseEnhMetaFile function closes the metafile DC so that it can no longer be used for recording. The function returns a handle to a metafile.
Now the metafile is ready for playback. The PlayMetaFile/PlayEnhMetaFile function is the simplest way to play back a metafile. It accepts a destination DC, which is where the image is to be drawn, and the metafile itself. In this function, GDI recalls every stored instruction in the metafile and executes it to the destination DC.
When the application is through with the metafile and before terminating, the application must free GDI memory used by the metafile by means of the DeleteMetaFile/DeleteEnhMetaFile function. If the metafile is stored on disk, the file remains untouched; only GDI memory associated with the metafile is freed. GDI deletes all objects created during a metafile playback as soon as the playing is complete.
Extracting and Playing Individual Record
As we saw that in metafiles graphics is stored as a series of GDI commands. Each record (command) is stored as a standard format which contains size of record, GDI command ID (i.e GDI function identifier) and GDI function related data (e.g for LineTo function X and Y parameter stored).
Windows has a mechanism that allows an application to inspect every record before it is played, change that record, or even invent a record of its own. EnumMetaFile calls a callback routine with every record found in the metafile. The application then calls PlayMetaFileRecord to play an individual record. In the simplest case, the information passed to the callback can be sent directly to PlayMetaFileRecord to simulate PlayMetaFile. In more complicated scenarios, the application can change the colors of objects or text of a TextOut, omit certain records, or simply add new records to the playback.
Step-By-Step Example
- Create a standard exe project - Add two command buttons, one checkbox, three option buttons and one picturebox - Add the following code in form1 |
Click here to copy the following block | Option Explicit
Private Const FILETYPE_APM = &H9AC6CDD7 Private Const MM_ANISOTROPIC = 8
Private Type RECT Left As Long Top As Long Right As Long Bottom As Long End Type
Private Type RECT16 Left As Integer Top As Integer Right As Integer Bottom As Integer End Type
Private Type SIZEL cx As Long cy As Long End Type
Private Type ENHMETAHEADER iType As Long nSize As Long rclBounds As RECT rclFrame As RECT dSignature As Long nVersion As Long nBytes As Long nRecords As Long nHandles As Integer sReserved As Integer nDescription As Long offDescription As Long nPalEntries As Long szlDevice As SIZEL szlMillimeters As SIZEL End Type
Private Type APMFILEHEADER key As Long hMF As Integer bbox As RECT16 inch As Integer reserved As Long checksum As Integer End Type
Private Type METAHEADER mtType As Integer mtHeaderSize As Integer mtVersion As Integer mtSize As Long mtNoObjects As Integer mtMaxRecord As Long mtNoParameters As Integer End Type
Private Declare Function DeleteMetaFile Lib "gdi32" (ByVal hMF As Long) As Long Private Declare Function GetMetaFile Lib "gdi32" Alias "GetMetaFileA" (ByVal lpFileName As String) As Long Private Declare Function GetMetaFileBitsEx Lib "gdi32" (ByVal hMF As Long, ByVal nSize As Long, lpvData As Any) As Long Private Declare Function PlayMetaFile Lib "gdi32" (ByVal hDC As Long, ByVal hMF As Long) As Long Private Declare Function SetMetaFileBitsEx Lib "gdi32" (ByVal nSize As Long, lpData As Byte) As Long Private Declare Function SetWinMetaFileBits Lib "gdi32" (ByVal cbBuffer As Long, lpbBuffer As Byte, ByVal hdcRef As Long, lpmfp As Any) As Long Private Declare Function CreateMetaFile Lib "gdi32.dll" Alias "CreateMetaFileA" (ByVal lpString As String) As Long Private Declare Function CloseMetaFile Lib "gdi32.dll" (ByVal hDcMF As Long) As Long
Private Declare Function MoveToEx Lib "gdi32.dll" (ByVal hDC As Long, ByVal x As Long, ByVal y As Long, ByRef lpPoint As Any) As Long Private Declare Function LineTo Lib "gdi32.dll" (ByVal hDC As Long, ByVal x As Long, ByVal y As Long) As Long
Private Declare Function DeleteEnhMetaFile Lib "gdi32" (ByVal hemf As Long) As Long Private Declare Function GetEnhMetaFile Lib "gdi32" Alias "GetEnhMetaFileA" (ByVal lpszMetaFile As String) As Long Private Declare Function GetEnhMetaFileHeader Lib "gdi32" (ByVal hemf As Long, ByVal cbBuffer As Long, lpemh As ENHMETAHEADER) As Long Private Declare Function PlayEnhMetaFile Lib "gdi32" (ByVal hDC As Long, ByVal hemf As Long, lpRect As Any) As Long Private Declare Function CreateEnhMetaFile Lib "gdi32.dll" Alias "CreateEnhMetaFileA" (ByVal hdcRef As Long, ByVal lpFileName As String, ByRef lpRect As Any, ByVal lpDescription As String) As Long Private Declare Function CloseEnhMetaFile Lib "gdi32.dll" (ByVal hDC As Long) As Long Private Declare Function GetWinMetaFileBits Lib "gdi32.dll" ( _ ByVal hemf As Long, _ ByVal cbBuffer As Long, _ ByRef lpbBuffer As Byte, _ ByVal fnMapMode As Long, _ ByVal hdcRef As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long) Private Declare Function GetDeviceCaps Lib "gdi32.dll" (ByVal hDC As Long, ByVal nIndex As Long) As Long
Private Const HORZRES = 8 Private Const HORZSIZE = 4 Private Const VERTRES = 10 Private Const VERTSIZE = 6
Dim picRect As RECT Dim RectNull As RECT
Dim ScreenWidthMM, ScreenHeightMM, ScreenWidthPix, ScreenHeightPix Dim MMPerPelX, MMPerPelY
Private Sub Check1_Click() Command1_Click End Sub
Private Sub Command1_Click() Picture1.Cls PaintAnyMetaFile Picture1.hDC, Text1, picRect, CBool(Check1.Value) End Sub
Private Sub Command2_Click() Const WMF_SAVE_NAME = "Apidemo_regular.wmf" Const EMF_SAVE_NAME = "Apidemo_enhanced.emf" Const APM_SAVE_NAME = "Apidemo_placeable.wmf"
Picture1.Cls
If Option1.Value = True Then Call EMFDemo(App.Path & "\" & EMF_SAVE_NAME) ElseIf Option2.Value = True Then Call WMFDemo(App.Path & "\" & WMF_SAVE_NAME) ElseIf Option3.Value = True Then Call APMDemo(App.Path & "\" & APM_SAVE_NAME) End If
MsgBox "File created at " & Text1 End Sub
Sub EMFDemo(strSavePath As String) Dim R As RECT Dim hdcMeta As Long, hMF As Long, temp As Long, s As String
If Dir(strSavePath) <> "" Then Kill strSavePath
R.Right = 200 * MMPerPelX R.Bottom = 200 * MMPerPelY
hdcMeta = CreateEnhMetaFile(Picture1.hDC, strSavePath, R, "This EMF file generated for APIDemo" & Chr(0))
If hdcMeta <> 0 Then Call DrawSomethingToDC(hdcMeta)
hMF = CloseEnhMetaFile(hdcMeta) Text1.Text = strSavePath temp = PaintAnyMetaFile(Picture1.hDC, strSavePath, picRect, CBool(Check1.Value)) temp = DeleteEnhMetaFile(hMF) End If End Sub
Sub WMFDemo(strSavePath As String) Dim hdcMeta As Long, hMF As Long, temp As Long, s As String
If Dir(strSavePath) <> "" Then Kill strSavePath hdcMeta = CreateMetaFile(strSavePath)
If hdcMeta <> 0 Then Call DrawSomethingToDC(hdcMeta)
hMF = CloseMetaFile(hdcMeta) Text1.Text = strSavePath temp = PaintAnyMetaFile(Picture1.hDC, strSavePath, picRect, CBool(Check1.Value)) temp = DeleteMetaFile(hMF) End If End Sub
Sub APMDemo(strSavePath As String) Dim R As RECT, dwSize As Long, i As Integer Dim hdcMeta As Long, hMF As Long, temp As Long, s As String Dim Buffer() As Byte Dim emh As ENHMETAHEADER, APMHeader As APMFILEHEADER Dim ChkSumBuffer(10) As Integer If Dir(strSavePath) <> "" Then Kill strSavePath
R.Right = 200 * MMPerPelX R.Bottom = 200 * MMPerPelY
hdcMeta = CreateEnhMetaFile(0&, vbNullString, R, "This EMF file generated for APIDemo" & Chr(0))
If hdcMeta <> 0 Then Call DrawSomethingToDC(hdcMeta)
hMF = CloseEnhMetaFile(hdcMeta)
temp = GetEnhMetaFileHeader(hMF, Len(emh), emh)
APMHeader.key = &H9AC6CDD7 APMHeader.hMF = 0 APMHeader.bbox.Top = 1000 * emh.rclFrame.Top / 2540 APMHeader.bbox.Left = 1000 * emh.rclFrame.Left / 2540 APMHeader.bbox.Right = 1000 * emh.rclFrame.Right / 2540 APMHeader.bbox.Bottom = 1000 * emh.rclFrame.Bottom / 2540 APMHeader.inch = 1000 APMHeader.reserved = 0 CopyMemory ChkSumBuffer(0), APMHeader, Len(APMHeader) APMHeader.checksum = ChkSumBuffer(0) For i = 1 To 9 APMHeader.checksum = APMHeader.checksum Xor ChkSumBuffer(i) Next
dwSize = GetWinMetaFileBits(hMF, 0, ByVal 0&, MM_ANISOTROPIC, Picture1.hDC) ReDim Buffer(dwSize - 1) temp = GetWinMetaFileBits(hMF, dwSize, Buffer(0), MM_ANISOTROPIC, Picture1.hDC)
Open strSavePath For Binary As #1 Put #1, , APMHeader Put #1, , Buffer Close #1
Text1.Text = strSavePath temp = PaintAnyMetaFile(Picture1.hDC, strSavePath, picRect, CBool(Check1.Value)) temp = DeleteEnhMetaFile(hMF) End If End Sub
Sub DrawSomethingToDC(hdcMeta As Long) Dim ret As Long, i For i = 1 To 200 Step 4 ret = MoveToEx(hdcMeta, i, i, ByVal 0&) ret = LineTo(hdcMeta, 200 - i, i) ret = LineTo(hdcMeta, 200 - i, 200 - i) ret = MoveToEx(hdcMeta, i, i, ByVal 0&) ret = LineTo(hdcMeta, i, i) Next End Sub
Private Sub Form_Load() Text1.Text = App.Path & "\print_enhanced.emf"
Command1.Caption = "Open/Play Any Meta File" Command2.Caption = "Create/Save/Play Meta File Demo"
Option1.Caption = "Enhanced Meta File (EMF)" Option2.Caption = "Windows Meta File (WMF)" Option3.Caption = "Aldus Placeable metafile (APM)" Option1.Value = True
Check1.Caption = "Fit to Picturebox" Check1.Value = 1
Picture1.ScaleMode = vbPixels
picRect.Top = 0 picRect.Left = 0 picRect.Right = Picture1.ScaleWidth picRect.Bottom = Picture1.ScaleHeight
ScreenWidthMM = GetDeviceCaps(hDC, HORZSIZE) ScreenHeightMM = GetDeviceCaps(hDC, VERTSIZE) ScreenWidthPix = GetDeviceCaps(hDC, HORZRES) ScreenHeightPix = GetDeviceCaps(hDC, VERTRES) MMPerPelX = (ScreenWidthMM * 100) / ScreenWidthPix MMPerPelY = (ScreenHeightMM * 100) / ScreenHeightPix End Sub
Private Function PaintAnyMetaFile(ByVal hDC As Long, ByVal Filename As String, _ R As RECT, Optional FitToRect As Boolean = True) As Boolean
Dim hMF As Long Dim emh As ENHMETAHEADER
hMF = GetEnhMetaFile(Filename) If hMF Then If FitToRect = False Then Call GetEnhMetaFileHeader(hMF, Len(emh), emh) PaintAnyMetaFile = PlayEnhMetaFile(hDC, hMF, emh.rclBounds) Else PaintAnyMetaFile = PlayEnhMetaFile(hDC, hMF, R) End If Call DeleteEnhMetaFile(hMF) Else PaintAnyMetaFile = PlayOldMetafile(hDC, Filename, R, FitToRect) End If End Function
Private Function PlayOldMetafile(ByVal hDC As Long, ByVal Filename As String, _ R As RECT, Optional FitToRect As Boolean = True) As Boolean
Dim hMF As Long Dim hemf As Long Dim Bytes As Long Dim Buffer() As Byte
hMF = GetMetaFile(Filename) If hMF = 0 Then hMF = GetPlaceableMetafile(Filename) If hMF = 0 Then PlayOldMetafile = False Exit Function End If End If
Bytes = GetMetaFileBitsEx(hMF, Bytes, ByVal 0&) If Bytes Then ReDim Buffer(1 To Bytes) As Byte Call GetMetaFileBitsEx(hMF, Bytes, Buffer(1)) Else PlayOldMetafile = False Exit Function End If
hemf = SetWinMetaFileBits(Bytes, Buffer(1), hDC, ByVal 0&)
If FitToRect = False Then Dim emh As ENHMETAHEADER Call GetEnhMetaFileHeader(hemf, Len(emh), emh) PlayOldMetafile = PlayEnhMetaFile(hDC, hemf, emh.rclBounds) Else PlayOldMetafile = PlayEnhMetaFile(hDC, hemf, R) End If
Call DeleteEnhMetaFile(hemf) Call DeleteMetaFile(hMF) End Function
Private Function GetPlaceableMetafile(ByVal Filename As String) As Long Dim hFile As Long Dim Apm As APMFILEHEADER Dim Hdr As METAHEADER Dim mtSize As Long Dim Buffer() As Byte Dim hMF As Long
On Error GoTo FileErr
hFile = FreeFile Open Filename For Binary Access Read As #hFile Get #hFile, , Apm
If Apm.key <> FILETYPE_APM Then GetPlaceableMetafile = 0 Exit Function End If
Get #hFile, 23, Hdr
Hdr.mtSize = Hdr.mtSize * 2
ReDim Buffer(1 To Hdr.mtSize) As Byte Get #hFile, 23, Buffer
Close hFile
GetPlaceableMetafile = SetMetaFileBitsEx(UBound(Buffer), Buffer(1)) Exit Function FileErr: GetPlaceableMetafile = 0 End Function |
|