Atlanta Custom Software Development 

 
   Search        Code/Page
 

User Login
Email

Password

 

Forgot the Password?
Services
» Web Development
» Maintenance
» Data Integration/BI
» Information Management
Programming
  Database
Automation
OS/Networking
Graphics
Links
Tools
» Regular Expr Tester
» Free Tools


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:
  1. Enhanced-format metafiles
  2. 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  '//Create 5 memory EMF

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

  '//We must find MM/Pix (Milimeters/Pixel) coz CreateEmfMetafile size must be specified in milimeter
  ScreenWidthMM = GetDeviceCaps(hDC, HORZSIZE)
  ScreenHeightMM = GetDeviceCaps(hDC, VERTSIZE)
  ScreenWidthPels = GetDeviceCaps(hDC, HORZRES)
  ScreenHeightPels = GetDeviceCaps(hDC, VERTRES)
  MMPerPelX = (ScreenWidthMM * 100) / ScreenWidthPels  'xPixel 1/100mm
  MMPerPelY = (ScreenHeightMM * 100) / ScreenHeightPels  'yPixel 1/100mm

  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  ' Create red square

      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))
      '//Draw to EMF DC
      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
  'Me.Line (10, 10)-(109, 109), vbYellow, B
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  ' Clean up
  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
  ' Only valid while recording
  If (m_Recording) Then hDC = m_hMeta
End Property

Public Property Get hEMF() As Long
  ' Only valid while not recording
  If (Not m_Recording) Then hEMF = m_hMeta
End Property

' Defines whether the class should manage its EMF handle
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
    ' Save the current state, start recording and manage by default
    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

  '//This will create a memory meta file coz filename is null
  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
    ' Save the current state, start recording and manage by default
    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

  'Restore original state, close MetaDC and store EMF handle
  Call RestoreDC(m_hMeta, SaveState)
  m_hMeta = CloseEnhMetaFile(m_hMeta)
  StopRecording = True
  m_Recording = False
End Function

Public Function Destroy() As Boolean ' Clean up resources
  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
      ' Get the header for the metafile and offset to the new location
      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         ' Fill the bounds rect from the passed parameters
      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       ' Always clean up on exit
End Sub


Submitted By : Nayan Patel  (Member Since : 5/26/2004 12:23:06 PM)

Job Description : He is the moderator of this site and currently working as an independent consultant. He works with VB.net/ASP.net, SQL Server and other MS technologies. He is MCSD.net, MCDBA and MCSE. In his free time he likes to watch funny movies and doing oil painting.
View all (893) submissions by this author  (Birth Date : 7/14/1981 )


Home   |  Comment   |  Contact Us   |  Privacy Policy   |  Terms & Conditions   |  BlogsZappySys

© 2008 BinaryWorld LLC. All rights reserved.