Skip to content

Latest commit

 

History

History
469 lines (371 loc) · 16.3 KB

File metadata and controls

469 lines (371 loc) · 16.3 KB

Windows Metafile (WMF) File Format

Overview

WMF (Windows Metafile Format) is a 16-bit graphics format created by Microsoft for storing vector and raster graphics. It was the primary metafile format for Windows prior to Windows 2000, when it was superseded by EMF (Enhanced Metafile Format). WMF files contain a sequence of GDI (Graphics Device Interface) function calls.

Key Features

  • Standard: Microsoft proprietary format (published specification)
  • Purpose: Vector graphics metafile for Windows applications
  • Encoding: Binary format with little-endian byte order
  • Era: Designed for 16-bit Windows systems (Windows 3.x era)

File Structure

A WMF file consists of:

[Optional Placeable Header (22 bytes)]
[Standard Header (18 bytes)]
[Record 1]
[Record 2]
...
[Record N - META_EOF]

Each Record represents a GDI function call or state change.


File Headers

Placeable Metafile Header (22 bytes, Optional)

Also called the "Aldus Placeable Metafile (APM) Header", this was added by Aldus Corporation for better device independence.

Offset Size Field Description
0 4 Key Magic number: 0x9AC6CDD7 (little-endian)
4 2 Handle Metafile handle (usually 0)
6 2 Left Left coordinate of bounding box
8 2 Top Top coordinate of bounding box
10 2 Right Right coordinate of bounding box
12 2 Bottom Bottom coordinate of bounding box
14 2 Inch Units per inch (e.g., 1440 for twips)
16 4 Reserved Reserved (usually 0)
20 2 Checksum XOR checksum of first 10 words

Detection: Check if first 4 bytes equal 0x9AC6CDD7 (or 0xD7CDC69A in big-endian representation).

Standard WMF Header (18 bytes)

Offset Size Field Description
0 2 FileType Type of metafile: 1=memory, 2=disk
2 2 HeaderSize Size of header in WORDs (always 9)
4 2 Version Windows version (0x0300 for Windows 3.x)
6 4 FileSize Size of entire file in WORDs
10 2 NumOfObjects Number of GDI objects
12 4 MaxRecordSize Size of largest record in WORDs
16 2 NumOfParams Not used (0)

Record Structure

Each WMF record has the following structure:

Offset Size Field Description
0 4 RecordSize Size of record in WORDs (including this)
4 2 RecordFunction GDI function identifier
6 Variable Parameters Function-specific parameters

Important:

  • All sizes are in WORDs (16-bit words, 2 bytes each)
  • Byte order is little-endian (opposite of CGM)
  • RecordSize includes the 6-byte header (3 WORDs)

Common Record Types

Drawing Records (Graphical Output)

Function Code Name Description
0x0000 META_EOF End of metafile
0x0213 META_LINETO Draw line to (x, y)
0x0214 META_MOVETO Move current position to (x, y)
0x0324 META_POLYGON Draw filled polygon
0x0325 META_POLYLINE Draw series of connected lines
0x041B META_RECTANGLE Draw filled rectangle
0x0418 META_ELLIPSE Draw filled ellipse
0x0538 META_POLYPOLYGON Draw multiple polygons
0x061C META_ROUNDRECT Draw rectangle with rounded corners
0x0817 META_ARC Draw elliptical arc
0x0830 META_CHORD Draw chord (closed arc)
0x0A32 META_PIE Draw pie slice
0x0521 META_TEXTOUT Output text string
0x0B41 META_EXTTEXTOUT Output text with options

Attribute Records (State Changes)

Function Code Name Description
0x0102 META_SETBKMODE Set background mode
0x0103 META_SETMAPMODE Set coordinate mapping mode
0x0104 META_SETROP2 Set binary raster operation
0x0106 META_SETPOLYFILLMODE Set polygon fill mode
0x0209 META_SETWINDOWORG Set window origin
0x020B META_SETWINDOWEXT Set window extent
0x020C META_SETVIEWPORTORG Set viewport origin
0x020E META_SETVIEWPORTEXT Set viewport extent

Object Management Records

Function Code Name Description
0x01F0 META_DELETEOBJECT Delete GDI object
0x012D META_SELECTOBJECT Select object into DC
0x02FA META_CREATEPENINDIRECT Create pen object
0x02FB META_CREATEBRUSHINDIRECT Create brush object
0x02FC META_CREATEFONTINDIRECT Create font object
0x00F8 META_CREATEPALETTE Create palette object
0x0142 META_DIBCREATEPATTERNBRUSH Create pattern brush from DIB

Color Records

Function Code Name Description
0x0201 META_SETBKCOLOR Set background color
0x0209 META_SETTEXTCOLOR Set text color

Record Parameter Formats

META_MOVETO (0x0214)

RecordSize: 5 WORDs
RecordFunction: 0x0214
Parameters:
  Y: 16-bit signed integer
  X: 16-bit signed integer

META_LINETO (0x0213)

RecordSize: 5 WORDs
RecordFunction: 0x0213
Parameters:
  Y: 16-bit signed integer
  X: 16-bit signed integer

META_POLYGON (0x0324)

RecordSize: 4 + NumberOfPoints WORDs
RecordFunction: 0x0324
Parameters:
  NumberOfPoints: 16-bit integer
  Points: Array of (X, Y) coordinate pairs
    X1: 16-bit signed integer
    Y1: 16-bit signed integer
    ...

META_POLYLINE (0x0325)

RecordSize: 4 + NumberOfPoints WORDs
RecordFunction: 0x0325
Parameters:
  NumberOfPoints: 16-bit integer
  Points: Array of (X, Y) coordinate pairs

META_RECTANGLE (0x041B)

RecordSize: 7 WORDs
RecordFunction: 0x041B
Parameters:
  BottomRect: 16-bit signed integer
  RightRect: 16-bit signed integer
  TopRect: 16-bit signed integer
  LeftRect: 16-bit signed integer

META_ELLIPSE (0x0418)

RecordSize: 7 WORDs
RecordFunction: 0x0418
Parameters:
  BottomRect: 16-bit signed integer (bounding box)
  RightRect: 16-bit signed integer
  TopRect: 16-bit signed integer
  LeftRect: 16-bit signed integer

META_CREATEPENINDIRECT (0x02FA)

RecordSize: 8 WORDs
RecordFunction: 0x02FA
Parameters:
  PenStyle: 16-bit integer (0=solid, 1=dash, 2=dot, etc.)
  WidthX: 16-bit integer (pen width X)
  WidthY: 16-bit integer (pen width Y, usually 0)
  ColorRef: 32-bit color (0x00BBGGRR format)

META_CREATEBRUSHINDIRECT (0x02FB)

RecordSize: 7 WORDs
RecordFunction: 0x02FB
Parameters:
  BrushStyle: 16-bit integer (0=solid, 1=hollow, 2=hatched, etc.)
  ColorRef: 32-bit color (0x00BBGGRR format)
  BrushHatch: 16-bit integer (hatch pattern if hatched)

META_SETBKCOLOR (0x0201)

RecordSize: 5 WORDs
RecordFunction: 0x0201
Parameters:
  ColorRef: 32-bit color (0x00BBGGRR format)

Color Format (COLORREF)

WMF uses 32-bit COLORREF values with the format:

Bytes: [BB GG RR 00]
  0x00BBGGRR (little-endian)

Bits 0-7:   Red component (0-255)
Bits 8-15:  Green component (0-255)
Bits 16-23: Blue component (0-255)
Bits 24-31: Reserved (0)

Parsing Example:

color_ref = struct.unpack('<I', data[0:4])[0]  # Little-endian 32-bit
r = color_ref & 0xFF
g = (color_ref >> 8) & 0xFF
b = (color_ref >> 16) & 0xFF
# Result: RGB(r, g, b)

Coordinate System

WMF uses logical coordinates that must be mapped to device coordinates using the current mapping mode.

Mapping Modes

  • MM_TEXT (1): Each logical unit = 1 device pixel, +X right, +Y down
  • MM_LOMETRIC (2): Each unit = 0.1mm, +X right, +Y up
  • MM_HIMETRIC (3): Each unit = 0.01mm, +X right, +Y up
  • MM_LOENGLISH (4): Each unit = 0.01 inch, +X right, +Y up
  • MM_HIENGLISH (5): Each unit = 0.001 inch, +X right, +Y up
  • MM_TWIPS (6): Each unit = 1/1440 inch, +X right, +Y up
  • MM_ISOTROPIC (7): User-defined, equally scaled axes
  • MM_ANISOTROPIC (8): User-defined, arbitrary axis scales

Coordinate Transformation

# Window extent defines logical coordinate space
# Viewport extent defines device coordinate space

# Transform logical to device coordinates:
device_x = ((logical_x - window_org_x) * viewport_ext_x) / window_ext_x + viewport_org_x
device_y = ((logical_y - window_org_y) * viewport_ext_y) / window_ext_y + viewport_org_y

For placeable metafiles, use the bounding box and units per inch to calculate scaling.


Object Management

WMF maintains a table of GDI objects (pens, brushes, fonts, etc.):

  1. Create: Records like META_CREATEPENINDIRECT add objects to the table
  2. Select: META_SELECTOBJECT activates an object for drawing
  3. Delete: META_DELETEOBJECT removes an object from the table

Important: Objects are referenced by their index in the object table (starting from 0).


Minimal Binary Parser (Python Example)

import struct

def parse_wmf(path):
    with open(path, 'rb') as f:
        # Check for placeable header
        magic = struct.unpack('<I', f.read(4))[0]
        if magic == 0x9AC6CDD7:
            # Read placeable header (22 bytes total)
            f.seek(0)
            placeable = f.read(22)
            # Parse placeable fields...
            left, top, right, bottom = struct.unpack('<hhhh', placeable[6:14])
            inch = struct.unpack('<H', placeable[14:16])[0]
            print(f"Placeable WMF: bbox=({left},{top},{right},{bottom}), inch={inch}")
        else:
            f.seek(0)

        # Read standard header (18 bytes)
        header = f.read(18)
        file_type, header_size, version, file_size, num_objects, max_record = \
            struct.unpack('<HHIHIH', header)

        print(f"WMF Version: 0x{version:04X}, Objects: {num_objects}")

        # Read records
        while True:
            record_header = f.read(6)
            if len(record_header) < 6:
                break

            record_size, record_func = struct.unpack('<IH', record_header)

            # record_size is in WORDs, includes header (3 WORDs = 6 bytes)
            param_bytes = (record_size - 3) * 2
            params = f.read(param_bytes)

            print(f"Record: 0x{record_func:04X}, Size: {record_size} WORDs")

            if record_func == 0x0000:  # META_EOF
                break

Practical Parsing Examples

Example 1: Placeable Header Detection

Hex: D7 CD C6 9A 00 00 00 00 00 00 40 01 58 02 A0 05 00 00 00 00 4F 4D
  • Magic: 0x9AC6CDD7 ✓ (placeable)
  • Bounding box: (0, 0, 320, 600)
  • Units: 1440 (twips)
  • Checksum: 0x4D4F

Example 2: META_RECTANGLE

Hex: 07 00 00 00 1B 04 64 00 C8 00 00 00 00 00
  • RecordSize: 0x00000007 = 7 WORDs (14 bytes)
  • RecordFunction: 0x041B = META_RECTANGLE
  • Parameters:
    • Bottom: 0x0064 = 100
    • Right: 0x00C8 = 200
    • Top: 0x0000 = 0
    • Left: 0x0000 = 0
    • Rectangle from (0,0) to (200,100)

Example 3: META_CREATEPENINDIRECT

Hex: 08 00 00 00 FA 02 00 00 02 00 00 00 00 00 FF 00
  • RecordSize: 0x00000008 = 8 WORDs
  • RecordFunction: 0x02FA = META_CREATEPENINDIRECT
  • Parameters:
    • PenStyle: 0x0000 = PS_SOLID
    • Width: 0x0002 = 2
    • Color: 0x0000FF00 = Green (0x00GGRRBB → RGB(0, 255, 0))

Example 4: META_POLYGON

Hex: 09 00 00 00 24 03 04 00 00 00 00 00 64 00 00 00 64 00 64 00 00 00 64 00
  • RecordSize: 0x00000009 = 9 WORDs
  • RecordFunction: 0x0324 = META_POLYGON
  • Parameters:
    • NumberOfPoints: 0x0004 = 4
    • Points: (0,0), (100,0), (100,100), (0,100)
    • Draws a square

Key Differences from CGM

Aspect WMF CGM
Byte Order Little-endian Big-endian
Origin Top-left Bottom-left
Size Units WORDs (2 bytes) Bytes
Color Format COLORREF (0x00BBGGRR) RGB triplets or indexed
Era Windows 3.x (1990s) ISO standard (1980s-present)
Design GDI function playback Device-independent graphics

Common Parsing Pitfalls

1. Byte Order (Endianness)

  • WMF uses little-endian (Intel byte order)
  • Always use struct.unpack('<H', ...) not struct.unpack('>H', ...)
  • The < prefix is critical (opposite of CGM's >)

2. Record Size Units

  • RecordSize is in WORDs (16-bit words), not bytes
  • To get byte size: byte_size = record_size * 2
  • RecordSize includes the 6-byte header (3 WORDs)
  • Parameter bytes = (record_size - 3) * 2

3. Color Format

  • COLORREF format is 0x00BBGGRR (reverse of RGB!)
  • Extract: R=bits 0-7, G=bits 8-15, B=bits 16-23
  • NOT the same as RGB byte order

4. Y-Axis Orientation

  • WMF default: origin at top-left, +Y down (screen coordinates)
  • Some mapping modes: origin at bottom-left, +Y up
  • Check mapping mode before rendering

5. Placeable Header

  • Not all WMF files have placeable headers
  • Always check magic number first
  • If no placeable header, start directly with standard header

6. Object Table Management

  • Objects are created and destroyed dynamically
  • Track object indices carefully (0-based)
  • Selecting invalid object index causes errors
  • Objects persist until explicitly deleted or EOF

7. State Management

  • GDI state (pen, brush, colors) persists across records
  • Must maintain current position for LINETO operations
  • Background mode affects text rendering
  • ROP2 mode affects drawing operations

Implementation Notes

  1. Check for placeable header first – detect magic 0x9AC6CDD7
  2. Handle little-endian byte order – critical for correct parsing
  3. Maintain object table – track created/selected/deleted objects
  4. Track graphics state – current position, selected pen/brush, colors
  5. Transform coordinates – account for mapping mode and extents
  6. Handle text rendering – requires font object management

References

  • [MS-WMF]: Windows Metafile Format - Microsoft Open Specifications
  • Windows Metafile Format (wmf) Specification - Microsoft Download Center
  • Microsoft Windows Metafile File Format Summary - Encyclopedia of Graphics File Formats
  • Kaitai Struct WMF specification - formats.kaitai.io/wmf

Document History

Author: Technical summary compiled from Microsoft WMF specification, file format documentation, and Windows GDI references.

Purpose: Companion documentation to CGM format guide for vintage vector image viewer project.