Internally, a metafile is an array of variable-length structures called metafile records. The first records in the metafile specify general information such as the resolution of the device on which the picture was created, the dimensions of the picture, and so on. The remaining records, which constitute the bulk of any metafile, correspond to the graphics device interface (GDI) functions required to draw the picture. These records are stored in the metafile after a special metafile device context is created. This metafile device context is then used for all drawing operations required to create the picture. When the system processes a GDI function associated with a metafile DC, it converts the function into the appropriate data and stores this data in a record appended to the metafile.
After a picture is complete and the last record is stored in the metafile, you can pass the metafile to another application by:
- Using the clipboard
- Embedding it within another file
- Storing it on disk
- Playing it repeatedly
A metafile is played when its records are converted to device commands and processed by the appropriate device.
There are mainly two types of metafiles:
- Enhanced-format metafiles
- Windows-format metafiles
In this article we will focus on the most useful meta file format which is Enhanced Meta File (EMF) format.
The enhanced-format metafile consists of the following elements:
- A header
- A table of handles to GDI objects
- A private palette
- An array of metafile records
Enhanced metafiles provide true device independence. You can think of the picture stored in an enhanced metafile as a "snapshot" of the video display taken at a particular moment. This "snapshot" maintains its dimensions no matter where it appearson a printer, a plotter, the desktop, or in the client area of any application.
You can use enhanced metafiles to store a picture created by using the GDI functions (including new path and transformation functions). Because the enhanced metafile format is standardized, pictures that are stored in this format can be copied from one application to another; and, because the pictures are truly device independent, they are guaranteed to maintain their shape and proportion on any output device.
Now lets have real fun with meta files
I have wrapped EMF related APIs in resuable class. Here is the list of what you will learn from this sample code
- Open and display meta file from disk. - Create metafile in memory and draw to memory metafile. - Save memory meta file to the disk when you done.
Step-By-Step Example
- Create a standard exe project - Add one command button and one timer control on the form1 - Add one class module to the project. Rename it to clsEMF - Add the following code to form1
Form1.frm |
Click here to copy the following block | Private Declare Function CreatePen Lib "gdi32.dll" (ByVal nPenStyle As Long, _ ByVal nWidth As Long, ByVal crColor As Long) As Long
Private Declare Function SelectObject Lib "gdi32.dll" ( _ ByVal hDC As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
Private Declare Function Rectangle Lib "gdi32.dll" (ByVal hDC As Long, _ ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function GetStockObject Lib "gdi32.dll" (ByVal nIndex As Long) 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
Const PenWidth As Long = 2
Private Const PS_SOLID As Long = &H0 Private Const NULL_BRUSH As Long = &H5
Private MyEmf(4) As clsEMF
Dim EMFWidth As Long, EMFHeight As Long Dim hPen As Long, hOldPen As Long Dim ScreenWidthMM, ScreenHeightMM, ScreenWidthPels, ScreenHeightPels Dim MMPerPelX, MMPerPelY
Private Sub Command1_Click() Dim i, ret As Boolean ret = True For i = 0 To UBound(MyEmf) ret = ret And MyEmf(i).SaveEmf(App.Path & "\MyEMF" & i & ".emf") Next If ret = True Then MsgBox "Save OK" Else MsgBox "Save Error" End If End Sub
Private Sub Form_Load() Dim i, s Me.ScaleMode = 3
ScreenWidthMM = GetDeviceCaps(hDC, HORZSIZE) ScreenHeightMM = GetDeviceCaps(hDC, VERTSIZE) ScreenWidthPels = GetDeviceCaps(hDC, HORZRES) ScreenHeightPels = GetDeviceCaps(hDC, VERTRES) MMPerPelX = (ScreenWidthMM * 100) / ScreenWidthPels MMPerPelY = (ScreenHeightMM * 100) / ScreenHeightPels
Command1.Caption = "Save EMF to Disk"
EMFWidth = 100 EMFHeight = 100
Randomize For i = 0 To UBound(MyEmf) Set MyEmf(i) = New clsEMF
If (MyEmf(i).Create(EMFWidth * MMPerPelX, EMFHeight * MMPerPelY)) Then
hPen = CreatePen(PS_SOLID, PenWidth, RGB(Rnd * 255, Rnd * 255, Rnd * 255)) hOldPen = SelectObject(MyEmf(i).hDC, hPen)
Call SelectObject(MyEmf(i).hDC, GetStockObject(NULL_BRUSH)) For s = 1 To 50 Step 5 Call Rectangle(MyEmf(i).hDC, s, s, EMFWidth - s, EMFHeight - s) Next
Call SelectObject(MyEmf(i).hDC, hOldPen) Call DeleteObject(hPen) Call MyEmf(i).StopRecording Else Debug.Print "Error creating red square EMF!" End If Next
Timer1.Enabled = True Timer1.Interval = 200 End Sub
Private Sub Form_Paint() Dim i For i = 0 To UBound(MyEmf) Randomize Call MyEmf(i).Draw(Form1.hDC, Rnd * Me.ScaleWidth - EMFWidth, Rnd * Me.ScaleHeight - EMFHeight, EMFWidth, EMFHeight) Next End Sub
Private Sub Form_Unload(ByRef Cancel As Integer) Dim i For i = 0 To UBound(MyEmf) MyEmf(i).Destroy Set MyEmf(i) = Nothing Next End Sub
Private Sub Timer1_Timer() Me.Refresh End Sub |
- Add the following code to class module
clsEMF.cls |
Click here to copy the following block | 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 DeleteEnhMetaFile Lib "gdi32.dll" (ByVal hEMF As Long) As Long
Private Declare Function GetEnhMetaFile Lib "gdi32.dll" Alias "GetEnhMetaFileA" ( _ ByVal lpszMetaFile As String) As Long
Private Declare Function PlayEnhMetaFile Lib "gdi32.dll" (ByVal hDC As Long, _ ByVal hEMF As Long, ByRef lpRect As Any) As Long
Private Declare Function GetEnhMetaFileHeader Lib "gdi32.dll" (ByVal hEMF As Long, _ ByVal cbBuffer As Long, ByRef lpEMH As EnhMetaHeader) As Long
Private Declare Function OffsetRect Lib "User32.dll" ( _ ByRef lpRect As RectAPI, ByVal X As Long, ByVal Y As Long) As Long
Private Declare Function SetRect Lib "User32.dll" (ByRef lpRect As RectAPI, _ ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function SaveDC Lib "gdi32.dll" (ByVal hDC As Long) As Long
Private Declare Function RestoreDC Lib "gdi32.dll" ( _ ByVal hDC As Long, ByVal nSavedDC As Long) As Long
Private Declare Function CopyEnhMetaFile Lib "gdi32.dll" Alias "CopyEnhMetaFileA" ( _ ByVal hemfSrc As Long, _ ByVal lpszFile As String) As Long
Private Type RectAPI Left As Long Top As Long Right As Long Bottom As Long End Type
Private Type SizeL cx As Long cy As Long End Type
Private Type EnhMetaHeader iType As Long nSize As Long rclBounds As RectAPI rclFrame As RectAPI 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 m_hMeta As Long Private m_Recording As Boolean Private m_Managed As Boolean Private SaveState As Long
Public Property Get hDC() As Long If (m_Recording) Then hDC = m_hMeta End Property
Public Property Get hEMF() As Long If (Not m_Recording) Then hEMF = m_hMeta End Property
Public Property Get Managed() As Boolean Managed = m_Managed End Property Public Property Let Managed(ByVal inNew As Boolean) m_Managed = inNew End Property Public Function OpenFile(strPath As String) As Boolean Call Destroy
m_hMeta = GetEnhMetaFile(strPath) If (m_hMeta) Then SaveState = SaveDC(m_hMeta) m_Recording = True m_Managed = True Create = True End If End Function Public Function Create(Optional nWidthPix As Long = 0, _ Optional nHeightPix As Long = 0, _ Optional sDescription As String = vbNullString) As Boolean
Dim R As RectAPI Call Destroy
If nWidthPix <> 0 And nHeightPix <> 0 Then R.Right = nWidthPix R.Bottom = nHeightPix m_hMeta = CreateEnhMetaFile(0&, vbNullString, R, sDescription) Else m_hMeta = CreateEnhMetaFile(0&, vbNullString, ByVal 0&, sDescription) End If
If (m_hMeta) Then SaveState = SaveDC(m_hMeta) m_Recording = True m_Managed = True Create = True End If End Function
Public Function StopRecording() As Boolean If (Not m_Recording) Then Exit Function
Call RestoreDC(m_hMeta, SaveState) m_hMeta = CloseEnhMetaFile(m_hMeta) StopRecording = True m_Recording = False End Function
Public Function Destroy() As Boolean If (m_hMeta = 0) Then Exit Function If (m_Recording) Then Call StopRecording If (m_Managed) Then Call DeleteEnhMetaFile(m_hMeta) Destroy = True m_hMeta = 0& End Function
Public Function Draw(ByVal inDC As Long, _ Optional ByVal inX As Long = 0, _ Optional ByVal inY As Long = 0, _ Optional ByVal inWidth As Long = 0, _ Optional ByVal inHeight As Long = 0) As Long Dim MetaHead As EnhMetaHeader Dim DrawArea As RectAPI
If ((m_hMeta <> 0) And (Not m_Recording)) Then If ((inWidth = 0) Or (inHeight = 0)) Then Call GetEnhMetaFileHeader(m_hMeta, Len(MetaHead), MetaHead) DrawArea = MetaHead.rclBounds If (inWidth) Then DrawArea.Right = DrawArea.Left + inWidth If (inHeight) Then DrawArea.Bottom = DrawArea.Top + inHeight Call OffsetRect(DrawArea, inX, inY) Else Call SetRect(DrawArea, inX, inY, inWidth + inX, inHeight + inY) End If
Draw = PlayEnhMetaFile(inDC, m_hMeta, DrawArea) <> 0 End If End Function Public Function SaveEmf(filepath As String) As Boolean If Dir(filepath) <> "" Then Kill filepath
SaveEmf = CopyEnhMetaFile(Me.hEMF, filepath) End Function
Private Sub Class_Terminate() Call Destroy End Sub |
|