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


A metafile is a collection of structures that store a picture in a device-independent format. Device independence is the one feature that sets metafiles apart from bitmaps. Unlike a bitmap, a metafile guarantees device independence. There is a drawback to metafiles however, they are generally drawn more slowly than bitmaps.

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.

A metafile is played when its records are converted to device commands and processed by the appropriate device.

There are two types of metafiles:
  • Windows-format metafiles
  • Enhanced-format metafiles

Windows-format metafiles

Following the standard header in all WMF metafiles is a series of data records. This record is defined by the METARECORD data type. It has the following format:

Click here to copy the following block
Type METARECORD
  nSize As Long
  rdFunction As Integer  '//Low byte FunctionId, Hi byte Para count
  '//rdParm(1) As Integer
  lpParm As Integer  '// Parm[] Pointer to staring address of parameter array
End Type

Size is the total size of the records in 16-bit WORDs, including the Size field itself. The minimum possible size for a record is 3.

Function is the GDI number of the function called to playback this record. The low-byte of this value identifies the specific GDI function to call. The high-byte is the number of WORDs passed to this function, and is also the number of elements in the Parameters array. For example, a value of 0x0213 indicates the LineTo() function (0x13) and that this function is passed two WORD values.

Parameters is an array of the parameters used by the GDI function specified by this record. The parameters are stored in the reverse order in which they are passed to the function. For example, the two parameter values of the LineTo record are passed to the LineTo() function in the order of X and Y, but store in the record as Y and X.

Although each record parameter is stored as a WORD, the exact data type of the parameter is determined by the function it is passed to. Parameter values that change, such as device context handles, are never stored in metafile records. The last record in every metafile always has a function number of 0000h

Enhanced Metafile Records

Enhanced metafiles also have an enhanced metafile record structure. When compared to standard metafile records you will see that all three fields are now 32-bit DWORDs and the positions of the Size and Function fields are exchanged:

Click here to copy the following block
Type ENHMETARECORD
  iType As Long
  nSize As Long
  lpParm As Long  '// Parm[] Pointer to staring address of parameter array
End Type

Function is the GDI number of the function called to playback this record. In the Win32 SDK these values identified by the EMR_*

Step-By-Step Example

- Create a standard exe project
- Add one module to the project
- Add one command button, one picturebox and one listbox control on the form1
- Add the following code in form1

Form1.frm

Click here to copy the following block
Dim picRECT As RECT
Dim hMF As Long, ret As Long, hEMFPal As Long

Public Sub ExtractMetaRecords(EMFHandle As Long, EMFObj As PictureBox)
  If OpenedFileType = EMF Then
    Call EnumEnhMetaFile(ByVal EMFObj.hdc, ByVal EMFHandle, AddressOf EnhMetaFileProc, 0, picRECT)
  Else
    Call EnumMetaFile(ByVal EMFObj.hdc, ByVal EMFHandle, AddressOf WinMetaFileProc, 0)
  End If
End Sub

Public Sub Command1_Click()

  List1.Clear

  '//First try to Open a as EMF file and get a handle
  hMF = GetEnhMetaFile(Text1)
  If hMF = 0 Then
    '//If failed then try as WMF file
    hMF = GetMetaFile(Text1)
    If hMF = 0 Then
      MsgBox "Can not open file, make sure that its a valid format (Only WMF/EMF Supported in this demo)", vbCritical
      Exit Sub
    Else
      OpenedFileType = WMF
    End If
  Else
    OpenedFileType = EMF
  End If

  '//Get Rectangle boundaries of picture box
  ret = GetClientRect(Picture1.hwnd, picRECT)

  If OpenedFileType = EMF Then
    Call EMFDemo
  Else
    Call WMFDemo
  End If
End Sub

Sub EMFDemo()
  '//Display EMF on the picturebox
  'ret = PlayEnhMetaFile(Picture1.hdc, hMF, ByVal 0&) '//Full size
  ret = PlayEnhMetaFile(Picture1.hdc, hMF, picRECT)  '//Fit to pic box

  '//Note EMF may contain palette so check for palette and if
  '//it is there then repaint using new palette

  '//Delete any previous Palette object
  If hEMFPal <> 0 Then DeleteObject hEMFPal

  hEMFPal = GetPalleteFromEMF(hMF)
  If hEMFPal <> 0 Then Call SetPaletteForDC(Picture1.hdc, hEMFPal, Picture1.hwnd)

  MsgBox "This is original file"

  Picture1.Cls
  '//Now lets extract each record from emf
  ExtractMetaRecords hMF, Picture1
End Sub

Sub WMFDemo()
  '//Display WMF on the picturebox
  ret = PlayMetaFile(Picture1.hdc, hMF)  '//Fit to pic box
  MsgBox "This is original file"

  Picture1.Cls
  '//Now lets extract each record from emf
  ExtractMetaRecords hMF, Picture1
End Sub

Function GetPalleteFromEMF(hEMF As Long) As Long
  Dim emh As ENHMETAHEADER, ret As Long, hPal As Long
  Dim LogPal As LOGPALETTE

  ret = GetEnhMetaFileHeader(hMF, Len(emh), emh)
  If emh.nPalEntries = 0 Then
    hPal = 0
  Else
    LogPal.palNumEntries = GetEnhMetaFilePaletteEntries(hMF, 1000, LogPal.palPalEntry(0))
    LogPal.palVersion = &H300
    hPal = CreatePalette(LogPal)
  End If
  GetPalleteFromEMF = hPal
End Function

Function SetPaletteForDC(hDcToSet As Long, hPal As Long, hWndToRepaint) As Boolean
  '// Select it in as a foreground palette
  hOldPal = SelectPalette(hdc, hEnhMetaPal, False)
  '// Realize the palette
  If RealizePalette(hDcToSet) Then
    '//Fource window to repaint coz we have selected a new palette
    Call InvalidateRect(hWndToRepaint, ByVal 0&, True)
  End If

  '//now we have repaint the window, select old palette back to dc
  Call SelectPalette(hDcToSet, hOldPal, True)
  RealizePalette (hDcToSet)
End Function

Public Sub Form_Load()
  Text1.Text = App.Path & "\emf_demo.emf"
  'Text1.Text = App.Path & "\wmf_placeabledemo.wmf" '//This is APM format so wont work with this example
  'Text1.Text = App.Path & "\wmf_regdemo.wmf"

  Command1.Caption = "Demo"

  arrRecords = Split(strRecords, ",")  '//Split GDI Function names seperated by commas
End Sub

Private Sub Form_Unload(Cancel As Integer)
  If OpenedFileType = EMF Then
    ret = DeleteEnhMetaFile(hMF)  '//Release handle of EMF file
  Else
    ret = DeleteMetaFile(hMF)  '//Release handle of WMF file
  End If

  '//Delete palette object
  If hEMFPal <> 0 Then DeleteObject hEMFPal
End Sub

- Add the following code in module1

Module1.bas

Click here to copy the following block
'//Ordered from 1 to 97 so when u use split() function it will be stored in array in order
Global Const strRecords = "M_HEADER, M_POLYBEZIER, M_POLYGON, M_POLYLINE, M_POLYBEZIERTO, M_POLYLINETO, " & _
    "M_POLYPOLYLINE, M_POLYPOLYGON, M_SETWINDOWEXTEX, M_SETWINDOWORGEX, M_SETVIEWPORTEXTEX, " & _
    "M_SETVIEWPORTORGEX, M_SETBRUSHORGEX, M_EOF, M_SETPIXELV, M_SETMAPPERFLAGS, M_SETMAPMODE, " & _
    "M_SETBKMODE, M_SETPOLYFILLMODE, M_SETROP2, M_SETSTRETCHBLTMODE, M_SETTEXTALIGN, " & _
    "M_SETCOLORADJUSTMENT, M_SETTEXTCOLOR, M_SETBKCOLOR, M_OFFSETCLIPRGN, M_MOVETOEX, M_SETMETARGN, " & _
    "M_EXCLUDECLIPRECT, M_INTERSECTCLIPRECT, M_SCALEVIEWPORTEXTEX, M_SCALEWINDOWEXTEX, M_SAVEDC, " & _
    "M_RESTOREDC, M_SETWORLDTRANSFORM, M_MODIFYWORLDTRANSFORM, M_SELECTOBJECT, M_CREATEPEN, " & _
    "M_CREATEBRUSHINDIRECT, M_DELETEOBJECT, M_ANGLEARC, M_ELLIPSE, M_RECTANGLE, M_ROUNDRECT, " & _
    "M_ARC, M_CHORD, M_PIE, M_SELECTPALETTE, M_CREATEPALETTE, M_SETPALETTEENTRIES, M_RESIZEPALETTE, " & _
    "M_REALIZEPALETTE, M_EXTFLOODFILL, M_LINETO, M_ARCTO, M_POLYDRAW, M_SETARCDIRECTION, M_SETMITERLIMIT, " & _
    "M_BEGINPATH, M_ENDPATH, M_CLOSEFIGURE, M_FILLPATH, M_STROKEANDFILLPATH, M_STROKEPATH, M_FLATTENPATH, " & _
    "M_WIDENPATH, M_SELECTCLIPPATH, M_ABORTPATH, EMR_RESERVED69, M_GDICOMMENT, M_FILLRGN, M_FRAMERGN, M_INVERTRGN, " & _
    "M_PAINTRGN, M_EXTSELECTCLIPRGN, M_BITBLT, M_STRETCHBLT, M_MASKBLT, M_PLGBLT, M_SETDIBITSTODEVICE, " & _
    "M_STRETCHDIBITS, M_EXTCREATEFONTINDIRECTW, M_EXTTEXTOUTA, M_EXTTEXTOUTW, M_POLYBEZIER16, M_POLYGON16, " & _
    "M_POLYLINE16, M_POLYBEZIERTO16, M_POLYLINETO16, M_POLYPOLYLINE16, M_POLYPOLYGON16, M_POLYDRAW16, " & _
    "M_CREATEMONOBRUSH, M_CREATEDIBPATTERNBRUSHPT, M_EXTCREATEPEN, M_POLYTEXTOUTA, M_POLYTEXTOUTW"

Enum enumWMFRecord
  WMR_ABORTDOC = &H52
  WMR_ARC = &H817
  WMR_CHORD = &H830
  WMR_CREATEPENINDIRECT = &H2FA
  WMR_CREATEBRUSHINDIRECT = &H2FC
  WMR_DELETEOBJECT = &H1F0
  WMR_ELLIPSE = &H418
  WMR_ENDDOC = &H5E
  WMR_ENDPAGE = &H50
  WMR_EXCLUDECLIPRECT = &H415
  WMR_EXTFLOODFILL = &H548
  WMR_FILLREGION = &H228
  WMR_FLOODFILL = &H419
  WMR_FRAMEREGION = &H429
  WMR_INTERSECTCLIPRECT = &H416
  WMR_INVERTREGION = &H12A
  WMR_LINETO = &H213
  WMR_MOVETO = &H214
  WMR_OFFSETCLIPRGN = &H220
  WMR_OFFSETVIEWPORTORG = &H211
  WMR_OFFSETWINDOWORG = &H20F
  WMR_PAINTREGION = &H12B
  WMR_PATBLT = &H61D
  WMR_PIE = &H81A
  WMR_REALIZEPALETTE = &H35
  WMR_RECTANGLE = &H41B
  WMR_RESETDC = &H14C
  WMR_RESIZEPALETTE = &H139
  WMR_RESTOREDC = &H127
  WMR_ROUNDRECT = &H61C
  WMR_SAVEDC = &H1E
  WMR_SCALEVIEWPORTEXT = &H412
  WMR_SCALEWINDOWEXT = &H410
  WMR_SELECTCLIPREGION = &H12C
  WMR_SELECTOBJECT = &H12D
  WMR_SELECTPALETTE = &H234
  WMR_SETTEXTALIGN = &H12E
  WMR_SETBKCOLOR = &H201
  WMR_SETBKMODE = &H102
  WMR_SETDIBTODEV = &HD33
  WMR_SETMAPMODE = &H103
  WMR_SETMAPPERFLAGS = &H231
  WMR_SETPALENTRIES = &H37
  WMR_SETPIXEL = &H41F
  WMR_SETPOLYFILLMODE = &H106
  WMR_SETRELABS = &H105
  WMR_SETROP2 = &H104
  WMR_SETSTRETCHBLTMODE = &H107
  'WMR_SETTEXTALIGN = &H12E
  WMR_SETTEXTCHAREXTRA = &H108
  WMR_SETTEXTCOLOR = &H209
  WMR_SETTEXTJUSTIFICATION = &H20A
  WMR_SETVIEWPORTEXT = &H20E
  WMR_SETVIEWPORTORG = &H20D
  WMR_SETWINDOWEXT = &H20C
  WMR_SETWINDOWORG = &H20B
  WMR_STARTDOC = &H14D
  WMR_STARTPAGE = &H4F
  WMR_TEXTOUT = &HC
End Enum

Public Type RECT
  Left As Long
  Top As Long
  Right As Long
  Bottom As Long
End Type

Public Type PALETTEENTRY
  peRed As Byte
  peGreen As Byte
  peBlue As Byte
  peFlags As Byte
End Type

Public Type LOGPALETTE
  palVersion As Integer
  palNumEntries As Integer
  palPalEntry(1) As PALETTEENTRY
End Type

Public Type SIZEL
  cx As Long
  cy As Long
End Type

Public 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

Public Type METARECORD
  nSize As Long
  rdFunction As Integer  '//Low byte FunctionId, Hi byte Para count
  '//rdParm(1) As Integer
  lpParm As Integer  '// Parm[] Pointer to staring address of parameter array
End Type

Public Type ENHMETARECORD
  iType As Long
  nSize As Long
  lpParm As Long  '// Parm[] Pointer to staring address of parameter array
End Type

Public Declare Function DeleteMetaFile Lib "gdi32.dll" ( _
    ByVal hMF As Long) As Long

Public Declare Function EnumMetaFile Lib "gdi32.dll" ( _
    ByVal hdc As Long, _
    ByVal hMetafile As Long, _
    ByVal lpMFEnumProc As Long, _
    ByVal lParam As Long) As Long

Public Declare Function GetMetaFile Lib "gdi32.dll" Alias "GetMetaFileA" ( _
    ByVal lpFileName As String) As Long

Public Declare Function PlayMetaFileRecord Lib "gdi32.dll" ( _
    ByVal hdc As Long, _
    ByRef lpHandletable As Long, _
    ByRef lpMetaRecord As METARECORD, _
    ByVal nHandles As Long) As Long

Public Declare Function CreatePen Lib "gdi32.dll" ( _
    ByVal nPenStyle As Long, _
    ByVal nWidth As Long, _
    ByVal crColor As Long) As Long

Public Declare Function DeleteObject Lib "gdi32.dll" ( _
    ByVal hObject As Long) As Long

Public Declare Function EnumEnhMetaFile Lib "gdi32.dll" ( _
    ByVal hdc As Long, _
    ByVal hEMF As Long, _
    ByVal lpEnhMetaFunc As Long, _
    ByRef lpData As Any, _
    ByRef lpRect As RECT) As Long

Public Declare Function GetEnhMetaFileHeader Lib "gdi32" ( _
    ByVal hEMF As Long, _
    ByVal cbBuffer As Long, _
    lpemh As ENHMETAHEADER) As Long

Public Declare Function GetEnhMetaFile Lib "gdi32.dll" Alias "GetEnhMetaFileA" ( _
    ByVal lpszMetaFile As String) As Long

Public Declare Function PlayEnhMetaFileRecord Lib "gdi32.dll" ( _
    ByVal hdc As Long, _
    ByRef lpHandletable As Long, _
    ByRef lpEnhMetaRecord As ENHMETARECORD, _
    ByVal nHandles As Long) As Long

Public Declare Function SelectObject Lib "gdi32.dll" ( _
    ByVal hdc As Long, _
    ByVal hObject As Long) As Long

Public Declare Function GetClientRect Lib "user32.dll" ( _
    ByVal hwnd As Long, _
    ByRef lpRect As RECT) As Long

Public Declare Function PlayEnhMetaFile Lib "gdi32.dll" ( _
    ByVal hdc As Long, _
    ByVal hEMF As Long, _
    ByRef lpRect As Any) As Long

Public Declare Function PlayMetaFile Lib "gdi32.dll" ( _
    ByVal hdc As Long, _
    ByVal hMF As Long) As Long

Public Declare Function GetEnhMetaFilePaletteEntries Lib "gdi32.dll" ( _
    ByVal hEMF As Long, _
    ByVal cEntries As Long, _
    ByRef lppe As PALETTEENTRY) As Long

Public Declare Function DeleteEnhMetaFile Lib "gdi32.dll" ( _
    ByVal hEMF As Long) As Long

Public Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" ( _
    ByRef Destination As Any, _
    ByRef Source As Any, _
    ByVal Length As Long)

'//Palette APIs
Public Declare Function CreatePalette Lib "gdi32.dll" ( _
    ByRef lpLogPalette As LOGPALETTE) As Long

Public Declare Function SelectPalette Lib "gdi32.dll" ( _
    ByVal hdc As Long, _
    ByVal hPalette As Long, _
    ByVal bForceBackground As Long) As Long

Public Declare Function InvalidateRect Lib "user32.dll" ( _
    ByVal hwnd As Long, _
    ByRef lpRect As RECT, _
    ByVal bErase As Long) As Long

Public Declare Function RealizePalette Lib "gdi32.dll" ( _
    ByVal hdc As Long) As Long
'//

Public Enum enumMetaFileType
  EMF = 0
  WMF = 1
End Enum

Public arrRecords() As String
Public OpenedFileType As enumMetaFileType

Public Function EnhMetaFileProc(ByVal ClientHDC As Long, _
  ByRef lpHandTab As Long, _
  ByRef MetaRec As ENHMETARECORD, _
  ByVal nHandles As Long, _
  ByVal OptData As Long) As Integer

  Dim hPen As Long
  Dim x As Long

  x = PlayEnhMetaFileRecord(ClientHDC, lpHandTab, MetaRec, ByVal nHandles)

  Debug.Print EMFRecordDesc(MetaRec)
  'Debug.Print MetaRec.iType

  Delay (0.05)  '//Give paush effect so you can see how actually records stored

  EnhMetaFileProc = 1  '//1=Continue , 0=exit
End Function

Public Function WinMetaFileProc(ByVal ClientHDC As Long, _
  ByRef lpHandTab As Long, _
  ByRef MetaRec As METARECORD, _
  ByVal nHandles As Long, _
  ByVal OptData As Long) As Integer

  Dim x As Long

  x = PlayMetaFileRecord(ClientHDC, lpHandTab, MetaRec, ByVal nHandles)

  Debug.Print WMFRecordDesc(MetaRec)
  'Debug.Print MetaRec.iType

  'Delay (0.05)       '//Give paush effect so you can see how actually records stored

  WinMetaFileProc = 1  '//1=Continue , 0=exit
End Function

Function EMFRecordDesc(MetaRec As ENHMETARECORD)
  Dim ParaBuffer() As Long, i
  Dim strPara As String, strMsg As String, sAPI As String * 15

  If MetaRec.iType <= UBound(arrRecords) + 1 Then
    strMsg = Trim(arrRecords(MetaRec.iType - 1)) & " "
    sAPI = Replace(strMsg, "M_", "")
    strMsg = sAPI
  Else
    strMsg = "UNKNOWN "
  End If

  '//If parameters there then get it
  If MetaRec.nSize > 8 Then  '//Para start after first 8 bytes
    Dim nParaCount As Long
    nParaCount = ((MetaRec.nSize - 8) / 4)

    ReDim ParaBuffer(nParaCount - 1)

    '//Grab parameters from the MetaRec (first 2 words are iType and nSize so dont copy)
    CopyMemory ParaBuffer(0), MetaRec.lpParm, MetaRec.nSize - 8

    For i = 0 To UBound(ParaBuffer)
      strPara = strPara & " " & ParaBuffer(i)
    Next
  End If

  Form1.List1.AddItem strMsg & strPara

  EMFRecordDesc = strMsg & strPara
End Function


Function WMFRecordDesc(MetaRec As METARECORD)
  Dim ParaBuffer() As Integer, i
  Dim strPara As String, strMsg As String

  Select Case MetaRec.rdFunction
    Case WMR_SAVEDC
      strMsg = "SAVEDC "
    Case WMR_SETMAPMODE
      strMsg = "SETMAPMODE "
    Case WMR_SETWINDOWORG
      strMsg = "SETWINDOWORG "
    Case WMR_SAVEDC
      strMsg = "SAVEDC "
    Case WMR_SETVIEWPORTORG
      strMsg = "SETVIEWPORTORG "
    Case WMR_SAVEDC
      strMsg = "SAVEDC "
    Case WMR_SETWINDOWEXT
      strMsg = "SETWINDOWEXT "
    Case WMR_RESTOREDC
      strMsg = "RESTOREDC "
    Case WMR_SELECTOBJECT
      strMsg = "SELECTOBJECT "
    Case WMR_CREATEPENINDIRECT
      strMsg = "CREATEPENINDIRECT "
    Case WMR_DELETEOBJECT
      strMsg = "DELETEOBJECT "
    Case WMR_RECTANGLE
      strMsg = "RECTANGLE "
      '//
      '// More command handling
      '//
    Case Else
      strMsg = "Command-" & LowByte(MetaRec.rdFunction) & " "
  End Select

  '//If parameters there then get it
  If MetaRec.nSize > 3 Then  '//Para start after first 3 words

    Dim nParaCount As Byte
    '//Size is returned in words so be careful
    nParaCount = (MetaRec.nSize - 3)  '//This works perfact
    'nParaCount = HiByte(MetaRec.rdFunction) '//Sometimes this line crash
    ReDim ParaBuffer(nParaCount - 1)

    '//Grab parameters from the MetaRec (first 2 words are iType and nSize so dont copy)
    CopyMemory ParaBuffer(0), MetaRec.lpParm, (MetaRec.nSize - 3) * 2

    '//Parameters stored in reverse order
    For i = UBound(ParaBuffer) To 0 Step -1
      strPara = strPara & " " & ParaBuffer(i)
    Next
  End If

  strMsg = "[&H" & Hex(MetaRec.rdFunction) & "] " & strMsg & strPara
  Form1.List1.AddItem strMsg

  WMFRecordDesc = strMsg
End Function

Public Function LowByte(ByVal w As Integer) As Byte
  LowByte = w And &HFF
End Function

Public Function HiByte(ByVal w As Integer) As Byte
  Dim hi As Integer
  If w And &H8000 Then hi = &H4000

  HiByte = (w And &H7FFE) \ 256
  HiByte = (HiByte Or (hi \ 128))
End Function

Sub Delay(nSec As Single)
  Dim t1 As Currency
  t1 = Timer
  Do While True
    If (Timer - t1) > nSec Then Exit Do
    DoEvents
  Loop
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.