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 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:
  1. standard meta file
  2. placeable meta file (also known as APM file)
  3. clipboard meta file
  4. 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  '//File type=APM
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

' Win32 implementations of obsolete WMF functions
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

' Win32 Enhanced Metafile functions
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  '//Redraw
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  '//Enhanced Metafile format
    Call EMFDemo(App.Path & "\" & EMF_SAVE_NAME)
  ElseIf Option2.Value = True Then  '//Windows Metafile (old 16 bit format)
    Call WMFDemo(App.Path & "\" & WMF_SAVE_NAME)
  ElseIf Option3.Value = True Then  '//Aldus Placeable Metafile format
    Call APMDemo(App.Path & "\" & APM_SAVE_NAME)
  End If

  MsgBox "File created at " & Text1
End Sub

'//Create, Save and Play EMF file
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

  '//Rect must be specified in milimeters
  R.Right = 200 * MMPerPelX  '//Convert Pixels to Milimeters
  R.Bottom = 200 * MMPerPelY

  hdcMeta = CreateEnhMetaFile(Picture1.hDC, strSavePath, R, "This EMF file generated for APIDemo" & Chr(0))

  If hdcMeta <> 0 Then  ' we've got a metafile running!
    Call DrawSomethingToDC(hdcMeta)

    hMF = CloseEnhMetaFile(hdcMeta)  '//Stop recording and free DC
    Text1.Text = strSavePath
    'temp = PlayEnhMetaFile(Picture1.hdc, hMF, ByVal 0&)
    temp = PaintAnyMetaFile(Picture1.hDC, strSavePath, picRect, CBool(Check1.Value))
    temp = DeleteEnhMetaFile(hMF)  ' doesn't erase the file, just the space in memory
  End If
End Sub

'//Create, Save and Play WMF file
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  ' we've got a metafile running!
    Call DrawSomethingToDC(hdcMeta)

    hMF = CloseMetaFile(hdcMeta)  '//Stop recording and free DC
    Text1.Text = strSavePath
    'temp = PlayMetaFile(Picture1.hDc, hMF)
    temp = PaintAnyMetaFile(Picture1.hDC, strSavePath, picRect, CBool(Check1.Value))
    temp = DeleteMetaFile(hMF)  ' doesn't erase the file, just the space in memory
  End If
End Sub

'//Create, Save and Play APM file
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  '//22 Bytes=APMFILEHEADER len
  If Dir(strSavePath) <> "" Then Kill strSavePath

  '//Rect must be specified in milimeters
  R.Right = 200 * MMPerPelX  '//Convert Pixels to Milimeters
  R.Bottom = 200 * MMPerPelY

  '//We need memory Meta file so we can add extra APM header to the file while saving to disk
  hdcMeta = CreateEnhMetaFile(0&, vbNullString, R, "This EMF file generated for APIDemo" & Chr(0))

  '//APM File format has extra header

  If hdcMeta <> 0 Then  ' we've got a metafile running!
    Call DrawSomethingToDC(hdcMeta)

    hMF = CloseEnhMetaFile(hdcMeta)  '//Stop recording and free DC

    temp = GetEnhMetaFileHeader(hMF, Len(emh), emh)

    '// Fill in the Aldus Placeable Header
    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
    '//Now checksum (XORing with first 10 WORD of header last WORD is checksum itself)
    CopyMemory ChkSumBuffer(0), APMHeader, Len(APMHeader)
    APMHeader.checksum = ChkSumBuffer(0)  '//Start with first byte
    For i = 1 To 9
      APMHeader.checksum = APMHeader.checksum Xor ChkSumBuffer(i)
    Next

    '//The following section will convert EMF to old 16bit windows meta fileformat (WMF)
    '//Calculate size require to store Metafile buffer
    dwSize = GetWinMetaFileBits(hMF, 0, ByVal 0&, MM_ANISOTROPIC, Picture1.hDC)
    ReDim Buffer(dwSize - 1)
    '//Get actual data into buffer
    temp = GetWinMetaFileBits(hMF, dwSize, Buffer(0), MM_ANISOTROPIC, Picture1.hDC)

    '//Write APM Header + WMF Data to file
    Open strSavePath For Binary As #1
    Put #1, , APMHeader
    Put #1, , Buffer
    Close #1

    Text1.Text = strSavePath
    'temp = PlayEnhMetaFile(Picture1.hdc, hMF, ByVal 0&)
    temp = PaintAnyMetaFile(Picture1.hDC, strSavePath, picRect, CBool(Check1.Value))
    temp = DeleteEnhMetaFile(hMF)  ' doesn't erase the file, just the space in memory
  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_regular.wmf"
  'Text1.Text = App.Path & "\print_placeable.wmf"
  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

  '//We must find MM/Pix (Milimeters/Pixel) coz CreateEmfMetafile size must be specified in milimeter
  ScreenWidthMM = GetDeviceCaps(hDC, HORZSIZE)
  ScreenHeightMM = GetDeviceCaps(hDC, VERTSIZE)
  ScreenWidthPix = GetDeviceCaps(hDC, HORZRES)
  ScreenHeightPix = GetDeviceCaps(hDC, VERTRES)
  MMPerPelX = (ScreenWidthMM * 100) / ScreenWidthPix  'xPixel 1/100mm
  MMPerPelY = (ScreenHeightMM * 100) / ScreenHeightPix  'yPixel 1/100mm
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

  ' Try using Enhanced routines first.
  hMF = GetEnhMetaFile(Filename)
  If hMF Then
    If FitToRect = False Then
      '//Get bounding rectangle info from header.
      Call GetEnhMetaFileHeader(hMF, Len(emh), emh)
      '//Play it out, then close handle.
      PaintAnyMetaFile = PlayEnhMetaFile(hDC, hMF, emh.rclBounds)
    Else
      '//Resized to fit in picbox
      PaintAnyMetaFile = PlayEnhMetaFile(hDC, hMF, R)
    End If
    Call DeleteEnhMetaFile(hMF)
  Else  'must be an old-fashioned WMF
    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

  ' First, just try a "simple" GetMetaFile call
  ' GetMetaFile is only for Win16

  ' Note: Many Windows-based applications import or export Windows metafiles
  ' in a format known as the Aldus Placeable Metafile (APM) format.
  ' In this format, these metafiles cannot be used with the Windows metafile
  ' functions such as GetMetaFile(), CopyMetaFile(), PlayMetaFile(), and so on.
  ' To use these metafiles, the APM header must be removed from the metafile and
  ' the remaining metafile bits must be written to a newly created metafile.

  hMF = GetMetaFile(Filename)
  If hMF = 0 Then
    'if not Windows Meta file then check for Aldus Placeable Metafile (APM) format
    hMF = GetPlaceableMetafile(Filename)
    If hMF = 0 Then
      ' No luck go back
      PlayOldMetafile = False
      Exit Function
    End If
  End If
  'if you come here means file is Windows Meta File (WMF)

  ' Get size of metafile, allocate buffer, and grab bytes.
  Bytes = GetMetaFileBitsEx(hMF, Bytes, ByVal 0&)
  If Bytes Then
    ReDim Buffer(1 To Bytes) As Byte
    Call GetMetaFileBitsEx(hMF, Bytes, Buffer(1))
  Else
    ' No luck go back
    PlayOldMetafile = False
    Exit Function
  End If

  ' Convert to enhanced format.
  hemf = SetWinMetaFileBits(Bytes, Buffer(1), hDC, ByVal 0&)

  ' Play metafile into DC.
  If FitToRect = False Then
    'Original Size
    Dim emh As ENHMETAHEADER
    '//Get bounding rectangle info from header.
    Call GetEnhMetaFileHeader(hemf, Len(emh), emh)
    PlayOldMetafile = PlayEnhMetaFile(hDC, hemf, emh.rclBounds)
  Else
    'Fit to Rect
    PlayOldMetafile = PlayEnhMetaFile(hDC, hemf, R)
  End If


  ' Clean up
  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

  ' Best prepare for file access errors.
  On Error GoTo FileErr

  ' Open the file for binary access
  hFile = FreeFile
  Open Filename For Binary Access Read As #hFile
  ' Read APM header first
  Get #hFile, , Apm

  If Apm.key <> FILETYPE_APM Then
    '//File is not APM so exit
    GetPlaceableMetafile = 0
    Exit Function
  End If

  ' Scan past APMFILEHEADER (22 bytes), and
  ' grab METAHEADER.
  Get #hFile, 23, Hdr

  ' The size field contains number of WORDs in metafile,
  ' we need to double for number of bytes.
  Hdr.mtSize = Hdr.mtSize * 2

  ' Grab actual metafile data. Need to back up to
  ' beginning of header.
  ReDim Buffer(1 To Hdr.mtSize) As Byte
  Get #hFile, 23, Buffer

  ' Done with file now.
  Close hFile

  ' Create new memory-based metafile and return the handle.
  GetPlaceableMetafile = SetMetaFileBitsEx(UBound(Buffer), Buffer(1))
  Exit Function
FileErr:
  GetPlaceableMetafile = 0
End Function


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.